【TCP/IP网络编程】:07优雅地断开套接字连接

本篇文章简单讨论了TCP套接字半关闭的相关知识。

通常来说,TCP建立连接的过程相对稳定,因为此时并未开始进行数据交换;而断开连接的过程由于已发生了数据交换,可能会发生一些预想不到的情况。

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

前文所述的内容中,我们直接调用了close函数进行了完全断开连接,这就意味着本端既无法再发送数据,也不能再接收数据了。而如果本端仅仅希望不再发送数据,还能够接收数据的话,直接调用close完全断开连接则显得不够优雅。因此,我们需要一种“只关闭一部分数据交换中使用的流”(Half-close)的方法。

单方面断开连接

套接字和流(Stream)

两台主机通过套接字建立连接后进入可交换数据的状态,又称为“流形成的状态”。每台主机都拥有单独的输入流和输出流,并和对端的输出流和输入流相匹配而形成两个I/O流。

套接字中形成的两个I/O流

针对优雅断开的shutdown函数

shutdown可用来断开双向I/O流中的一个。

#include <sys/socket.h>

int shutdown(int sock, int howto);
    -> 成功时返回0,失败时返回-1

其中,第二个参数决定断开流的方式:

  • SHUT_RD:断开输入流
  • SHUT_WR:断开输出流
  • SHUT_RDWR:同时断开两个I/O流

所谓断开流,其实是断开套接字与其I/O缓冲区之间的通道。因此,SHUT_RD断开输入流,套接字便无法接收数据,即使输入缓冲收到数据也会被抹去,且无法调用输入相关函数;SHUT_WR断开输出流,套接字便无法传输数据,但如果输出缓冲还留有数据,仍然可以传递至目标主机。

为何需要半关闭

半关闭主要作用有两方面。其一,向目标主机发出一个数据传输结束的信号(EOF),使对端感知到本端数据已经发送完成而可以进行其他动作了(这个作用close函数也可以完成);其二,如果本端在数据发送完成后还需要接收对端的反馈信息,则需要调用shutdown函数仅进行输出流的关闭(半关闭,这一点close函数无法实现)。

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

下面以基于半关闭的文件传输程序做结,来展示shutdown半关闭的作用。

文件传输数据流图

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <arpa/inet.h>
 6 #include <sys/socket.h>
 7
 8 #define BUF_SIZE 30
 9 void error_handling(char *message);
10
11 int main(int argc, char *argv[])
12 {
13     int serv_sd, clnt_sd;
14     FILE * fp;
15     char buf[BUF_SIZE];
16     int read_cnt;
17
18     struct sockaddr_in serv_adr, clnt_adr;
19     socklen_t clnt_adr_sz;
20
21     if(argc!=2) {
22         printf("Usage: %s <port>\n", argv[0]);
23         exit(1);
24     }
25
26     fp=fopen("file_server.c", "rb");
27     serv_sd=socket(PF_INET, SOCK_STREAM, 0);
28
29     memset(&serv_adr, 0, sizeof(serv_adr));
30     serv_adr.sin_family=AF_INET;
31     serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
32     serv_adr.sin_port=htons(atoi(argv[1]));
33
34     bind(serv_sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
35     listen(serv_sd, 5);
36
37     clnt_adr_sz=sizeof(clnt_adr);
38     clnt_sd=accept(serv_sd, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
39
40     while(1)
41     {
42         read_cnt=fread((void*)buf, 1, BUF_SIZE, fp);
43         if(read_cnt<BUF_SIZE)
44         {
45             write(clnt_sd, buf, read_cnt);
46             break;
47         }
48         write(clnt_sd, buf, BUF_SIZE);
49     }
50
51     shutdown(clnt_sd, SHUT_WR);
52     read(clnt_sd, buf, BUF_SIZE);
53     printf("Message from client: %s \n", buf);
54
55     fclose(fp);
56     close(clnt_sd); close(serv_sd);
57     return 0;
58 }
59
60 void error_handling(char *message)
61 {
62     fputs(message, stderr);
63     fputc(‘\n‘, stderr);
64     exit(1);
65 }

flie_server

file_client

运行结果

原文地址:https://www.cnblogs.com/Glory-D/p/12112875.html

时间: 2024-07-30 18:34:32

【TCP/IP网络编程】:07优雅地断开套接字连接的相关文章

TCP/IP网络编程读书笔记-简单的套接字编程(1)

在linux和windows下都是通过套接字编程进行网络编程.不同的系统上通信有部分差别,现在刚开始学习,给自己学习的时候一个总结. 一,socket函数的套接字步骤 第一,linux网络编程中接受连接请求(服务器端)套接字的四个步骤: 1)调用socket函数创建套接字 2)调用bind函数分配IP地址和端口号 3)调用listen函数转为可接收请求状态 4)调用accept函数受理连接请求 第二,linux网络编程中请求连接(客户端)套接字的两个步骤: 1)调用socket函数创建套接字 2

TCP/IP网络编程 学习笔记_8 --优雅地断开套接字连接

基于TCP的半关闭 TCP中的断开连接过程比建立连接过程更重要,因为建立连接过程一般不会出现什么大的变数,但断开过程就有可能发生预想不到的情况,因此要准确的掌控. 单方面断开连接带来的问题 Linux的close函数和Windows的closesocket函数是完全断开连接.完全断开是指无法传输数据也不能接收数据.因此,一方这样直接断开连接就显得不太优雅了.如:主机A发送完最后的数据后,调用close函数单方断开了连接,那么最终,由主机B传输的,主机A必须接收的确认数据也销毁了(四次握手). 为

《TCP/IP网络编程》

<TCP/IP网络编程> 基本信息 作者: (韩)尹圣雨 译者: 金国哲 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115358851 上架时间:2014-6-19 出版日期:2014 年6月 开本:16开 页码:1 版次:1-1 所属分类:计算机 > 计算机网络 > 网络协议 > TCP/IP 更多关于>>><TCP/IP网络编程> 编辑推荐 为初学者准备的网络编程 本书涵盖操作系统.系统编程.TCP/IP协议等多种

浅谈TCP/IP网络编程中socket的行为

我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: . TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) . Socket I/O系统调用(重点如read/write),这是TCP/IP协议在应用层表现出来的行为. . 编写Performant, Scalable的服务器程序.包括多线程.IO Multiplexing.非阻塞.异步等各种技术. 关于TCP/IP协议,建议参考Richard Stevens的<TCP/IP Illust

TCP/IP网络编程系列之四(初级)

TCP/IP网络编程系列之四-基于TCP的服务端/客户端 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流的套接字.在了解TCP之前,先了解一下TCP所属的TCP/IP协议栈. 如图所示,TCP/IP协议栈共分为4层,可以理解成数据收发分成了4个层次化过程. 链路层 它是物理链接领域标准化结果,也是最基本的领域,专门定义LAN.WAN.MAN等网络标准.若两台计算机通过网络进行数据交换,链路层就负责整个物

TCP/IP网络编程之基于TCP的服务端/客户端(二)

回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服务端的I/O代码 echo_server.c --while ((str_len = read(clnt_sock, messag, 1024)) != 0) write(clnt_sock, messag, str_len);-- 接着,我们回顾客户端的代码 echo_client.c -- wr

TCP/IP 网络编程 (抄书笔记 1) -- TCP

TCP/IP 网络编程 (抄书笔记 1) – TCP TCP/IP 网络编程 (抄书笔记 1) – TCP Table of Contents server client 更好的 client 端实现 来源: <TCP/IP 网络编程> 抄书: 通信的双方都各自 拥有 输入缓存和输出缓存 socket 的 write 函数并不是立即传输数据, 而是写到输出缓存区, 到达另一端的输入缓存区 socket 的 read 函数调用的瞬间, 就从输入缓存区中读取数据 TCP 协议中的滑动窗口会保证 数

TCP/IP 网络编程 (抄书笔记 2) -- UDP

TCP/IP 网络编程 (抄书笔记 2) – UDP TCP/IP 网络编程 (抄书笔记 2) – UDP Table of Contents server client connect 来源: <TCP/IP 网络编程> 抄书: TCP 协议若要向 10 个客户端提供服务, 除了需要 listen 套接字外, 还需要 10 个服务器端套接字 (accept), 但是在 UDP 中, 不管是服务器端还是客户端都只需要 1 个套接字 udp 的 client 不需要 bind, 调用 sendt

TCP/IP 网络编程 (抄书笔记 4) -- 管道: 进程间通信

TCP/IP 网络编程 (抄书笔记 4) – 管道: 进程间通信 TCP/IP 网络编程 (抄书笔记 4) – 管道: 进程间通信 int fds[2]; pipe(fds); write(fds[1], buf, strlen(buf)); read(fds[0], buf, BUF_SIZE); 如果两个进程的通信只是 单纯的一方写, 然后另一方读 的情况, 那么 我们的管道操作没有问题, 但是: char str1[] = "str1"; char str2[] = "