端口复用(bind error: Address already in use 问题)

举报
mindtechnist 发表于 2024/08/06 17:24:48 2024/08/06
【摘要】 ​在前面讲解TCP状态转换中提到过一个2MSL等待时间,如果在通信过程中,server主动断开连接,那么server进程会处于TIME_WAIT状态并等待2MSL的时间,此时server进程还没终止,端口号port还被该进程占用呢,所以当server主动断开连接时,如果立即再次启动server,就会提示端口已经被使用,等待2MSL后才可以再次启动server。请看下图端口复用常见的用途包括:...

​在前面讲解TCP状态转换中提到过一个2MSL等待时间,如果在通信过程中,server主动断开连接,那么server进程会处于TIME_WAIT状态并等待2MSL的时间,此时server进程还没终止,端口号port还被该进程占用呢,所以当server主动断开连接时,如果立即再次启动server,就会提示端口已经被使用,等待2MSL后才可以再次启动server。请看下图

端口复用常见的用途包括:

  • 防止服务器重启时,之前绑定的端口还未释放;

  • 程序突然退出系统但是没有释放端口。


设置端口复用需要用到的API如下:

int opt = 1;  setsockopt(sockfd, SOL_SOCKET,  SO_REUSEADDR, (const void *)&opt, sizeof(opt)); 
  • sockfd:监听的文件描述符

  • SOL_SOCKET:级别

  • SO_REUSEADDR:也可以用SO_REUSEPORT

  • opt:赋值为1表示属性被启用

  • sizeof(opt):

相关参数设置及含义可在下图中查看

在设置端口复用时,需要注意的是,必须在绑定端口之前就设置端口复用属性。实际上setsockopt()函数有很多功能,设置端口复用只是它的功能之一。

下面是一个设置端口复用的示例

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
​
// server
int main(int argc, const char* argv[])
{
    // 创建监听的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1)
    {
        perror("socket error");
        exit(1);
    }
​
    // 绑定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(9999);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 本地多有的IP
    // 127.0.0.1
    // inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
    
    // 设置端口复用 
    // 需要在bind函数之前设置
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));
​
    // 绑定端口
    int ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if(ret == -1)
    {
        perror("bind error");
        exit(1);
    }
​
    // 监听
    ret = listen(lfd, 64);
    if(ret == -1)
    {
        perror("listen error");
        exit(1);
    }
​
    // 阻塞等待连接请求, 并接受连接请求
    struct sockaddr_in clien_addr;
    socklen_t clien_len = sizeof(clien_addr);
    int cfd = accept(lfd, (struct sockaddr*)&clien_addr, &clien_len);
    if(cfd == -1)
    {
        perror("accetp error");
        exit(1);
    }
​
    char ipbuf[128];
    printf("client iP: %s, port: %d\n", inet_ntop(AF_INET, &clien_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
           ntohs(clien_addr.sin_port));
​
    char buf[1024] = {0};
    while(1)
    {
        // read data
        // int len = read(cfd, buf, sizeof(buf));
        int len = recv(cfd, buf, sizeof(buf), 0);
        if(len == -1)
        {
            perror("recv error");
            exit(1);
        }
        else if(len == 0)
        {
            printf("客户端已经断开连接。。。\n");
            break;
        }
        printf("read buf = %s\n", buf);
        // 小写转大写
        for(int i=0; i<len; ++i)
        {
            buf[i] = toupper(buf[i]);
        }
        printf("after buf = %s\n", buf);
​
        // 大写串发给客户端
        // write(cfd, buf, strlen(buf)+1);
        ret = send(cfd, buf, strlen(buf)+1, 0);
        if(ret == -1)
        {
            perror("send error");
            exit(1);
        }
    }
​
    close(cfd);
    close(lfd);
​
    return 0;
}

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。