(1)在默认的阻塞模式下的套接字里,recv会阻塞在那里,直到套接字连接上有数据可读,把数据读到buffer里后recv函数才会返回,不然就会一直阻塞在那里。在单线程的程序里出现这种情况会导致主线程(单线程程序里只有一个默认的主线程)被阻塞,这样整个程序被锁死在这里,如果永 远没数据发送过来,那么程序就会被永远锁死。这个问题可以用多线程解决,但是在有多个套接字连接的情况下,这不是一个好的选择,扩展性很差。
(2)在非阻塞模式的套接字里,recv的调用不管套接字连接上有没有数据可以接收都会马上返回。但在没有数据的情况下,recv确实是马上返回了,但是也返回了一个错误:WSAEWOULDBLOCK,即请求的操作没有成功完成。
若重复调用recv并检查返回值,直到成功为止,但是这样做效率很成问题,开销太大。
select就是为了解决上述两个问题,能够实现在一个线程内完成并发操作,即I/O多路复用:多个描述符的I/O操作都能在一个线程内并发交替地完成,随着研究的进展,poll/epoll方法(select的改善)也相继出现,其功能和select类似,可以同时监视多个描述符的读写就绪状况,即在一个线程内同时处理多个socket的I/O请求。
在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件,帮助调用者寻找当前就绪的设备。

原理图如下所示,select首先进行系统调用,当数据准备好时返回socket接口,然后使用recv接受socket发送过来的数据,并进行处理。 
select伪代码如下:依次顺序遍历fd,等待数据准备好。
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;
(3)select支持的文件描述符数量太小了,默认是1024;
每次调用Select都需要将进程加入到所有监视Socket的等待队列,每次唤醒都需要从每个队列中移除。这里涉及了两次遍历,而且每次都要将整个FDS列表传递给内核,有一定的开销。所以为了减少遍历次数,并保存就绪的socket(fd),研究出现了epoll方法,它是select和poll的增强版本。
epoll伪代码如下,将所有socket加入到等待队列中,当有数据到达时,可以直接找到对应的socket,无需遍历;

使用快递来进行类比,select:需要对快递编号依次遍历,看看有没有快递员到达,也就是当快递到达时没法直接知道快递编号,需要依次遍历进行获取;epoll:当有快递到达时,可以直接得知快递编号。
server.c
- //server.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- int server_sockfd, client_sockfd;
- int server_len, client_len;
- struct sockaddr_in server_address;
- struct sockaddr_in client_address;
- int ret;
- fd_set testfds,readfds;
-
- //1.建立服务器端socket,用于描述IP地址和端口,通过socket向网络发请求或应答网络请求
- server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socket
-
- //2.绑定侦听的IP地址和端口
- server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY=0,表示侦听全部IP地址
- server_address.sin_port = htons(8888); //端口
- server_len = sizeof(server_address);
- bind(server_sockfd, (struct sockaddr *)&server_address, server_len); //将套接字套接字绑定到地址server上。
-
- //3.侦听
- listen(server_sockfd, 7); //监听队列最多容纳7个
-
- FD_ZERO(&readfds);
- FD_SET(server_sockfd, &readfds);//可读集合初始化为[server_sockfd,]
-
- while(1)
- {
- char ch;
- int nread;
- printf("server waiting......\n");
-
- /*无限期阻塞,并测试文件描述符变动 */
- testfds = readfds; //将需要监视的描述符集copy到select查询队列中,select会对其修改,所以一定要分开使用变量
- ret = select(FD_SETSIZE, &testfds, (fd_set *)0,(fd_set *)0, (struct timeval *) 0); //数据准备就绪,则文件描述符会发生变化
- /* select:寻找当前准备就绪的设备,当可读、可写、异常任一文件描述符准备就绪,则select返回,每次结束后,select重新赋值
- * 当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位(变成ready),使得进程可以获得这些文件描述符从而进行后续的读写操作
- * readfds:监视的socket,testfds:监视的socket中数据就绪的socket
- * select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
- * maxfdp(FD_SETSIZE):被监听文件描述符总数;readfds、writefds、exceptset:分别指向可读、可写和异常事件
- * timeout: 等于NULL(0)表示无限等待
- */
- //testfds :初始化的可读集合里面哪一个准备就绪了,返回的testfds就等于几,FD_SETSIZE等于初始化可读集合总数+1
- //testfds初始是监听的集合,返回值是准备好的集合
- if(ret < 1) //ret:0表示超时,-1表示失败
- {
- perror("select error");
- exit(1);
- }
-
- /*扫描所有的文件描述符*/
- for(int fd = 0; fd < FD_SETSIZE; fd++) /*循环遍历*/
- {
- if(FD_ISSET(fd,&testfds)) //有数据准备就绪,fd为进来的请求,定位置位
- {
- // printf("fd:%d\n",fd);
- if(fd == server_sockfd) /*判断是否为服务器套接字(3),是则表示为客户请求连接。*/
- {
- client_len = sizeof(client_address);
- client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, &client_len); //接收客户端socket
- FD_SET(client_sockfd, &readfds);//将客户端socket加入到集合中(可读集合扩充了)
- printf("adding client on fd %d......\n", client_sockfd);
- }
- else /*客户端socket中有数据请求时*/
- {
- char cmd[256];
- ret = recv(fd, cmd, sizeof(cmd), 0); //接受数据(命令)
- // printf("cmd:%s",cmd);
-
- if(ret < 1 || strncmp(cmd, "exit", 4) ==0)/*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */
- {
- close(fd);
- FD_CLR(fd, &readfds); //去掉关闭的fd
- printf("client on fd %d closed......\n", fd);
- }
- else /*对数据进行处理*/
- {
- /*处理客户数据请求*/
- if(strncmp(cmd, "put", 3) ==0) //上传文件
- {
- char filename[256] = "";
- char filepath[256] = "./data/"; //上传的文件存放在服务器的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
-
- // printf("recv cmd on fd %d: %s\n", fd,cmd);
- char a[2] = "ok";
- ret = send(fd, a,sizeof(a), 0); //通知客户端收到,防止粘包
-
- char buffer[1024];
- FILE * fp = fopen(filepath,"w"); //保存客户端发送的文件内容
- bzero(buffer,1024);
- int length = 0;
- if((length = recv(fd,buffer,1024,0))>0) //接收并写到文件
- {
- if(length < 0)
- {
- printf("Recieve Data Failed!\n");
- break;
- }
- int write_length = fwrite(buffer,sizeof(char),length,fp);
- if (write_length
- {
- printf("File: Write Failed\n");
- break;
- }
- }
- printf("Successfully received file %s(filelength = %i) on fd %d...\n", filename,length,fd);
- fclose(fp);
- }
- else if(strncmp(cmd, "get" , 3) == 0)//下载文件
- {
- char filename[256] = "";
- char filepath[256] = "./data/"; //下载的文件在服务器的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
- if((access(filepath,F_OK))!=-1) //文件存在
- {
- char a[2] = "ok";
- ret = send(fd, a,sizeof(a), 0); //通知客户端文件存在
-
- char buffer[1024];
- FILE * fp = fopen(filepath,"r");
- bzero(buffer, 1024);
- int file_length = 0;
- while( (file_length = fread(buffer,sizeof(char),1024,fp))>0)
- {
- if(send(fd,buffer,file_length,0)<0) //向客户端发送文件内容
- {
- printf("Send File:\t%s Failed\n", filepath);
- break;
- }
- bzero(buffer, 1024);
- printf("put all file(file_length = %d) to fd %d...\n",file_length,fd);
- }
- fclose(fp);
- }
- else
- {
- char a[2] = "on";
- ret = send(fd, a,sizeof(a), 0); //通知客户端文件不存在
- printf("get file is not valid\n");
- }
- }
- }
- }
- }
- }
- }
-
- return 0;
- }
client.c
- //client.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
-
- int main()
- {
- int client_sockfd,len,ret;
- struct sockaddr_in address;//服务器端网络地址结构体
- char cmd[256];
-
- //1.建立客户端socket
- client_sockfd = socket(AF_INET, SOCK_STREAM, 0);
- //2.连接服务端
- address.sin_family = AF_INET;
- address.sin_addr.s_addr = inet_addr("127.0.0.1");
- address.sin_port = htons(8888);
- len = sizeof(address);
- ret = connect(client_sockfd, (struct sockaddr *)&address, len);
- if(ret == -1)
- {
- perror("connect error");
- exit(1);
- }
-
- printf("------Welcome!!-----\n");
- printf(" (helps:information you need)\n");
-
- while(1)
- {
- printf(">>>");
- bzero(cmd,256);
- if(fgets(cmd,256,stdin) == NULL)
- {
- printf("input Error!\n");
- return -1;
- }
-
- cmd[strlen(cmd)-1]='\0'; //fgets函数读取的最后一个字符为换行符,此处将其替换为'\0'
-
- if(strncmp(cmd, "put", 3) ==0 ) //上传文件(空格是为了标准化格式)
- {
- if(cmd[3]=='\0'||cmd[4]=='\0') //直接回车
- {
- printf("No filename follows after put cmd!\n");
- continue;
- }
-
- char filename[256] = "";
- char filepath[256] = "./data/"; //上传文件在本地的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
- if((access(filepath,F_OK))!=-1) //文件存在
- {
- ret = send(client_sockfd, cmd,sizeof(cmd), 0); //发送命令
-
- char a[2];
- ret = recv(client_sockfd, a, sizeof(a), 0); //防止粘包
- if(strncmp(a, "ok" , 2) == 0) //收到服务器的回应
- {
- char buffer[1024];
- FILE * fp = fopen(filepath,"r");
- bzero(buffer, 1024);
- int file_length = 0;
- while( (file_length = fread(buffer,sizeof(char),1024,fp))>0)
- {
- //上传文件内容
- if(send(client_sockfd,buffer,file_length,0)<0)
- {
- printf("Send File:\t%s Failed\n", filepath);
- break;
- }
- bzero(buffer, 1024);
- printf("put all file(file_length = %d)...\n",file_length);
- } //这段代码是循环读取文件的一段数据,在循环调用send,发送到客户端,这里强调一点的TCP每次接受最多是1024字节,多了就会分片,因此每次发送时尽量不要超过1024字节。
- fclose(fp);
- }
- }
- else{
- printf("File is not valid\n");
- continue;
- }
- }
- else if(strncmp(cmd, "get" , 3) == 0) //下载文件
- {
- if(cmd[3]=='\0'||cmd[4]=='\0') //直接回车的情况
- {
- printf("No filename follows after get cmd!\n");
- continue;
- }
-
- char filename[256] = "";
- char filepath[256] = "./data/"; //下载的文件在服务器的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
-
- ret = send(client_sockfd,cmd,sizeof(cmd), 0); //发送命令
-
- char a[2];
- ret = recv(client_sockfd, a, sizeof(a), 0); //防止粘包
- if(strncmp(a, "ok" , 2) == 0) //收到服务器的回应,文件存在
- {
- char buffer[1024];
- FILE * fp = fopen(filepath,"w"); //保存服务器发送的文件内容
- bzero(buffer,1024);
- int length = 0;
- if((length = recv(client_sockfd,buffer,1024,0))>0) //接收并写到文件
- {
- if(length < 0)
- {
- printf("Recieve Data Failed!\n");
- break;
- }
- int write_length = fwrite(buffer,sizeof(char),length,fp);
- if (write_length
- {
- printf("File: Write Failed\n");
- break;
- }
- }
- printf("Successfully received file %s(filelength = %i) ...\n", filename,length);
- fclose(fp);
- }
- else
- {
- printf("get file is not valid\n");
- }
- }
- else if(strncmp(cmd,"helps",5) == 0) //帮助信息
- {
- printf("put filename #upload\n");
- printf("get filename #download\n");
- printf("exit #exit\n");
- printf("others #return NULL\n");
- }
- else if(strncmp(cmd,"exit",4) == 0) //退出
- {
- ret = send(client_sockfd, cmd,sizeof(cmd), 0); //发送命令
- close(client_sockfd);
- exit(1);
- }
- else
- {
- printf("NULL\n");
- }
- }
-
- return 0;
- }
操作说明:
#原始文件#
--server
--/data
--5.txt 6.txt 服务器中的文件
--client
--/data
--1.txt 2.txt 客户端中的文件
1.打开服务器
cd server
gcc server.c -o server
./server
2.打开客户端1
cd client
gcc client.c -o client
./client
#向服务器上传文件1.txt,成功后会出现/server/data/1.txt
put 1.txt
#从服务器中下载文件5.txt,成功后会出现/client/data/5.txt
get 5.txt
3.打开客户端2
cd client
gcc client.c -o client
./client
#向服务器上传文件2.txt,成功后会出现/server/data/2.txt
put 2.txt
#从服务器中下载文件6.txt,成功后会出现/client/data/6.txt
get 6.txt
- epoll
server.c
- //server.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- int server_sockfd, client_sockfd;
- int server_len, client_len;
- struct sockaddr_in server_address;
- struct sockaddr_in client_address;
- int ret;
-
- //1.建立服务器端socket,用于描述IP地址和端口,通过socket向网络发请求或应答网络请求
- server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socket
-
- //2.绑定侦听的IP地址和端口
- server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY=0,表示侦听全部IP地址
- server_address.sin_port = htons(8787); //端口
- server_len = sizeof(server_address);
- bind(server_sockfd, (struct sockaddr *)&server_address, server_len); //将套接字套接字绑定到地址server上。
-
- //3.侦听
- listen(server_sockfd, 7); //监听队列最多容纳7个
-
- int epollfd = epoll_create(10); //创建文件描述符(10表示epollfd上能关注的最大fd数)
-
- struct epoll_event tmp,epevents[5]; //创建epoll对象
- tmp.events = EPOLLIN; //表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
- tmp.data.fd = server_sockfd;
- epoll_ctl(epollfd,EPOLL_CTL_ADD,server_sockfd,&tmp);//将需要监视的socket加入到epollfd中(关注了server_sockfd),等待队列
- /*(epoll_create()返回值,动作,需要监听的socket,需要监听的内容(可读))*/
-
- while(1)
- {
- char ch;
- int nread;
- printf("server waiting......\n");
-
- //获取准备好的描述符事件
- int num = epoll_wait(epollfd,epevents,5,-1); //等待epevents对象,可读时通知,读到后就改为写时通知,epollfd为需要监视的socket集合
-
- /*select扫描所有的fd,然后准备就绪的fd放在等待队列进行数据处理
- *epoll 不用,其等待队列已经存放于events中(即epevents[i].data.fd),只需要判断它的fd是不是可读
- *select每次都需要添加可读等待队列(fd),然后阻塞,遍历所有的fd-size。
- *但是epoll不用,其等待队列(fd)已经存放于events中,一直阻塞,只需要遍历epevents事件判断其fd是否可读
- */
-
- /*扫描所有的文件描述符*/
- for(int i = 0; i < num; i++) /*num为等待到的连接数(1)*/
- {
- if(!(epevents[i].events & EPOLLIN)) //如果不是读事件,直接跳过 [&:两个都运行,有一个为假就不成立]
- {
- continue;
- }
- if(epevents[i].data.fd == server_sockfd) /*判断是否为服务器套接字(3),是则表示为客户请求连接。*/
- {
- client_len = sizeof(client_address);
- client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, &client_len); //接收客户端socket
- tmp.events = EPOLLIN;
- tmp.data.fd = client_sockfd;
- ret = epoll_ctl(epollfd,EPOLL_CTL_ADD,client_sockfd, &tmp); //将客户端socket加入到epollfd中
- printf("adding client on fd %d......\n", client_sockfd);
- }
- else /*客户端socket中有数据请求时*/
- {
- char cmd[256];
- ret = recv(epevents[i].data.fd, cmd, sizeof(cmd), 0); //接受数据(命令)
-
- if(ret < 1 || strncmp(cmd, "exit", 4) ==0)/*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */
- {
- close(epevents[i].data.fd);
- epoll_ctl(epollfd,EPOLL_CTL_DEL,epevents[i].data.fd,NULL);//删除已经注册的fd
- printf("client on fd %d closed......\n", epevents[i].data.fd);
- }
- else /*对数据进行处理*/
- {
- /*处理客户数据请求*/
- if(strncmp(cmd, "put", 3) ==0) //上传文件
- {
- char filename[256] = "";
- char filepath[256] = "./data/"; //上传的文件存放在服务器的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
-
- // printf("recv cmd on fd %d: %s\n", fd,cmd);
- char a[2] = "ok";
- ret = send(epevents[i].data.fd, a,sizeof(a), 0); //通知客户端收到,防止粘包
-
- char buffer[1024];
- FILE * fp = fopen(filepath,"w"); //保存客户端发送的文件内容
- bzero(buffer,1024);
- int length = 0;
- if((length = recv(epevents[i].data.fd,buffer,1024,0))>0) //接收并写到文件
- {
- if(length < 0)
- {
- printf("Recieve Data Failed!\n");
- break;
- }
- int write_length = fwrite(buffer,sizeof(char),length,fp);
- if (write_length
- {
- printf("File: Write Failed\n");
- break;
- }
- }
- printf("Successfully received file %s(filelength = %i) on fd %d...\n", filename,length,epevents[i].data.fd);
- fclose(fp);
- }
- else if(strncmp(cmd, "get" , 3) == 0)//下载文件
- {
- char filename[256] = "";
- char filepath[256] = "./data/"; //下载的文件在服务器的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
- if((access(filepath,F_OK))!=-1) //文件存在
- {
- char a[2] = "ok";
- ret = send(epevents[i].data.fd, a,sizeof(a), 0); //通知客户端文件存在
-
- char buffer[1024];
- FILE * fp = fopen(filepath,"r");
- bzero(buffer, 1024);
- int file_length = 0;
- while( (file_length = fread(buffer,sizeof(char),1024,fp))>0)
- {
- if(send(epevents[i].data.fd,buffer,file_length,0)<0) //向客户端发送文件内容
- {
- printf("Send File:\t%s Failed\n", filepath);
- break;
- }
- bzero(buffer, 1024);
- printf("put all file(file_length = %d) to fd %d...\n",file_length,epevents[i].data.fd);
- }
- fclose(fp);
- }
- else
- {
- char a[2] = "on";
- ret = send(epevents[i].data.fd, a,sizeof(a), 0); //通知客户端文件不存在
- printf("get file is not valid\n");
- }
- }
- }
- }
- }
- }
-
- close(epollfd);
-
- return 0;
- }
client.c
- //client.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
-
- int main()
- {
- int client_sockfd,len,ret;
- struct sockaddr_in address;//服务器端网络地址结构体
- char cmd[256];
-
- //1.建立客户端socket
- client_sockfd = socket(AF_INET, SOCK_STREAM, 0);
- //2.连接服务端
- address.sin_family = AF_INET;
- address.sin_addr.s_addr = inet_addr("127.0.0.1");
- address.sin_port = htons(8787);
- len = sizeof(address);
- ret = connect(client_sockfd, (struct sockaddr *)&address, len);
- if(ret == -1)
- {
- perror("connect error");
- exit(1);
- }
-
- printf("------Welcome!!-----\n");
- printf(" (helps:information you need)\n");
-
- while(1)
- {
- printf(">>>");
- bzero(cmd,256);
- if(fgets(cmd,256,stdin) == NULL)
- {
- printf("input Error!\n");
- return -1;
- }
-
- cmd[strlen(cmd)-1]='\0'; //fgets函数读取的最后一个字符为换行符,此处将其替换为'\0'
-
- if(strncmp(cmd, "put", 3) ==0 ) //上传文件(空格是为了标准化格式)
- {
- if(cmd[3]=='\0'||cmd[4]=='\0') //直接回车
- {
- printf("No filename follows after put cmd!\n");
- continue;
- }
-
- char filename[256] = "";
- char filepath[256] = "./data/"; //上传文件在本地的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
- if((access(filepath,F_OK))!=-1) //文件存在
- {
- ret = send(client_sockfd, cmd,sizeof(cmd), 0); //发送命令
-
- char a[2];
- ret = recv(client_sockfd, a, sizeof(a), 0); //防止粘包
- if(strncmp(a, "ok" , 2) == 0) //收到服务器的回应
- {
- char buffer[1024];
- FILE * fp = fopen(filepath,"r");
- bzero(buffer, 1024);
- int file_length = 0;
- while( (file_length = fread(buffer,sizeof(char),1024,fp))>0)
- {
- //上传文件内容
- if(send(client_sockfd,buffer,file_length,0)<0)
- {
- printf("Send File:\t%s Failed\n", filepath);
- break;
- }
- bzero(buffer, 1024);
- printf("put all file(file_length = %d)...\n",file_length);
- } //这段代码是循环读取文件的一段数据,在循环调用send,发送到客户端,这里强调一点的TCP每次接受最多是1024字节,多了就会分片,因此每次发送时尽量不要超过1024字节。
- fclose(fp);
- }
- }
- else{
- printf("File is not valid\n");
- continue;
- }
- }
- else if(strncmp(cmd, "get" , 3) == 0) //下载文件
- {
- if(cmd[3]=='\0'||cmd[4]=='\0') //直接回车的情况
- {
- printf("No filename follows after get cmd!\n");
- continue;
- }
-
- char filename[256] = "";
- char filepath[256] = "./data/"; //下载的文件在服务器的位置
- for(int i=4;cmd[i]!='\0';i++)
- {
- filename[i-4]=cmd[i];
- }
- strcat(filepath,filename);
-
- ret = send(client_sockfd,cmd,sizeof(cmd), 0); //发送命令
-
- char a[2];
- ret = recv(client_sockfd, a, sizeof(a), 0); //防止粘包
- if(strncmp(a, "ok" , 2) == 0) //收到服务器的回应,文件存在
- {
- char buffer[1024];
- FILE * fp = fopen(filepath,"w"); //保存服务器发送的文件内容
- bzero(buffer,1024);
- int length = 0;
- if((length = recv(client_sockfd,buffer,1024,0))>0) //接收并写到文件
- {
- if(length < 0)
- {
- printf("Recieve Data Failed!\n");
- break;
- }
- int write_length = fwrite(buffer,sizeof(char),length,fp);
- if (write_length
- {
- printf("File: Write Failed\n");
- break;
- }
- }
- printf("Successfully received file %s(filelength = %i) ...\n", filename,length);
- fclose(fp);
- }
- else
- {
- printf("get file is not valid\n");
- }
- }
- else if(strncmp(cmd,"helps",5) == 0) //帮助信息
- {
- printf("put filename #upload\n");
- printf("get filename #download\n");
- printf("exit #exit\n");
- printf("others #return NULL\n");
- }
- else if(strncmp(cmd,"exit",4) == 0) //退出
- {
- ret = send(client_sockfd, cmd,sizeof(cmd), 0); //发送命令
- close(client_sockfd);
- exit(1);
- }
- else
- {
- printf("NULL\n");
- }
- }
-
- return 0;
- }
操作说明:
#原始文件#
--server
--/data
--5.txt 6.txt 服务器中的文件
--client
--/data
--1.txt 2.txt 客户端中的文件
1.打开服务器
cd server
gcc server.c -o server
./server
2.打开客户端1
cd client
gcc client.c -o client
./client
#向服务器上传文件1.txt,成功后会出现/server/data/1.txt
put 1.txt
#从服务器中下载文件5.txt,成功后会出现/client/data/5.txt
get 5.txt
3.打开客户端2
cd client
gcc client.c -o client
./client
#向服务器上传文件2.txt,成功后会出现/server/data/2.txt
put 2.txt
#从服务器中下载文件6.txt,成功后会出现/client/data/6.txt
get 6.txt
参考博客:
https://www.cnblogs.com/skyfsm/p/7079458.html
https://www.cnblogs.com/-zyj/p/5719923.html
-
相关阅读:
Vue2封装评论组件详细讲解
SpringBoot--HttpServletRequest中的方法--使用/详解
见缝插针游戏针不会更着上面旋转的小球运动
CUDA学习笔记5——CUDA程序错误检测
[carla] GNSS传感器与Carla坐标系 转换方法
jmeter如何压测和存储
200、使用默认 Exchange 实现 P2P 消息 之 消息生产者(发送消息) 和 消息消费者(消费消息)
软考高级信息系统项目管理师系列之二:信息化和信息系统习题
LeetCode简单题之数组能形成多少数对
深入理解c指针(五)
-
原文地址:https://blog.csdn.net/m0_37865160/article/details/126700709