《TCP IP网络编程》第七章 优雅地断开套接字连接

    技术2022-07-11  77

    第七章 优雅地断开套接字连接

    直接调用close或closesocket函数单方面断开连接,不叫优雅。

    7.1 基于TCP的半关闭

    单方面断开连接带来的问题

    主机A强行close后,由主机B传输的、主机A必须接收的数据也销毁了,所以不优雅。

    只关闭一部分数据交换中使用的流的方法就叫优雅的方法。 断开一部分是指:可以传输数据但是无法接收,或可以接收数据但无法传输,就是只关闭流的一半。

    套接字和流

    两台主机之间有两个流,一个从A到B,一个从B到A,只断开其中的一个流

    针对优雅断开的shutdown函数

    用于半关闭的函数:

    #include<sys/socket.h> int shutdown(int sock,int howto); sock 需要断开的套接字文件描述符 howto 传递断开方式信息 SHUT_RD 断开输入流,套接字无法接收数据,即使输入缓冲收到数据也会抹去 SHUT_WR 断开输出流,套接字无法传输数据。但如果输出缓冲还留有未传输的数据,则将传递至目标主机 SHUT_RDWR 同时断开I/O流 成功返回0,失败返回-1

    基于半关闭的文件传输程序

    理解传递EOF的重要性和半关闭的重要性: 传递EOF的重要性在于,要告诉客户端,我这边给你发的文件发完啦,别再一直调用输入函数啦; 半关闭的重要性在于,即使通过关闭输出流向文件发送了EOF,也仍然还保留有输入流,这样还是能收到客户端发来的Thank you字符串。

    file_server.c:

    [root@VM_0_10_centos tcpHalf]# cat file_server.c #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); 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); 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; } void handling(char * message){ fputs(message,stderr); fputc('\n',stderr); exit(1); }

    file_client.c :

    [root@VM_0_10_centos tcpHalf]# cat file_client.c #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); 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 <IP> <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); puts("Received file data"); write(sd,"Thank you",10); fclose(fp); close(sd); return 0; } void error_handling(char* message){ fputs(message,stderr); fputc('\n',stderr); exit(1); }

    运行结果:

    [root@VM_0_10_centos tcpHalf]# ./fserver 9190 & [1] 5639 [root@VM_0_10_centos tcpHalf]# ./fclient 127.0.0.1 9190 Received file data Message from client:Thank you 服务器端半关闭输出流后,表明不往外写东西了,但仍可以接收到客户端发来的信息 [1]+ Done ./fserver 9190 [root@VM_0_10_centos tcpHalf]#

    7.2 习题

    (1)解释TCP中“流”的概念。UDP中能否形成流?请说明原因

    TCP的流指:两台主机通过套接字建立连接后进入可交换数据的状态,也称为“流形成的状态”。而对于UDP来说,不存在流,因为两个SOCKET不能相互连接

    (2)Linux中的close函数或Windows中的closesocket函数属于单方面断开连接的方法,有可能带来一些问题。什么是单方面断开连接?什么情况下会出现问题?

    单方面的断开连接意味着套接字无法再发送数据。一般在对方有剩余数据未发送完成时,断开己方连接,会造成问题。

    (3)什么是半关闭?针对输出流执行半关闭的主机处于何种状态?半关闭会导致对方主机接收什么信息?

    半关闭是指只关闭输入和输出流中的其中一个。而且,如果对输出流进行半关闭,EOF被传送到对方主机,己方套接字无法再传送别的数据,但可以接收对方主机传送的数据。

    Processed: 0.009, SQL: 9