文章出處 @ http://blog.csdn.net/gatieme
TCP編程流程說明
(1)SERVER 服務器端編程流程
TCP服務器端編程流程如下:
① 創建套接字socket;
② 綁定套接字bind;
③ 設置套接字爲監聽模式,進入被動接受連接狀態listen;
④ 接受請求,建立連接accpet;
⑤ 讀寫數據read/write;
⑥ 終止連接close。
(2) CLIENT客戶端編程流程
TCP客戶端編程流程如下:
① 創建套接字socket;
② 與遠程服務器建立連接connect;
③ 讀寫數據read/write;
④ 終止連接close。
異常機制
3) TCP服務器三種異常情況
TCP服務器有三種異常情況,分別爲服務器主機崩潰、服務器主機崩潰後重啓、服務器主機關機。
服務器主機崩潰
在服務器主機崩潰的情況下,已有的TCP網絡連接上發不出任何東西。
此時客戶端發出數據後,會一直阻塞在套接字的讀取響應。但是由於服務器主機已崩潰,TCP客戶端會持續重傳數據分節,試圖從服務器接收一個ACK[一般重傳12次(源自Berkeley的實現)]後,客戶TCP最終選擇放棄,返回給應用經常一個ETIMEDOUT錯誤;或者是因爲中間路由器判定服務器主機不可達,則返回一個目的地不可達的ICMP消息響應,其錯誤代碼爲EHOSTUNREACH或ENETUNREACH。
簡單來說,客戶端會一直阻塞與read調用,然後一直重傳,直到最後超時,客戶最終會發現服務器主機已崩潰或主機不可達,然後返回一個狀態碼,但是這個過程可能是很長就的,解決的方法就是自己寫一個read函數然後設置超時,或者加一個SO_KEEPALIVE選項,也可以通過設置套接字選項可以更改TCP持續重傳等待的超時時間。
服務器主機崩潰後重啓
在服務器主機崩潰後重啓的情況下,如果客戶在主機崩潰重啓前不主動發送數據,那麼客戶是不會知道服務器已崩潰。
在服務器重啓後,客戶向服務器發送一個數據分節;由於服務器重啓後丟失了以前的連接信息(儘管在服務端口上有進程監聽,但連接套接字所在的端口無進程等待),因此導致服務器主機的TCP響應RST;而客戶由於之前未收到服務器的響應數據,頁阻塞於read調用,當客戶端收到這個RST之後便返回錯誤ECONNRESET。
如果客戶對服務器的崩潰情況很關心,即使客戶不主動發送數據也這樣,這需要進行相關設置(如設置套接口選項SO_KEEPALIVE或某些客戶/服務器心跳函數)。
服務器主機關機
這裏一般指的是正常關機,因爲Unix系統關機時,會發送SIGTERM和SIGKILL信號,SIGTREM可能忽略,但是SIGKILL信號不能忽略,於是服務器將由SIGKILL終止,
當服務器主機關機的情況下,由於init進程給所有運行的進程發信號SIGTERM,這時服務器程序可以捕獲該信號,並在信號處理程序中正常關閉網絡連接。如果服務器程序忽略了SIGTERM信號,則init進程會等待一段固定的時間(通常是5s~20s),然後給所有還在運行的程序發信號SIGKILL。服務器將由信號SIGKILL終止,其終止時,所有打開的描述字被關閉,這導致向客戶發送FIN分節,客戶收到FIN分節後,能推斷出服務器將終止服務。
簡單的TCP套接字應用程序
我們下面將實現一個迭代類型的簡單TCP服務器與客戶端,實現客戶端從服務器上傳/下載文件(我們的程序中主要是上傳)。
TCP服務器
/**********************************************************
> File Name: server.c
> Author: GatieMe
> Mail: [email protected]
> Created Time: 2015年04月11日 星期六 16時22分10秒
*********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#define TCP_SERVER_PORT 6666 /* 服務器的端口 */
#define BUFFER_SIZE 4096
#define IP_SIZE 20
#define MAX_FILENAME_SIZE 256
#define LISTEN_QUEUE 20
/* 服務器接收從客戶端傳送來的文件 */
void
TcpServerPullFile(
int connfd, /* 服務器與客戶端通訊的套接字文件 */
struct sockaddr_in clientAddr, /* 與之通信的客戶端的信息 */
char *fileServerRoot) /* 上傳文件的存儲路徑 */
{
char buffer[BUFFER_SIZE];
char filename[MAX_FILENAME_SIZE];
char fileServerPath[MAX_FILENAME_SIZE]/* = fileServerRoot*/;
// 定義文件流
FILE *stream;
int count; /* 發送文件名的字節數目 */
int dataLength; /* 接收到的數據大小 */
int writeLength; /* 實際寫入的數據大小 */
int flag = 0;
bzero(buffer, BUFFER_SIZE);
/*
* 向客戶端提示輸入文件路徑提示...
*
* strcpy(buffer, "請輸入要傳輸的文件的完整路徑:");
strcat(buffer, "\n");
send(new_server_socket, buffer, BUFFER_SIZE, 0);
bzero(buffer, BUFFER_SIZE);
*/
/* 首先獲取客戶端發送過來的文件名 */
count = recv(connfd, buffer, BUFFER_SIZE, 0);
if(count < 0)
{
perror("獲取文件名失敗...\n");
exit(1);
}
else
{
strncpy(filename, buffer, strlen(buffer) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(buffer));
strcpy(fileServerPath, fileServerRoot);
strcat(fileServerPath, filename);
printf("\n獲取客戶端發送過來的文件名成功...\n");
printf("文件名[%s]\n", filename);
printf("文件存儲路徑[%s]\n\n", fileServerPath);
}
// 服務器接受數據, 首先打開一個文件流
if((stream = fopen(fileServerPath, "w")) == NULL)
{
perror("file open error...\n");
exit(1);
}
else
{
bzero(buffer,BUFFER_SIZE);
}
printf("正在接收來自%s的文件....\n",inet_ntoa(clientAddr.sin_addr));
dataLength = 0;
/* 先將數據接受到緩衝區buffer中,再寫入到新建的文件中 */
while((dataLength = recv(connfd, buffer, BUFFER_SIZE, 0)) > 0)
{
flag++;
if(flag == 1)
{
printf("正在接收來自%s的文件....\n", inet_ntoa(clientAddr.sin_addr));
}
if(dataLength < 0)
{
printf("接收錯誤i\n");
exit(1);
}
/* 向文件中寫入數據 */
writeLength = fwrite(buffer, sizeof(char), dataLength, stream);
if(writeLength != dataLength)
{
printf("file write failed\n");
exit(1);
}
bzero(buffer,BUFFER_SIZE);
}
if(flag > 0)
{
printf("%s的文件傳送完畢\n", inet_ntoa(clientAddr.sin_addr));
}
if(flag==0)
{
printf("%s的文件傳輸失敗\n", inet_ntoa(clientAddr.sin_addr));
}
fclose(stream);
//rename("data",inet_ntoa(clientAddr.sin_addr));
}
/* 服務器將文件發送到客戶端
*
* 當用戶選擇了下載文件後,服務器將執行此操作
*
* */
void TcpServerPushFile(
int connfd, /* 服務器與客戶端通訊的套接字文件 */
struct sockaddr_in clientAddr, /* 與之通信的客戶端的信息 */
char *filePath) /* 帶發送至客戶端的文件路徑 */
{
//send file imformation
char buff[BUFFER_SIZE];
char filename[MAX_FILENAME_SIZE];
int count;
FILE *stream;
/* 先將文件名發送給客戶端
* 2015-4-13 21:38 Modify
* 發送文件名時只需要發送filePath最後的文件名filename就可以了
* */
bzero(buff, BUFFER_SIZE);
strcpy(filename, strrchr(filePath, '/') + 1);
strncpy(buff, filename, strlen(filename) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(filename));
count = send(connfd, buff, BUFFER_SIZE, 0);
printf("服務器待發送的文件名[%s]..\n", filename);
if(count < 0)
{
perror("Send file information");
exit(1);
}
/* 服務器開始讀取並且發送文件 : */
if((stream = fopen(filePath, "rb")) == NULL)
{
printf("File :%s not found!\n",filePath);
}
printf("服務器打開文件成功...\n");
printf("正在向客戶端發送文件...\n");
bzero(buff, BUFFER_SIZE);
int fileBlockLength = 0;
while((fileBlockLength = fread(buff, sizeof(char), BUFFER_SIZE, stream)) > 0)
{
printf("讀取了:%d個數據...\n",fileBlockLength);
if(send(connfd, buff, fileBlockLength, 0) < 0)
{
perror("Send file error...\n");
perror("向客戶端發送文件失敗...\n");
exit(1);
}
bzero(buff,BUFFER_SIZE);
}
fclose(stream);
printf("服務器發送文件成功\n");
}
extern int errno;
int main(int argc, char *argv[])
{
/**********************************************************
*
* 創建並初始化服務器套接字
*
**********************************************************/
struct sockaddr_in serverAddr;
int socketFd;
bzero(&serverAddr, sizeof(serverAddr)); /* 全部置零 */
/* 設置地址相關的屬性 */
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htons(INADDR_ANY);
serverAddr.sin_port = htons(TCP_SERVER_PORT);
/* 創建套接字 */
socketFd = socket(AF_INET, SOCK_STREAM, 0);
if(socketFd < 0)
{
perror("socket create error\n");
exit(-1);
}
else
{
printf("socket create success...\n");
printf("創建套接字成功[errno = %d]...\n", errno);
}
/* 綁定端口 */
if(bind(socketFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) > 0)
{
perror("bind error\n");
exit(1);
}
else
{
printf("server bind port %d success...\n", TCP_SERVER_PORT);
printf("服務器綁定端口%d成功...\n", TCP_SERVER_PORT);
}
/* 開始監聽綁定的端口 */
if(listen(socketFd, LISTEN_QUEUE))
{
printf("Server listen error[errno = %d]...\n", errno);
exit(-1);
}
else
{
printf("Server listen success...\n");
printf("服務器開始監聽...\n");
}
struct sockaddr_in clientAddr;
socklen_t length = sizeof(clientAddr);
int connFd;
while( 1 )
{
/* accept返回一個新的套接字與客戶端進行通信 */
connFd = accept(socketFd, (struct sockaddr*)&clientAddr, &length);
if(connFd == -1)
{
printf("accept error[%d]...\n", errno);
continue;
}
else
{
printf("獲取到從客戶端%s的連接...\n", inet_ntoa(clientAddr.sin_addr));
////////////////////////////////////////////////////////////////////////
//
// 這裏填寫服務器的處理代碼
//
////////////////////////////////////////////////////////////////////////
TcpServerPullFile(connFd, clientAddr, "./sdata/"); /* 將客戶端發送來的文件存儲在./sdata目錄下 */
close(connFd);
}
}
// sleep(10);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
TCP客戶端
/**********************************************************
> File Name: client.c
> Author: GatieMe
> Mail: [email protected]
> Created Time: 2015年04月11日 星期六 12時05分02秒
*********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#define TCP_SERVER_PORT 6666
#define MAX_FILENAME_SIZE 256
#define IP_SIZE 20
#define BUFFER_SIZE 4096
/* 客戶端將文件上傳到服務器上 */
void TcpClientPushFile(int socketFd, char *filePath)
{
FILE *stream;
char buffer[BUFFER_SIZE];
char filename[MAX_FILENAME_SIZE];
int count = 0;
bzero(buffer, BUFFER_SIZE);
strcpy(filename, strrchr(filePath, '/') + 1);
strncpy(buffer, filename, strlen(filename) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(filename));
count = send(socketFd, buffer, BUFFER_SIZE, 0);
printf("客戶端待上傳待文件名[%s]..\n", filename);
if(count < 0)
{
perror("Send file information");
exit(1);
}
/* 打開文件流 */
if((stream = fopen(filePath, "r")) == NULL)
{
printf("Can't open the file [%s]\n", filePath);
exit(1);
}
else
{
printf("客戶端打開文件成功\n");
}
printf("正在向服務器傳上傳文件...\n");
count = 0;
/* 清空緩衝區 */
bzero(buffer, BUFFER_SIZE);
/* 不斷讀取併發送數據 */
while((count = fread(buffer, 1, BUFFER_SIZE, stream)) > 0)
{
// printf("count =%d\n", count);
if(send(socketFd, buffer, count, 0) < 0)
{
printf("send file error...\n");
break;
}
bzero(buffer, BUFFER_SIZE); /* 再次將緩衝區清空 */
}
printf("向服務器發送文件成功...\n");
/* 傳送完畢後, 關閉文件流 */
if(fclose(stream))
{
printf("file close error\n");
exit(1);
}
else
{
printf("關閉文件流成功...\n");
}
/* 關閉與服務器通訊的套接字 */
close(socketFd);
}
/* 從服務器上下載文件 */
void TcpClientPullFile(int socketFd, char *filePath)
{
char buff[BUFFER_SIZE];
char filename[MAX_FILENAME_SIZE];
int count, writeLength, dataLength;
FILE *stream;
bzero(buff,BUFFER_SIZE);
/* 首先獲取服務器發送過來的文件名 */
count = recv(socketFd, buff, BUFFER_SIZE, 0);
if(count < 0)
{
perror("獲取文件名失敗...\n");
exit(1);
}
strncpy(filename, buff, strlen(buff) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(buff));
/* 開始接收文件 */
printf("Preparing download file : %s", filename);
/* 打開文件流 */
if((stream = fopen(filename, "wb+")) == NULL)
{
perror("create file %s error...\n");
perror("創建文件失敗...\n");
exit(1);
}
bzero(buff, BUFFER_SIZE); /* 清空緩衝區 */
dataLength = 0;
while((dataLength = recv(socketFd, buff, BUFFER_SIZE, 0)) != 0)
{
if(dataLength < 0) /* 如果接收文件失敗 */
{
perror("download error...\n");
perror("下載文件失敗...\n");
exit(1);
}
/* 將接收到的文件數據寫入文件中 */
writeLength = fwrite(buff, sizeof(char), dataLength, stream);
if(writeLength < dataLength) /* 如果寫入的數據比實際接收到的數據少 */
{
perror("file write error...\n");
perror("寫入文件失敗...\n");
exit(1);
}
bzero(buff, BUFFER_SIZE); /* 清空緩衝區 */
}
printf("下載來自服務器%s的文件成功\n", filename);
printf("Receieved file:%s finished!\n", filename);
fclose(stream); /* 關閉文件流 */
}
int main(int argc, char *argv[])
{
char serverIp[IP_SIZE]; /* 服務器的IP地址 */
if(argc >= 2) /* 參數過多的時候,提示用戶 */
{
printf("You have given to much parameters...\n");
printf("Yous should give the IP address after %s\n without any other parametes...\n", (char *)argv[0]);
}
else if(argc == 1) /* 只有一個參數,則默認使用localhost(127.0.0.1) */
{
strcpy(serverIp, "127.0.0.1");
}
else
{
strcpy(serverIp, argv[1]);
}
/**********************************************************
*
* 創建並初始化套接字
*
**********************************************************/
struct sockaddr_in serverAddr; /* 服務器的套接字信息 */
int socketFd; /* 客戶端的套接字信息 */
bzero(&serverAddr, sizeof(serverAddr)); /* 全部置零 */
serverAddr.sin_family = AF_INET; /* internet協議族 */
serverAddr.sin_addr.s_addr = inet_addr(serverIp); /* 設置所連接服務器的IP */
serverAddr.sin_port = htons(TCP_SERVER_PORT); /* 設置連接的服務器端口 */
/* 開始創建套接字 */
/* SOCK_STREAM 面向連接的套接字,即TCP */
socketFd = socket(AF_INET, SOCK_STREAM, 0);
if(socketFd < 0)
{
printf("socket error\n");
exit(-1);
}
/* 嘗試連接服務器 */
if(connect(socketFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0)
{
printf("Can Not Connect To %s\n", serverIp);
exit(1);
}
else
{
printf("connect to the server %s SUCCESS...\n", serverIp);
printf("連接服務器成功...\n");
}
/**********************************************************
*
* 下面進行正常的套接字通信
*
**********************************************************/
// 上傳文件到服務器
TcpClientPushFile(socketFd, "./cdata/push");
close(socketFd);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
由於TCP和UDP在實現流程上有很多異曲同工之處,因此我們後面的拓展優化將主要針對TCP套接字程序,我們在下一篇開始UDP的套接字編程詳解,理解了TCP不同的實現思想之後稍加修改即可。。。。
運行情況