下面建立的套接字都是tcp套接字
1.进程创建监听套接字socket1,邦定一个指定端口,并接受了若干连接。那么进程创建另外一个套接口socket2,并试图邦定同一个端口时候,bind错误返回“Address already in use”(即使使用了SO_REUSEADDR).
2.进程创建监听套接字,邦定一个指定端口,并接受了若干连接,为每个连接创建子进程为连接服务。杀死监听套接字所在进程,然后重新启动。重新启动的进程调用bind重新建立监听套接字。这次邦定只有在bind前指定了SO_REUSEADDR时才能成功。(因为直接杀进程,没有显式关闭套接字来释放端口,会等待一段时间后才可以重新use这个关口,解决办法就是用SO_REUSEADDR)。
3.进程创建套接字socket1,邦定一个指定端口,使用这个套接字去connect另外一个监听套接字。连接建立。然后进程建立一个监听套接字socket2,邦定同一个端口。这次邦定只有在下面两个条件都满足的情况下才成功返回:为socket2邦定前指定SO_REUSEADDR,且为socket1邦定前也指定了SO_REUSEADDR。
4.进程创建套接字socket1,邦定一个指定端口,去连接某个监听套接口。杀死进程,保证socket1一端执行主动关闭。那么重启进程后,除非上一个连接中socket1退出了TIME_WAIT状态,否则重启的进程在调用bind时候错误返回。
同一个机器上一个端口PORT1,TCP socket1 绑定PORT1,然后TCP socket2绑定PORT1会失败;
同一个机器上一个端口PORT1,TCP socket1 绑定PORT1,然后UDP socket2绑定PORT1会成功;
同一个机器上一个端口PORT1,UDP socket1 绑定PORT1,然后UDP socket2绑定PORT1会失败;
同一个机器上一个端口PORT1,TCP socket1 绑定PORT1,然后TCP socket2绑定PORT1会成功的条件是:
两个套接字绑定前都调用:
int opt = 1;
// sockfd为需要端口复用的套接字
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const voidvoid *)&opt, sizeof(opt));
但是假如socket1不仅bind了,还listen,并且accept成功了,这个时候socket2再次绑定到这个端口就会失败!!但是假如socket2是UDP的socket,那么socket2的bind还是会成功的!!!
端口复用允许在一个应用程序可以把 n 个套接字绑在一个端口上而不出错。同时,这 n 个套接字发送信息都正常,没有问题。但是,这些套接字并不是所有都能读取信息,只有最后一个套接字会正常接收数据。
端口复用最常用的用途应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败,提示ADDR已经在使用中——那只好等等再重试了,麻烦!
复用真正是什么意义呢?这个我们可以看看TCP/IP里面TCP建立和断开链接的方法。
我们知道,在TCP断开链接的时候我们需要四次握手来断开,而且当两端都关闭了read/write通道以后我们还是要等待一个TIME_WAIT时间。
这就是SO_REUSEADDR的作用所在.
其实这个选项就是告诉OS如果一个端口处于TIME_WAIT状态, 那么我们就不用等待直接进入使用模式, 不需要继续等待这个时间结束.
那这样我们肯定要问,那为什么我们需要有这个TIME_WAIT时间啊?
看看TCP/IP协议组我们就知道,这样做是为了让在网络中残余的TCP包消失, 也就是说, 如果我们没有等到这个时间就让OS把这个端口释放给其他的进程使用,别的进程很有可能就会收到上一个会话的残余TCP包,这样就会出现一系列的不可预知的错误.
一、保证TCP协议的全双工连接能够可靠关闭
二、保证这次连接的重复数据段从网络中消失
那么什么时候我们可以用这个选项以加快我们进程的速度减小等待时间呢?
这里有一些例子:
SO_REUSEADDR可以用在以下四种情况下。
(摘自《Unix网络编程》卷一,即UNPv1)
1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启
动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但
每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可
以测试这种情况。
3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个soc
ket绑定的ip地址不同。这和2很相似,区别请看UNPv1。
4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的
多播,不用于TCP。
也就是说,不是所有的情况我们都可以使用这个选项的,请参阅这篇淘宝的案例:
http://rdc.taobao.com/blog/cs/?p=1195
1、一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。
SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的socket,才可以重复绑定使用。server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。TCP,先调用close()的一方会进入TIME_WAIT状态
2、SO_REUSEADDR和SO_REUSEPORT
SO_REUSEADDR提供如下四个功能:
SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。
SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。
SO_REUSEPORT选项有如下语义:
此选项允许完全重复捆绑,但仅在想捆绑相同IP地址和端口的套接口都指定了此套接口选项才行。
如果被捆绑的IP地址是一个多播地址,则SO_REUSEADDR和SO_REUSEPORT等效。
使用这两个套接口选项的建议:
在所有TCP服务器中,在调用bind之前设置SO_REUSEADDR套接口选项;
当编写一个同一时刻在同一主机上可运行多次的多播应用程序时,设置SO_REUSEADDR选项,并将本组的多播地址作为本地IP地址捆绑。
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&nOptval , sizeof(int)) < 0) ...
附
Q:编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?
A:这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不可能。
一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使用SO_REUSEADDR 选项。【2】