博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux下套接字详解(五)----基于fork多进程的TCP套接字(阻塞/同步/并发)
阅读量:6639 次
发布时间:2019-06-25

本文共 19097 字,大约阅读时间需要 63 分钟。

简介


一个简单的改进方案是在服务器端使用多线程(或多进程)。多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他的连接。具体使用多进程还是多线程,并没有一个特定的模式。传统意义上,进程的开销要远远大于线程,所以如果需要同时为较多的客户机提供服务,则不推荐使用多进程;如果单个服务执行体需要消耗较多的CPU资源,譬如需要进行大规模或长时间的数据运算或文件访问,则进程较为安全。通常,使用pthread_create ()创建新线程,fork()创建新进程。

那么今天我们就使用fork来实现 一个简单的并发服务器

示例


客户端TcpClient


/*************************************************************************    > File Name: TcpClient.c    > Author: GatieMe    > Mail: gatieme@163.com    > Created Time: 2015年12月09日 星期三 16时28分24秒 ************************************************************************//**********************************************************    > File Name: client.c    > Author: GatieMe    > Mail: gatieme@163.com    > Created Time: 2015年04月11日 星期六 12时05分02秒 *********************************************************/#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TCP_SERVER_PORT 6666#define MAX_FILENAME_SIZE 256#define IP_SIZE 20#define BUFFER_SIZE 4096extern int errno;// 客户端进行的处理逻辑void RaiseServerResponse(int socketFd);/* 从服务器上下载文件 */void TcpClientPullFile(int socketFd, char *filePath);/* 客户端将文件上传到服务器上 */void TcpClientPushFile(int socketFd, char *filePath);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"); } /********************************************************** * * 下面进行正常的套接字通信 * **********************************************************/ RaiseServerResponse(socketFd); for(;;); // 关闭套接字的文件描述符 close(socketFd); return EXIT_SUCCESS;}/* 客户端将文件上传到服务器上 */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)); if((count = send(socketFd, buffer, BUFFER_SIZE, 0)) < 0) { perror("Send file information"); exit(1); } printf("客户端待上传待文件名[%s]..\n", filename); /* 打开文件流 */ if((stream = fopen(filePath, "r")) == NULL) { printf("Can't open the file [%s], errno = %d\n", filePath, errno); 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"); } /// BUG 这里其实有问题 /// 因为客户端将文件发送完毕后, 服务器是不知道的, /// 因此当客户端文件发送完毕后, 服务器会陷入一个死等的循环 /// 这时一个问题, 但是不是我们代码的重点, /// 因为我们的代码, 只是用于学习套接字网络编程 /// /// 这个BUG其实很好处理, 因此我们在网络传输的过程中 /// 客户端与服务器通信的数据肯定有我们自己的格式或者规范 /// 比如 [request/response HEAD + LENGTH + DATA]的格式 /// 要不然连基本的UDP丢包 和 TCP粘包问题都解决不了 /// 一般情况下, 我们与客户 /* 关闭与服务器通讯的套接字 */ //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); /* 首先获取服务器发送过来的文件名 */ if((count = recv(socketFd, buff, BUFFER_SIZE, 0)) < 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); /* 关闭文件流 */}// 客户端进行的处理逻辑void RaiseServerResponse(int socketFd){ char buffer[BUFFER_SIZE]; /* 数据缓冲区 */ int count; /* 接受或者发送的数据大小 */ printf("\n\n\n下面将依次测试 发送数据 接收数据 上传文件 下载文件\n\n\n"); // 发送数据流 printf("===========send data===========\n"); bzero(buffer, BUFFER_SIZE); strcpy(buffer, "How are you ?"); if((count = send(socketFd, buffer, strlen(buffer) + 1, 0)) < 0) { printf("send data[%s] error[errno = %d]...\n", buffer, errno); printf("发送数据[%s] 失败[错误码 = %d]...\n", buffer, errno); } else { printf("send data[%s] success...\n", buffer); printf("发送数据[%s]成功...\n", buffer); } printf("===========send data===========\n\n\n"); // 接受数据流 printf("===========recv data===========\n"); bzero(buffer, BUFFER_SIZE); if((count = recv(socketFd, buffer, BUFFER_SIZE, 0)) < 0) { printf("recv data error[errno = %d]...\n", errno); printf("接收数据失败[错误码 = %d]...\n", errno); } else { printf("recv %d data : %s\n", count, buffer); printf("接收%d个数据 : %s\n", count, buffer); } printf("===========recv data===========\n\n\n"); // 上传文件到服务器 printf("===========push file===========\n"); TcpClientPushFile(socketFd, "./cdata/cpush"); printf("===========push file===========\n\n\n"); // 上传文件到服务器 //printf("===========push file===========\n"); //TcpClientPullFile(socketFd, "./cdata/"); //printf("===========push file===========\n\n\n"); //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
  • 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
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 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
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325

服务器


/**********************************************************    > File Name: TcpServer.c    > Author: GatieMe    > Mail: gatieme@163.com    > Created Time: 2015年12月09日 星期三 16时26分46秒 *********************************************************/#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TCP_SERVER_PORT 6666 /* 服务器的端口 */#define BUFFER_SIZE 4096#define IP_SIZE 20#define MAX_FILENAME_SIZE 256#define LISTEN_QUEUE 20extern int errno;/* 服务器接收从客户端传送来的文件 */voidTcpServerPullFile( int connFd, /* 服务器与客户端通讯的套接字文件 */ struct sockaddr_in clientAddr, /* 与之通信的客户端的信息 */ char *fileServerRoot); /* 上传文件的存储路径 *//* 服务器将文件发送到客户端 */void TcpServerPushFile( int connFd, /* 服务器与客户端通讯的套接字文件 */ struct sockaddr_in clientAddr, /* 与之通信的客户端的信息 */ char *filePath); /* 带发送至客户端的文件路径 *//* 处理子进程退出的信号处理函数 */void SignalChild(int signo); /* 信号的标示信息 *//// 处理客户端的请求信息void RaiseClientRequest( int connFd, /* 客户端的连接套接字描述符, 用于发送和接收数据 */ struct sockaddr_in clientAddr); /* 客户端的信息, 用于显示一些客户端的信息 */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); } /* 绑定端口 */ /********************************************************** * * 命名服务器的套接字, 进行BIND端口绑定 * **********************************************************/ 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; int childPid; /// modify by gatieme at 2015-12-10 16:12 /// 添加了对子进程退出的异常信号处理 // // 对父进程使用fork派生子进程后,如果子进程运行结束,那么该进程不会立刻被销毁, // 而会进入“僵尸状态”,仍然维护着自身的信息, // 这时候如果服务器父进程不加以处理, // 那么很快就会消耗完系统的内存空间,所以父进程需要监听子进程SIGCHLD信号, // 并做出处理以销毁残留信息,这里可以使用wait或者waitpid来实现。 // 我们在父进程调用listen之后, // 注册监听信号和信号处理函数signal(SIGCHLD, SignalChild); // // SIGCHLD 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。 // 缺省情况下该Signal会被忽略 signal(SIGCHLD, SignalChild); //子进程退出的信号处理 while( 1 ) { /* accept返回一个新的套接字与客户端进行通信 */ /********************************************************** * * ACCEPT返回一个新的套接字与客户端进行通信 * **********************************************************/ if((connFd = accept(socketFd, (struct sockaddr*)&clientAddr, &length)) < 0) { printf("accept error, errno = %d...\n", errno); continue; } else { // // 这里填写服务器的处理代码 // printf("\n\naccept connect from client %s\n",inet_ntoa(clientAddr.sin_addr)); printf("获取到从客户端%s的连接...\n\n\n", inet_ntoa(clientAddr.sin_addr)); if((childPid = fork( )) == 0) // FORK在子进程中返回0, 在父进程中返回子进程的ID { // 千万不要以为fork出来一个子进程就产生了2个新的socket描述符, // 实际上子进程和父进程是共享socketFd和connFd的, // 每个文件和套接字都有一个引用计数, // 引用计数在文件表项中维护, // 它是当前打开着的引用该文件或套接字的描述符的个数。 // socket返回后与socketFd关联的文件表项的引用计数为1。 // accept返回后与connFd关联的文件表项的引用计数也为1。 // 然后fork返回后,这两个描述符就在父进程与子进程间共享(也就是被复制), // 因此与这两个套接字相关联的文件表项各自的访问计数值均为2。 // 这么一来,当父进程关闭connFd时, // 它只是把相应的引用计数值从2减为1。 // 该套接字真正的清理和资源释放要等到其引用计数值到达0时才发生。 // 这会在稍后子进程也关闭connFd时发生。 // // 因此当父进程关闭connFd的时候它只是把这个connfd的访问计数值减了1而已, // 由于访问计数值还 > 0(因为还有客户端的connFd连着呢), // 所以它并没有断开和客户端的连接。 // // // 因此在fork之后socketFd和connFd的引用计数均为2,父子进程均有一份 // 在父进程中, 需要关闭客户端连接套接字的文件描述符号connfd -= 1 // 在子进程中, 需要关闭服务器的文件socketFd, // 因为他不需要监听客户端的连接 socketFd -= 1 // 然后处理完毕后需要关闭自己的客户端套接字connFd -= 1 // // 这样在客户端处理完信息后, connFd == 0将被完全关闭 // socketFd仅在父亲进程中仍被打开 close(socketFd); printf("分叉了一个新的进程%d用于处理客户端的连接信息\n", getpid()); printf("fork a new process %d to deal with the client\n", getpid()); RaiseClientRequest(connFd, clientAddr); close(connFd); exit(0); // 子进程结束,会成为僵尸进程,所以注册SIGCHLD来让父亲进程处理僵尸进程的遗留数据 // signal(SIGCHLD, SignalChild); //子进程退出的信号处理 } else if(childPid < 0) // FROK出错返回-1 { printf("fork error: %s\n", strerror(errno)); close(connFd); } } } close(socketFd);} /* 服务器接收从客户端传送来的文件 */voidTcpServerPullFile( 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)); /// BUG 这里其实有问题 /// 因为客户端将文件发送完毕后, 服务器是不知道的, /// 因此当客户端文件发送完毕后, 服务器会陷入一个死等的循环 /// 这时一个问题, 但是不是我们代码的重点, /// 因为我们的代码, 只是用于学习套接字网络编程 /// /// 这个BUG其实很好处理, 因此我们在网络传输的过程中 /// 客户端与服务器通信的数据肯定有我们自己的格式或者规范 /// 比如 [request/response HEAD + LENGTH + DATA]的格式 /// 要不然连基本的UDP丢包 和 TCP粘包问题都解决不了 /// 一般情况下, 我们与客户}/* 服务器将文件发送到客户端 * * 当用户选择了下载文件后,服务器将执行此操作 * * */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("%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((count =send(connFd, buff, fileBlockLength, 0)) < 0) { perror("Send file error...\n"); perror("向客户端发送文件失败...\n"); exit(1); } bzero(buff,BUFFER_SIZE); } fclose(stream); printf("服务器发送文件成功\n");}/// 处理客户端的请求信息void RaiseClientRequest( int connFd, /* 客户端的连接套接字描述符, 用于发送和接收数据 */ struct sockaddr_in clientAddr) /* 客户端的信息, 用于显示一些客户端的信息 */{ printf("\n\n\n下面将依次测试 接收数据 发送数据 存储文件 推送文件\n\n\n"); int count; char buffer[BUFFER_SIZE]; // 首先测试接收客户端发送来的数据 printf("===========recv data===========\n"); bzero(buffer, BUFFER_SIZE); if((count = recv(connFd, buffer, BUFFER_SIZE, 0)) < 0) { printf("recv data error from %s error, errno = %d...\n", inet_ntoa(clientAddr.sin_addr), errno); printf("接收来自 %s 的数据错误, 错误码errno = %d....\n", inet_ntoa(clientAddr.sin_addr), errno); } else { printf("recv %d data : %s\n", count, buffer); printf("接收%d个数据 : %s\n", count, buffer); } printf("===========recv data===========\n\n\n"); // 接着测试向客户端发送反馈数据 printf("===========send data===========\n"); bzero(buffer, BUFFER_SIZE); strcpy(buffer, "I am fine !"); if((count = send(connFd, buffer, strlen(buffer) + 1, 0)) < 0) { printf("send data[%s] error[errno = %d]...\n", buffer, errno); printf("发送数据[%s] 失败[错误码 = %d]...\n", buffer, errno); } else { printf("send data[%s] success...\n", buffer); printf("发送数据[%s]成功...\n", buffer); } printf("===========send data===========\n\n\n"); // 首先测试接收客户端发送来的数据 printf("===========pull file============\n"); TcpServerPullFile(connFd, clientAddr, "./sdata/"); /* 将客户端发送来的文件存储在./sdata目录下  */ printf("===========pull file============\n"); // 首先测试接收客户端发送来的数据 //printf("===========pull file============\n"); //TcpServerPushFile(connFd, clientAddr, "./sdata/spush"); /* 将客户端发送来的文件存储在./sdata目录下  */ //printf("===========pull file============\n");}// 处理子进程退出的信号处理函数void SignalChild(int signo) //父进程对子进程结束的信号处理{ pid_t pid; int stat; // SIGCHLD 20,17,18 B 子进程结束信号 printf("get a signal %d\n", signo); while((pid = waitpid(-1, &stat, WNOHANG)) > 0) { printf("child %d terminated\n", pid); printf("子进程%d终止\n", pid); } return ;}

转载:http://blog.csdn.net/gatieme/article/details/50615112

你可能感兴趣的文章
【Java学习笔记之二十三】instanceof运算符的用法小结
查看>>
【Letter Combinations of a Phone Number】cpp
查看>>
C++STL中set的使用策略(详解)
查看>>
Block的定义,以及使用
查看>>
Gimp制作圆角透明图片
查看>>
将图片保存到照片相册中
查看>>
利用CSS3制作网页动画
查看>>
JS将图片转为base64
查看>>
前端面试题二十二
查看>>
SPOJ - VISIBLEBOX [multiset的使用]
查看>>
构建之法阅读笔记
查看>>
window 10 + python3.6 +numpy+ tensorflow + pycharm
查看>>
使用bootstrap validator异步提交,出现提交两次问题!
查看>>
EntityFramework Core迁移时出现数据库已存在对象问题解决方案
查看>>
MVC 5 App 通过 Facebook OAuth2 登陆(Sign-on)的问题
查看>>
2.7.3版本hadoop之HDFS环境搭建之浅谈
查看>>
seaweddfs配置不定时更新
查看>>
日期格式化,moment.js
查看>>
使用PowerShell批量注册DLL到GAC
查看>>
谴责盛大Bambook 的ADB.EXE流氓进程
查看>>