基于TCP的半关闭
单方面断开连接带来的问题
调用close函数断开连接,意味着完全断开连接,不仅无法传输数据,而且也不能接收数据。
套接字和流(Stream)
两台主机通过套接字建立连接后进入可交换数据的状态。把建立套接字后可交换数据的状态看作一种流。
为了实现双向通信,套接字生成的两个流。
针对优雅断开的shutdown函数
shutdown函数关闭其中一个流。
调用上述函数时,第二个参数决定断开连接的方式:
-SHUT_RD:断开输入流
-SHUT_WR:断开输出流
-SHUT_RDWR:同时断开I/O流
基于半关闭的文件传输程序
“一旦客户连接到服务器端,服务器端将约定的文件传给客户端,客户端收到后发送字符串 ‘Thank you’ 给服务器端。
服务端:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h> #define BUF_SIZE 30 void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); } int main(int argc, char *argv[]) { int serv_sd, clnt_sd; FILE *fp; char buf[BUF_SIZE]; int read_cnt; struct sockaddr_in serv_adr, clnt_adr; socklen_t clnt_adr_sz; if (argc != 2) { printf("Usage: %s <port> \n",argv[0]); exit(1); } fp = fopen("file_server.c","rb"); serv_sd = socket(PF_INET,SOCK_STREAM,0); memset(&serv_adr,0,sizeof(serv_adr)); serv_adr.sin_family = AF_INET; serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); serv_adr.sin_port = htons(atoi(argv[1])); bind(serv_sd,(struct sockaddr*) &serv_adr,sizeof(serv_adr)); listen(serv_sd,5); clnt_adr_sz = sizeof(clnt_adr); clnt_sd = accept(serv_sd,(struct sockaddr*) &clnt_adr, &clnt_adr_sz); while(1) { read_cnt = fread((void*)buf,1,BUF_SIZE,fp); //从文件描述符fp指向的文件中读数据到buf缓冲区 if (read_cnt < BUF_SIZE) { write(clnt_sd,buf,read_cnt); break; } write(clnt_sd,buf,BUF_SIZE); } shutdown(clnt_sd,SHUT_WR); //发送文件后针对输出流进行半关闭。 read(clnt_sd,buf,BUF_SIZE); //只关闭了输出流,依旧可以通过输入流接收数据。 printf("Message from client: %s \n",buf); fclose(fp); close(clnt_sd); close(serv_sd); return 0; }
客户端:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h> #define BUF_SIZE 30 void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); } int main(int argc, char *argv[]) { int sd; FILE *fp; char buf[BUF_SIZE]; int read_cnt; struct sockaddr_in serv_adr; if (argc != 3) { printf("Usage: %s <port> \n",argv[0]); exit(1); } fp = fopen("receive.dat","wb"); //创建新文件保存服务器端传输的文件数据 sd = socket(PF_INET,SOCK_STREAM,0); memset(&serv_adr,0,sizeof(serv_adr)); serv_adr.sin_family = AF_INET; serv_adr.sin_addr.s_addr = inet_addr(argv[1]); serv_adr.sin_port = htons(atoi(argv[2])); connect(sd,(struct sockaddr*) &serv_adr,sizeof(serv_adr)); while ((read_cnt = read(sd,buf,BUF_SIZE)) != 0) fwrite((void*)buf,1,read_cnt,fp); //将接收的数据写入到fp指向的文件receive.dat中 puts("Receive file data"); write(sd,"Thank you",10); fclose(fp); close(sd); return 0; }
完