Linux网络编程中常见的陷阱

时间:2022-06-22 10:24:39

1、谨慎处理Linux信号量和错误号

首先介绍两个很常用网络编程的用法,

(1)很多套接字程序中为了防止进程收到SIGPIPE信号时崩溃往往会在进程中设置忽略信号SIGPIPE​。

(2)但是很多程序为了提高send的成功率对send做了二次封装,即对send的返回值进行判断,如果返回-1则进一步对Linux系统错误号errno进行判断,如果errno==EINTR则继续send,errno==EINTR表示进程在send过程中捕获到系统信号且系统信号处理函数处理已经完成,但是这时send的套接字可能并没有任何问题所以可以继续使用。

以上两个处理方法单独看都没有问题,都能提高程序的鲁棒性,但是如果结合在一起却给自己的程序挖了个陷阱,问题在于当send的对端已经关闭时​send第一次会返回一个RST信号,这个信号一般都没有处理继续send,这时会收到一个SIGPIPE信号,这个信号已经被第一条忽略所以进程不会崩溃,但是send会返回-1且系统会将errno置为错误号EINTR,这时第二条中判断到errno==EINTR会继续send如此则会造成一直在send的死循环。

解决方法可以对send设置超时机制,一段时间send补成功则退出,从而避免死循环。​

2、慎用​SO_REUSEADDR套接字选项

《Unix网络编程》卷一对该选项是这样描述的:

    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。

在linux上即便设置了SO_REUSEADDR属性也是绝对不允许端口的完全重复绑定的,并且报EADDRINUSE的系统错误。

但是windows和linux机制不一样,通过查阅微软官网资料确实如此,原文如下:

Linux网络编程中常见的陷阱

Windows在设置了SO_REUSEADDR属性后是允许完全重复绑定的,那么windows平台有没有办法避免端口冲突呢?答案是肯定的,windows为了解决这个问题引入了SO_EXCLUSIVEADDRUSE属性,设置了SO_EXCLUSIVEADDRUSE属性后后面绑定到同一端口的行为都将失败。下面是微软官网列出的套接字在默认情况、设置SO_REUSEADDR属性和设置SO_EXCLUSIVEADDRUSE属性后然后重复绑定某个端口的各种情况:(wildcard表示INADDR_ANY这类通用地址,Specific表示特定地址)

注:此表格情况适用于Windows Server 2003或者以上的系统且同一用户调用设置。

Linux网络编程中常见的陷阱

由上图可知:要在INADDR_ANY通用地址上使用某个端口并保证不会与其他应用冲突时需要设置SO_EXCLUSIVEADDRUSE属性。但是尤其需要注意的是如果设置了SO_EXCLUSIVEADDRUSE属性那么处于TIME_WAIT状态的套接字的端口将不能被绑定成功。