SO_REUSEADDR
有过网络编程经验的人肯定都会遇到过关于 SO_REUSEADDR socket选项的问题。
问题背景:在socket编程中当我们调用closesocket(或者close in linux)时会关闭两端的连接,在TCP连接中主动关闭的一方会进入TIME_WAIT等待2MSL的时间之后才会真正关闭,这样做是为了确保outdate的数据报被丢弃同时防止假连接的到来。那么这段时间该套接字所占用的端口并不会被释放。
但是对于服务器这种存在大量连接,如果服务器需要重启,在未设置SO_REUSEADDRSD时,有时候会出现bind失败的情况,原因就是因为服务器关闭的时候改端口会处在TIME_WAIT状态2MSL长的时间段,短则几十秒长则几分钟,这个对于服务器来说应该是比较严重的问题。在Linux下可以通过在创建socket的时候bind之前对套接字设置SO_REUSEADDR选项,这个选项在Linux系统上的作用是允许立即重用端口,那么就会在使处在TIME_WAIT状态下的端口被立即重新使用。windows上也有这个socket选项,然而在windows上的SO_REUSEADDR的含义确实大不一样,windows设置这个选项的套接字可以共享同一端口,但是如果两个监听套接字共享同一端口的话,如果有一个新的连接接入,那么带来的行为将是未定义的。这种做法会带来相应的安全问题,我们可以通过监听同一端口来截获某一服务的连接,这样很容易窃取信息,微软为了解决这个问题并没有直接修改这个设置的作用,而是新增添了一个SO_EXLUSIVEADDRUSE选项配合使用,这个选项是指禁止两个相同地址socket绑定到同一个socket上,即使设置SO_REUSEADDR选项。(MSDN解释)
SO_LINGER
SO_LINGER选项控制着如何关闭TCP连接,在默认情况下,当调用close关闭socke的使用,close会立即返回,但是,如果send buffer中还有数据,系统会试着先把send buffer中的数据发送出去,SO_LINGER选项则是用来修改这种默认操作的。
struct linger {
int l_onoff //0=off, nonzero=on(开关)
int l_linger //linger time(延迟时间)
}
其取值和处理如下:
1、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据;
2、设置 l_onoff为非0,l_linger为0,当调用close的时候,TCP连接会立即断开.send buffer中未被发送的数据将被丢弃,并向对方发送一个RST信息.值得注意的是,由于这种方式,不是以4次握手方式结束TCP链接,所以,TCP连接将不会进入TIME_WAIT状态,这样会导致新建立的可能和就连接的数据造成混乱。这种关闭方式称为“强制”或“失效”关闭。
3、设置 l_onoff 为非0,l_linger为非0,在这种情况下,回事的close返回得到延迟。调用close去关闭socket的时候,内核将会延迟。也就是说,如果send buffer中还有数据尚未发送,该进程将会被休眠直到一下任何一种情况发生:
a. send buffer中的所有数据都被发送并且得到对方TCP的应答消息(这种应答并不是意味着对方应用程序已经接收到数据,在后面shutdown将会具体讲道)
b.延迟时间消耗完。在延迟时间被消耗完之后,send buffer中的所有数据都将会被丢弃。
这种关闭称为“优雅的”关闭。