数码资讯
socket编程实战-bind端口占用问题
选购提示
关注价格、性能、续航、售后和真实使用场景,理性比较后再下单。
https://www.cnblogs.com/rockyching2009/p/11032230.html
一、背景
端对端的通信中存在的一个问题是:如何唯一地标识通信主体。对于socket,解决这个问题的方式是四元组:自身IP,自身端口,对方IP,对方端口。
在socket编程中,作为client,端口号是由操作系统管理和分配的,所以不存在端口占用的情况。如果作为server,在bind某个端口的时候,或多或少遇到过如下错误:
bind: Address already in use
在进程异常终止或重启网络服务的时候,出现上述问题的情况很常见;而作为服务端又需要进程马上步入正轨,这个问题就显得尤其重要。
二、原因及解决方案
/** From APUE */
int initserver(int type, const struct sockaddr *addr, socklen_t alen)
{
int fd;
if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) {
perror("socket");
return (-1);
}
if (bind(fd, addr, alen) < 0) {
perror("bind");
goto errout;
}
if (listen(fd, SOMAXCONN) < 0) {
perror("listen");
goto errout;
}
return (fd);
errout:
err = errno;
close(fd);
errno = err;
return(-1);
}
以上代码段,进程终止后立即投入运行的话,就会产生开篇说的问题。原因在APUE这本书也有提及:
Normally, the implementation of TCP will prevent us from binding the same address until a timeout expires, which is usually on the order of several minutes.
同时也提供了解决方法,给socket设置SO_REUSEADDR属性:
int reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int);
但是,如果你的进程调用了fork()或system()函数,上述方案则无能为力。
我们知道,每一个文件描述符都有一个引用计数,open的时候增加,close的时候减少,只有在引用计数为0的时候,文件描述符占用的资源才会被回收。而创建子进程会使文件描述符的引用计数增加,如此即使父进程终止,其监听的socket也会因为引用计数不为0而无法释放,进而相应端口一直处于Listen状态(netstat命令),导致进程无法bind指定端口。
知道了原因,对症下药即可:创建子进程时禁止对文件描述符的复制。
声明:本文内容用于数码产品信息整理与选购参考,具体价格、库存、售后政策以官方渠道和电商页面实时信息为准。