如何优雅地关闭一个socket

最近在windows编程时需要考虑到“如何优雅地关闭一个socket”,查阅了一些资料,现将查到的相关资料做个汇编,希望能对后来者有所帮助(比较懒,所以英文资料没有翻译:-))

1. 关闭Socket时究竟做了什么

    关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况。前者是指有本地主机主动发起的关闭;而后者则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接。
    其状态图如下图所示:

      起初每个socket都是CLOSED状态,当客户端初使化一个连接,他发送一个SYN包到服务器,客户端进入SYN_SENT状态。
服务器接收到SYN包,反馈一个SYN-ACK包,客户端接收后返馈一个ACK包客户端变成ESTABLISHED状态,如果长时间没收到SYN-ACK包,客户端超时进入CLOSED状态。
  当服务器绑定并监听某一端口时,socket的状态是LISTEN,当客户企图建立连接时,服务器收到一个SYN包,并反馈SYN-ACK包。服务器状态变成SYN_RCVD,当客户端发送一个ACK包时,服务器socket变成ESTABLISHED状态。

  当一个程序在ESTABLISHED状态时有两种图径关闭它, 第一是主动关闭,第二是被动关闭。如果你要主动关闭的话,发送一个FIN包。当你的程序closesocket或者shutdown(标记),你的程序发送一个FIN包到peer,你的socket变成FIN_WAIT_1状态。peer反馈一个ACK包,你的socket进入FIN_WAIT_2状态。如果peer也在关闭连接,那么它将发送一个FIN包到你的电脑,你反馈一个ACK包,并转成TIME_WAIT状态。
  TIME_WAIT状态又号2MSL等待状态。MSL意思是最大段生命周期(Maximum Segment Lifetime)表明一个包存在于网络上到被丢弃之间的时间。每个IP包有一个TTL(time_to_live),当它减到0时则包被丢弃。每个路由器使TTL减一并且传送该包。当一个程序进入TIME_WAIT状态时,他有2个MSL的时间,这个充许TCP重发最后的ACK,万一最后的ACK丢失了,使得FIN被重新传输。在2MSL等待状态完成后,socket进入CLOSED状态。
  被动关闭:当程序收到一个FIN包从peer,并反馈一个ACK包,于是程序的socket转入CLOSE_WAIT状态。因为peer已经关闭了,所以不能发任何消息了。但程序还可以。要关闭连接,程序自已发送给自已FIN,使程序的TCP socket状态变成LAST_ACK状态,当程序从peer收到ACK包时,程序进入CLOSED状态。

2. Winsock2 API中的相关函数

    先当然是查MSDN,看到winsocks2 API中的相关函数有:closesocket,shutdown,WSASendDisconnect. 我大致说一下,具体详细的资料还请自行查MSDN.

    int closesocket( SOCKET s)的作用是关闭指定的socket,并且回收其所有的资源。
    int shutdown( SOCKET s,  int how)则是禁止在指定的socket s上禁止进行由how指定的操作,但并不对资源进行回收,shutdown之后而closesocket之前s还不能再次connect或者WSAConnect.
    int WSASendDisconnect( SOCKET s,  LPWSABUF lpOutboundDisconnectData)则和shutdown基本类似,稍有不同的就是WSASendDisconnect函数多了一个lpOutboundDisconnectData参数,可以允许发送“断开数据”(disconnect data).但MSDN上写了“The native implementation of TCP/IP on Windows does not support disconnect data.”,所以一般我们就用shutdown函数就行了。

3. Socket的优雅关闭

在MSDN中对shutdown函数中的Remarks部分有下面一段话,指出了如何进行一次优雅的slcket关闭:

To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. For example, to initiate a graceful disconnect:

 

  1. Call WSAAsyncSelect to register for FD_CLOSE notification.
  2. Call shutdown with how=SD_SEND.
  3. When FD_CLOSE received, call recv until zero returned, or SOCKET_ERROR.
  4. Call closesocket.

closesocket的行为也是随setsockopt()中参数的不同而有不同的表现,这里影响它的行为的主要就是那个linger结构。

SO_DONTLINGER 若为真,则SO_LINGER选项被禁止。 
SO_LINGER 延迟关闭连接 struct linger  
上面这两个选项影响close行为 
选项 间隔 关闭方式 等待关闭与否 
SO_DONTLINGER 不关心 优雅 否 
SO_LINGER 零 强制 否 
SO_LINGER 非零 优雅 是 
若设置了SO_LINGER(亦即linger结构中的l_onoff域设为非零),并设置了零超时间隔,则closesocket()不被阻塞立即执行,不论是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭,因为套接口的虚电路立即被复位,且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。 
若设置了SO_LINGER并确定了非零的超时间隔,则closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“优雅的”关闭。请注意如果套接口置为非阻塞且SO_LINGER设为非零超时,则closesocket()调用将以WSAEWOULDBLOCK错误返回。 
若在一个流类套接口上设置了SO_DONTLINGER(也就是说将linger结构的l_onoff域设为零),则closesocket()调用立即返回。但是,如果可能,排队的数据将在套接口关闭前发送。请注意,在这种情况下WINDOWS套接口实现将在一段不确定的时间内保留套接口以及其他资源,这对于想用所以套接口的应用程序来说有一定影响。

    所以一般来说,不应该把linger设置为SO_LINGER 并且设置timeout为0,这样的话,当本地主机调用closesocket时将会造成一个“强制”或“失效”的非优雅关闭。可以根据实际情况设置为另外两种情况。

时间: 2024-08-30 06:02:21

如何优雅地关闭一个socket的相关文章

单独写的一个socket服务应该怎么部署?部署到哪个平台?

问题描述 RT:想问下:我通过java代码实现了一个socket服务端(可以启动socket服务,接收客户端请求,给客户端回复响应),在eclipse中调试启动服务,调测是OK的:但是这个服务怎么才能集成或部署到一个类似于jetty或tomcat容器中呢?(而不是每次通过eclipse启动),知道的能告诉我一个具体方法吗?感激! 解决方案 应该是c/s程序吧,直接在服务器上调用java命令运行main类就行,就像我们开始学习java时需要手动编译运行一样,先javac编译,再java运行你把在你

从使用Python开发一个Socket示例说到开发者的思维和习惯问题

今天主要说的是一个开发者的思维和习惯问题. 思维包括编程的思维和解决一个具体问题的分析思维,分析思路,分析方法,甚至是分析工具. 无论是好习惯还是不好的习惯,都是在者一天一天的思维中形成的.那些不好的习惯,久了确实不好改.所以说,如果今天你认识到了,那么就从今天开始改,早改早受益,晚改痛苦一生. 先说一下今天的引子,那就是使用Python开发一个简单的Socket应用,就是一个client/server通信的小例子. 假设现在需要我们使用python开发一个socket的聊天应用,可能会遇到下面

socket通信-用vc写一个socket程序 实现两个客户端通过一个服务器的对话

问题描述 用vc写一个socket程序 实现两个客户端通过一个服务器的对话 初学socket 求详细教程,最好有C++源码 感激不尽 解决方案 孙鑫的VC视频教程中就有这样的例子程序,可以参考一下.

socket数据传输-一个socket问题,文件传输,但不保存问题。大神们在哪儿呢

问题描述 一个socket问题,文件传输,但不保存问题.大神们在哪儿呢 用socket 传文件, 比如说传视频,接收方 不下载,只将接受到的数据存在内存中,并在内存中使用 播放它.大神们,帮帮忙嘞~~~ 解决方案 你保存不保存,都无所谓,本身要播放文件,也是要把文件加载到内存.. 解决方案二: 是传到服务器吗?感觉这个视频还是要转到服务器中才行!服务器才是中转站 解决方案三: 你要了解一点,如果接收到数据直接利用这个流数据,你得控制好你服务器的内存使用量 解决方案四: 那就每次固定从缓冲区取内容

socket-new 一个Socket 出来,是不是Socket的源代码已经包括基础输入输出流了

问题描述 new 一个Socket 出来,是不是Socket的源代码已经包括基础输入输出流了 DataOutputStream dos = new DataOutputStream(s.getOutputStream());(其中s是一个Socket对象).我想问一下s.getOutputStream()直接拿出来一个socket的输出流,这个我们从来就没有定义过一个输出流,为什么就可以拿到,是说new出来一个Socket 对象出来,是不是Socket的源代码已经包括基础输入输出流了.如果说的不

两个线程都通过一个socket进行send是否需要互斥锁?

问题描述 两个线程都通过一个socket进行send是否需要互斥锁? 两个线程都通过一个socket进行send是否需要互斥锁?同理,消息队列是不是也要尼? 解决方案 如果没有出现对同个资源write操作的情况下,是完全可以不适用锁的 解决方案二: 同一个资源?不是啊,socket数全局的,两个线程都进行send数据,也就是写缓冲,如果同时发生的话,会不会出现问题啊! 解决方案三: 需要加锁.不然数据可能会写乱 解决方案四: 最好锁一下 不然如果线程执行时间戳交叉 比如发12345 和 6788

java 网络通信-我做了一个socket通信程序,服务器是server,用户是client

问题描述 我做了一个socket通信程序,服务器是server,用户是client 但是我同学运行用户程序,我运行服务器程序,却连接不上,为什么呢?具体代码如下 服务器程序: import java.awt.Color; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import j

我做的一个SOCKET试验,奇怪了!

问题描述 我做了一个socket客户端,使用connect指定ip和端口可以连接上,那在服务端ip改变了,就不行,怎么样通过客户端动态获取服务端的ip地址,然后返回给客户端,然后连接?c#做的!请指教

c++-请教一个SOCKET套接字的问题

问题描述 请教一个SOCKET套接字的问题 int socket(int domain, int type, int protocol); 请问下上面这句话 我看到有的人写的时候用的 不是INT类型 而是 SOCKET SS=socket....... 请问下INT和 SOCKET能一样么?我试过 结果是一样的,但是不太明白所以来请教一下各位. 解决方案 SOCKET应该就是一个socket句柄类型,而windows下各种句柄都是dword型的数所以用int也一样,你可以转到SOCKET的定义看