Socket编程中的强制关闭与优雅关闭及相关socket选项
以下描述主要是针对windows平台下的TCP socket而言。
首先需要区分一下关闭socket和关闭TCP连接的区别,关闭TCP连接是指TCP协议层的东西,就是两个TCP端之间交换了一些协议包(FIN,RST等),具体的交换过程可以看TCP协议,这里不详细描述了。而关闭socket是指关闭用户应用程序中的socket句柄,释放相关资源。但是当用户关闭socket句柄时会隐含的触发TCP连接的关闭过程。
TCP连接的关闭过程有两种,一种是优雅关闭(graceful close),一种是强制关闭(hard close或abortive close)。所谓优雅关闭是指,如果发送缓存中还有数据未发出则其发出去,并且收到所有数据的ACK之后,发送FIN包,开始关闭过程。而强制关闭是指如果缓存中还有数据,则这些数据都将被丢弃,然后发送RST包,直接重置TCP连接。
下面说一下shutdown及closesocket函数。
shutdown函数的原型是:
int shutdown(
SOCKET s,
int how
);
该函数用于关闭TCP连接,单并不关闭socket句柄。其第二个参数可以取三个值:SD_RECEIVE,SD_SEND,SD_BOTH。
SD_RECEIVE表明关闭接收通道,在该socket上不能再接收数据,如果当前接收缓存中仍有未取出数据或者以后再有数据到达,则TCP会向发送端发送RST包,将连接重置。
SD_SEND表明关闭发送通道,TCP会将发送缓存中的数据都发送完毕并收到所有数据的ACK后向对端发送FIN包,表明本端没有更多数据发送。这个是一个优雅关闭过程。
SD_BOTH则表示同时关闭接收通道和发送通道。
closesocket函数的原型是:
int closesocket(
SOCKET s
);
该函数用于关闭socket句柄,并释放相关资源。前面说过,关闭socket句柄时会隐含触发TCP连接的关闭过程,那么closesocket触发的是一个优雅关闭过程还是强制关闭过程呢?这个与一个socket选项有关:SO_LINGER 选项,该选项的设置值决定了closesocket的行为。该选项的参数值是linger结构,其定义是:
typedef struct linger {
u_short l_onoff;
u_short l_linger;
} linger;
当l_onoff值设置为0时,closesocket会立即返回,并关闭用户socket句柄。如果此时缓冲区中有未发送数据,则系统会在后台将这些数据发送完毕后关闭TCP连接,是一个优雅关闭过程,但是这里有一个副作用就是socket的底层资源会被保留直到TCP连接关闭,这个时间用户应用程序是无法控制的。
当l_onoff值设置为非0值,而l_linger也设置为0,那么closesocket也会立即返回并关闭用户socket句柄,但是如果此时缓冲区中有未发送数据,TCP会发送RST包重置连接,所有未发数据都将丢失,这是一个强制关闭过程。
当l_onoff值设置为非0值,而l_linger也设置为非0值时,同时如果socket是阻塞式的,此时如果缓冲区中有未发送数据,如果TCP在l_linger表明的时间内将所有数据发出,则发完后关闭TCP连接,这时是优雅关闭过程;如果如果TCP在l_linger表明的时间内没有将所有数据发出,则会丢弃所有未发数据然后TCP发送RST包重置连接,此时就是一个强制关闭过程了。
另外还有一个socket选项SO_DONTLINGER,它的参数值是一个bool类型的,如果设置为true,则等价于SO_LINGER中将l_onoff设置为0。
注意SO_LINGER和SO_DONTLINGER选项只影响closesocket的行为,而与shutdown函数无关,shutdown总是会立即返回的。
所以为了保证建议的最好的关闭方式是这样的:
发送完了所有数据后:
(1)调用shutdown(s, SD_SEND),如果本端同时也接收数据时则执行第二步,否则跳到第4步。
(2)继续接收数据,
(3)收到FD_CLOSE事件后,调用recv函数直到recv返回0或-1(保证收到所有数据),
(4)调用closesocket,关闭socket句柄。
在实际编程中,我们经常也不调用shutdown,而是直接调用closesocket,利用closesocket隐含触发TCP连接关闭过程的特性。此时的过程就是:
当发送完所有数据后:
(1)如果本端同时也接受数据则则执行第二步,否则跳到第4步。
(2)继续接收数据,
(3)收到FD_CLOSE事件后,调用recv函数直到recv返回0或-1(保证收到所有数据),
(4)调用closesocket,关闭socket句柄。
但是此时为了保证数据不丢失,则需要设置SO_DONTLINGER选项,不过windows平台下这个也是默认设置。
经过实验发现,发送端应用程序即便是异常退出或被kill掉进程,操作系统也不会丢弃发送缓冲区中的未发送数据,而是会在后台将这些数据发送出去。但是这是在socket的发送缓存不为0的前提下,当socket的发送缓存设置为0(通过SO_SNDBUF选项)时比较特殊,此时不论socket是否是阻塞的,send函数都会被阻塞直到传入的用户缓存中的数据都被发送出去并被确认,因为此时在驱动层没有分配缓存存放用户数据,而是直接使用的应用层的用户缓存,所以必须阻塞直到数据都发出,否则可能会造成系统崩溃。
另外,如果是接收端的应用程序异常退出或被kill掉进程,并且接收缓存中还有数据没有取出的话,那么接收端的TCP会向发送端发送RST包,重置连接,因为后续数据已经无法被提交应用层了。
最后这里说一个感觉是windows的bug,就是做这样的一个测试:
在一端线listen一个socket,然后在另一端connect,connect成功后,listen端会检测到网络事件触发,在listen端accept之前,将connect端kill掉,然后继续运行listen端,listen端任然会accept成功,且在accept出来的socket发送数据也能成功。发送完之后在等网络事件,此时又会等待成功,但是调用WSAEnumNetworkEvents得出的事件标识却是0。之后再也不会等到网络事件。
Socket编程中的强制关闭与优雅关闭及相关socket选项的更多相关文章
- socket编程中客户端常用函数
1 常用函数 1.1 connect() int connect(int sockfd, const struct sockaddr *servaddr, socklen_taddrlen); 客 ...
- socket编程中write、read和send、recv之间的区别~转载
socket编程中write.read和send.recv之间的区别 http://blog.csdn.net/petershina/article/details/7946615 一旦,我们建立 ...
- [转]Socket编程中,阻塞与非阻塞的区别
阻塞:一般的I/O操作可以在新建的流中运用.在服务器回应前它等待客户端发送一个空白的行.当会话结束时,服务器关闭流和客户端socket.如果在队列中没有请示将会出现什么情况呢?那个方法将会等待一个的到 ...
- PHP socket 编程中的超时设置
PHP socket 编程中的超时设置.网上找了半天也没找到.贴出来分享之:设置$socket 发送超时1秒,接收超时3秒: $socket = socket_create(AF_INET,SOCK_ ...
- Socket编程中,阻塞与非阻塞的区别
阻塞:一般的I/O操作可以在新建的流中运用.在服务器回应前它等待客户端发送一个空白的行.当会话结束时,服务器关闭流和客户端socket.如果在队列中没有请示将会出现什么情况呢?那个方法将会等待一个的到 ...
- Socket编程中 setsockopt的作用
功能描述: 获取或者设置与某个套接字关联的选 项.选项可能存在于多层协议中,它们总会出现在最上面的套接字层.当操作套接字选项时,选项位于的层和选项的名称必须给出.为了操作套接字层的选项,应该 将层的值 ...
- Socket 编程中,TCP 流的结束标志与粘包问题
因为 TCP 本身是无边界的协议,因此它并没有结束标志,也无法分包. socket和文件不一样,从文件中读,读到末尾就到达流的结尾了,所以会返回-1或null,循环结束,但是socket是连接两个主机 ...
- Linux socket 编程中存在的五个隐患
前言: Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是 开发新手可能会经历一些常见的问题.本文识别一些最常见的隐患并向您显示如何避免它 ...
- java socket编程中backlog的含义(zz)
使用Java.NET.ServerSocket能够方便的创建一个服务端套接字,这个类的构造函数有一个参数backlog.下面这段代码,在本机的8888端口上建立了一个套接字,backlog设置为5. ...
随机推荐
- PowerDesigner怎么调出工具箱?
PowerDesigner怎么调出工具箱? 如图操作:
- iOS ReactiveCocoa 最全常用API整理(可做为手册查询)
本文适合有一定RAC基础的童鞋做不时的查询,所以本文不做详细解释. 一.常见类 1.RACSiganl 信号类. RACEmptySignal :空信号,用来实现 RACSignal 的 +empty ...
- LinkedList源码学习
链表数据结构 当前节点会保存上一个.下一个节点. 参见 LinkedList的Node类 实现: 1. 内部链表的方式. 1.1 添加元素.追加的方式,创建一个新的节点[Node],用最后一个节点关联 ...
- 常见bug分析
变量类型不匹配,形参和实参类型不匹配,隐式类型转换,变量类型赋值不匹配, 工具不熟悉,导致逻辑错误,查看代码,测试驱动开发,完整的测试用例,覆盖所有分支, 变量超出范围,对于大的数据要特别注意, 工具 ...
- Raspberry Pi - 调整你的SD卡分割区的大小
在使用Win32DiskImager为一张空白的SD卡刷入新的Rasbian系统后,卡上的可用剩余空间并不大, 本人有一张8G的SD卡,但是刷入4.1的Rasbian后,用df -h查看,根目录下的空 ...
- caioj 1067动态规划入门(一维一边推5: 乘积最大(高精度版))
因为这里涉及到乘号的个数,那么我们可以用f[i][j]表示前i个位乘号为j个时的最大乘积 那么相比上一题就是多了一层枚举多少个乘号的循环,可以得出 f[i][r] = max(f[j - 1][r - ...
- 【转】 我的java web登录RSA加密
[转] 我的java web登录RSA加密 之前一直没关注过web应用登录密码加密的问题,这两天用appscan扫描应用,最严重的问题就是这个了,提示我明文发送密码.这个的确很不安全,以前也大概想过, ...
- C#版清晰易懂TCP通信原理解析(附demo)
[转] C#版清晰易懂TCP通信原理解析(附demo) (点击上方蓝字,可快速关注我们) 来源:周见智 cnblogs.com/xiaozhi_5638/p/4244797.html 对.NET中网络 ...
- Android输入法扩展之远程输入法
近年来,互联网电视開始火热,乐视TV,小米TV,近期爱奇艺也在大肆的招人做爱奇艺电视.当然还有更被关注的苹果电视.事实上,这个趋势非常正常,也非常合理,传统单纯的接收电视节目的电视已经太传统了.是该被 ...
- uvalive 6669 hidden tree(好壮压dp)
题目见option=com_onlinejudge&Itemid=8&page=show_problem&problem=4681">here 题意:给一个序列 ...