• 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()函数,上述方案则无能为力。

    对于子进程,创建成功后将复制父进程的数据空间、堆和栈副本,这其中也包括文件描述符(fork过程);而且,一般我们fork子进程后接着执行一个新程序(execve过程),这就导致copy过来的文件描述符一直无法关闭,如此即使父进程终止,其监听的socket也无法释放,进而相应端口一直处于Listen状态(netstat命令),导致进程无法bind指定端口。

    知道了原因,对症下药即可:创建的子进程execve时禁止对文件描述符的复制,即close-on-exec

    在Linux系统中,这是通过fcntl()设置文件描述符的FD_CLOEXEC标志实现:

    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的目的。

    参考资料

    1、man 2 open

    2、《UNIX环境高级编程》

  • 相关阅读:
    Session、Cookie、Application、ViewState和Cache 这四者的区别
    用C#构造HighChart类库,把数据转换成JSON第二阶段完成50%API,已经能满足项目要求了
    HttpHandler 在SharePoint 2010中的应用
    SharePoint 2010 在多台前端环境 还原 网站集 问题解析
    SharePoint 2010 PowerShell 系列 之 文档管理 高级应用和企业案例(文档迁移)
    设计模式策略模式
    CentOS7 安装Hbase集群
    CentOS7 安装zookeeper
    CentOS7 安装Hadoop集群环境
    Django Rest Framework关闭CSRF验证
  • 原文地址:https://www.cnblogs.com/rockyching2009/p/11032230.html
Copyright © 2020-2023  润新知