网络编程-关闭连接(1)-C/C++相关系统调用

背景

在linux网络编程中,经常需要编写关闭socket的代码,比如心跳检测失败需要关闭重连;网络报异常需要关闭重连。但究竟关闭操作做了什么,却不太清楚。目前项目使用Netty框架来实现的网络编程,查看netty源码可以得知,netty最终是调用了java Nio的close接口做的关闭操作,那么想研究清楚这个close操作究竟做了什么,可以从两个方向入手,这两个方向也是从下至上的。

  1. 搞清楚如果使用C/C++编程,应该调用哪个系统调用函数?函数内部做了什么,涉及到什么TCP/IP的协议参数
  2. 搞清楚java nio在调用close方法时,究竟使用了哪个系统调用?

本文首先解决的是第一步,搞清楚系统调用相关的知识。

相关系统调用

Linux平台下,提供了两个系统调用函数供开发人员使用:

  • close函数
  • shutdown函数
close函数
int close(int sockfd);

这个函数的具体行为由一个TCP/IP套接字选项控制:SO_LINGER

SO_LINGER的在头文件<sys/socket.h>中定义如下:

struct linger{
int l_onoff;
int l_linger;
}

根据这个选项参数的不同,close的逻辑如下:
1)l_onoff=0,l_linger=1或者0时(这个是默认选项)

  • close会立即返回,0为成功-1为失败
  • 调用进程在该套接字上不能再发送或接收请求
  • 接收缓冲区中的数据将会被抛弃
  • 如果发送缓冲区中还有数据,会由操作系统在后台继续发送
  • 如果套接字的引用计数变为0,则发送FIN表示关闭
    • 引用计数:进程和子进程可以共享一个套接字,每当一个进程做了close操作,引用计数就会减1
  • 最后释放套接字的系统资源

2)l_onoff=1,l_linger=0时

  • close会立即返回
  • 调用进程在该套接字上不能再发送或接收请求
  • 发送和接收缓冲区中的数据都会被抛弃
  • 如果套接字的引用计数变为0,则发送RST到对端,并且状态直接变成CLOSED
    • 注:RST没有超时重发机制,如果对端没有收到RST,继续发送,那么又会促使本端发送RST,直到对方收到
  • 最后释放套接字的系统资源

3)l_onoff=1,l_linger>0时

  • 如果是阻塞的socket,close函数不会立即返回;非阻塞的会立即返回
  • 调用进程在该套接字上不能再发送或接收请求
  • 接收缓冲区中的数据将会被抛弃
  • 如果发送缓冲区中还有数据,会由操作系统在后台继续发送
  • 如果套接字的引用计数变为0,则发送FIN表示关闭,在套接字状态编程CLOSED前,如果超时时间到,返回EWOULDBLOCK错误
  • 最后释放套接字的系统资源

总结一下:
默认情况和第三种情况对比,默认情况相当于一个异步请求,并且无法得知操作结果;第三种情况,可以在超时时间范围内做close处理,发送未发送完毕的数据。第二种情况属于粗暴的关闭socket,在2MSL时间范围内如果新建立了一个“化身”(ip port dip dport都一样的套接字),可能会被前一个套接字相关的数据所影响。
注:对2MSL不理解的小伙伴,可以看下这篇博客,讲解的很清晰:
为什么tcp的TIME_WAIT状态要维持2MSL

shutdown函数

有一种业务场景,客户端发送数据到服务端,发送完毕后,客户端就可以关闭客户端写方向的连接了,等待服务端处理。
业务需求是保证客户端发送的数据都会被服务端应用程序接收并处理。如果使用close函数关闭连接,最多只能保证,全部数据都已经发送到了对端的接收缓冲区中(使用SO_LINGER相关配置项),但是无法确保对端的应用程序一定读取到数据(close以后,本端socket就无法读了)。

在这种业务场景下,如果需要确保服务端一定读取到了数据,可以考虑使用shutdown函数。

int shutdown(int sockfd,int howto);

执行shutdown函数,成功返回0,出错返回-1。

howto是这个函数的设置选项:

  • SHUT_RD:关闭套接字的读方向。读缓冲区中的数据都会被抛弃,如果有新数据到达,都将被ACK,并且被悄悄丢弃。
  • SHUT_WR:关闭套接字的写方向。在套接字发送缓冲区的数据都会被继续发送过去,然后发送正常的FIN开始挥手流程。
  • SHUT_RDWR:读和写两个方向都关闭

只使用shutdown函数,也无法保证满足我们上面提到的业务需求,即保证服务端应用程序是否正确读取数据。目前有两种解决方式可以实现上述业务需求:

  • shutdown后,使用read函数,等待对端的FIN发送过来,此时read函数返回0
  • 应用级别确认:完全发送数据后,再读取一个字节的数据(这个数据是客户端和服务端的自定义协议,比如:服务端完全接受数据后,可以继续发送一字节的数据,代表读取成功)

第一种方式流程图如下(摘自《Unix网络编程》):

Image.png

第二种方式流程图如下(摘自《Unix网络编程》):

Image1 [2].png
close函数和shutdow函数的区别
  1. close函数会计算引用计数,当计数为0时才触发挥手操作;shutdown函数则不需要判断引用计数来触发挥手操作
  2. close函数可以终止两个方向的传输,shutdown可以控制只终止一个方向的
  3. close函数会关闭资源,shutdown函数不会
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355