https://www.cnblogs.com/rockyching2009/p/11032230.html
一、背景
端对端的通信中存在的一个问题是:如何唯一地标识通信主体。对于socket,解决这个问题的方式是四元组:自身IP,自身端口,对方IP,对方端口。
在socket编程中,作为client,端口号是由操作系统管理和分配的,所以不存在端口占用的情况。如果作为server,在bind某个端口的时候,或多或少遇到过如下错误:
1 | bind: Address already in use |
在进程异常终止或重启网络服务的时候,出现上述问题的情况很常见;而作为服务端又需要进程马上步入正轨,这个问题就显得尤其重要。
二、原因及解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /** 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这本书也有提及:
1 | Normally, the implementation of TCP will prevent us from binding the same address until a timeout expires,<br>which is usually on the order of several minutes. |
同时也提供了解决方法,给socket设置SO_REUSEADDR属性:
1 2 | int reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof ( int ); |
三、未完再续
但是,如果你的进程调用了fork()或system()函数,上述方案则无能为力。
对于子进程,创建成功后将复制父进程的数据空间、堆和栈副本,这其中也包括文件描述符(fork过程);而且,一般我们fork子进程后接着执行一个新程序(execve过程),这就导致copy过来的文件描述符一直无法关闭,如此即使父进程终止,其监听的socket也无法释放,进而相应端口一直处于Listen状态(netstat命令),导致进程无法bind指定端口。
知道了原因,对症下药即可:创建的子进程execve时禁止对文件描述符的复制,即close-on-exec。
在Linux系统中,这是通过fcntl()设置文件描述符的FD_CLOEXEC标志实现:
1 2 3 4 5 6 7 8 | int set_cloexec( int fd) { int val; val = fcntl(fd, F_GETFD, 0); val |= FD_CLOEXEC; /* enable close-on-exec */ return fcntl(fd, F_SETFD, val); } |
莎翁说:简洁是智慧的灵魂,上面一段毕竟显得臃肿,所以socket引入了SOCK_CLOEXEC实现同样的功能
对于通过open()函数创建的文件描述符,可以设置flags入参O_CLOEXEC选项达到close-on-exec的目的。
参考资料
2、《UNIX环境高级编程》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!