0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

bind系统调用背后的端口管理复用

麦辣鸡腿堡 来源:威廉希尔官方网站 简说 作者:董旭 2023-07-31 10:45 次阅读

很久之前写过以上:套接字socket的底层来龙去脉、sockfs文件系统的实现,可以作为本文的前置知识进行学习浏览。

先来一张本文中核心的一张图,具体可以看后面文章的解释:

图片

本文从socket的bind系统调用进行分析,主要是了解一下bind背后,Linux内核是如何进行端口绑定、如何管理本地众多的端口号。

先直观感受bind系统调用背后的端口管理、端口复用

#include < stdio.h >
#include < stdlib.h >
#include < string.h >
#include < unistd.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
 
int main(int argc, char *argv[])
{
    int sockfd_one;
    int err_log;
    sockfd_one = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字one
    if(sockfd_one < 0)
    {
    perror("sockfd_one");
    exit(-1);
    }
 
    // 设置本地网络信息
    struct sockaddr_in my_addr;
    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(8000);        // 端口为8000
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
    // 绑定,端口为8000
    err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));
    if(err_log != 0)
    {
        perror("bind sockfd_one");
        close(sockfd_one);        
        exit(-1);
    }
 
    int sockfd_two;
    sockfd_two = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字two
    if(sockfd_two < 0)
    {
        perror("sockfd_two");
        exit(-1);
    }
 
    // 新套接字sockfd_two,继续绑定8000端口,绑定失败
    // 因为8000端口已被占用,默认情况下,端口没有释放,无法绑定
    err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));
    if(err_log != 0)
    {
        perror("bind sockfd_two");
        close(sockfd_two);        
        exit(-1);
    }
 
    close(sockfd_one);
    close(sockfd_two);
 
    return 0;
}

图片

可以看到端口重复绑定导致了第二个套接字创建失败,我们通过setsockopt系统调用在创建socket后设置端口可复用:

int opt = 1;
// sockfd为需要端口复用的套接字
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));

具体如下:

#include < stdio.h >
#include < stdlib.h >
#include < string.h >
#include < unistd.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
 
int main(int argc, char *argv[])
{
    int sockfd_one;
    int err_log;
    sockfd_one = socket(AF_INET, SOCK_STREAM, 0); //创建UDP套接字one
    if(sockfd_one < 0)
    {
    perror("sockfd_one");
    exit(-1);
    }
 
    // 设置本地网络信息
    struct sockaddr_in my_addr;
    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(8000);        // 端口为8000
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    // 在sockfd_one绑定bind之前,设置其端口复用
    int opt = 1;
    setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, 
                    (const void *)&opt, sizeof(opt) );
 
    // 绑定,端口为8000
    err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));
    if(err_log != 0)
    {
        perror("bind sockfd_one");
        close(sockfd_one);        
        exit(-1);
    }
 
    int sockfd_two;
    sockfd_two = socket(AF_INET, SOCK_STREAM, 0);  //创建UDP套接字two
    if(sockfd_two < 0)
    {
        perror("sockfd_two");
        exit(-1);
    }
 
    // 在sockfd_two绑定bind之前,设置其端口复用
    opt = 1;
    setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR, 
                    (const void *)&opt, sizeof(opt) );
    
    // 新套接字sockfd_two,继续绑定8000端口,成功
    err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));
    if(err_log != 0)
    {
        perror("bind sockfd_two");
        close(sockfd_two);        
        exit(-1);
    }
    printf("two socket create success!n");
    close(sockfd_one);
    close(sockfd_two);
 
    return 0;
}

图片

如上,两个套接字绑定同一个端口都创建成功。下面将从bind出发分析bind是如何端口管理、复用的。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内核
    +关注

    关注

    3

    文章

    1372

    浏览量

    40288
  • Linux
    +关注

    关注

    87

    文章

    11302

    浏览量

    209430
  • 系统
    +关注

    关注

    1

    文章

    1016

    浏览量

    21338
收藏 人收藏

    评论

    相关推荐

    从Linux源码分析bind系统调用

    众所周知,一个Server端Socket的建立,需要socket、bind、listen、accept四个步骤。
    的头像 发表于 10-16 11:08 3303次阅读
    从Linux源码分析<b class='flag-5'>bind</b><b class='flag-5'>系统</b><b class='flag-5'>调用</b>

    udp_bind这个绑定的端口怎么解除?

    请教下,udp_bind 这个绑定的端口,刚开始是可以的,但是重新绑定时返回错误,有什么方法可以在 重新绑定前解除之前的绑定 ?
    发表于 04-22 07:41

    TCP server 不能 bind 80 端口

    后程序只开一个 AP 模式下的 TCP server ,测试。  但是发现 80 端口没法 bind ,错误码是 -98 ,意思是端口已被占用? 换一个其它端口号(比如 12345)就
    发表于 05-14 00:33

    如何去使用STM32的端口复用

    STM32的端口复用映射原理是什么?如何去使用STM32的端口复用呢?
    发表于 11-01 06:45

    端口复用映射原理及GPIO外设复用功能配置过程

    文章目录端口复用端口复用映射原理原理示意图映射配置系统功能GPIO外设复用功能配置过程GPIO
    发表于 12-09 07:20

    STM32的端口复用映射原理是什么

    STM32的端口复用映射原理是什么?STM32的端口复用配置过程是怎样的?
    发表于 12-15 07:03

    什么叫端口复用?如何配置端口

    端口复用和重映射什么叫端口复用?一个引脚既能当IO用又有其他的第二功能那么如何配置端口呢?1.IO口使能2.
    发表于 01-11 08:12

    端口复用概念

    记录一下,方便以后翻阅~主要内容:1)端口复用;2)端口重映射;官方资料:《STM32中文参考手册V10》第8章 通用和复用功能IO(GPIO和AFIO)1.
    发表于 01-11 07:43

    Bind源代码包安装

    先到官方下载Bind的安装包 wgetftp://ftp.isc.org/isc/bind9/9.6.0-P1/bind-9.6.0-P1.tar.gz tar xzvf
    发表于 04-04 20:30 23次下载

    STM32单片机端口复用端口重映射

    STM32单片机端口复用端口重映射STM32单片机上有很多I/O口,也有很多的内置外设,比如I2C、ADC、DAC、USART等都属于内置外设。这些内置外设基本都是与I/O口共用管脚的,也就是I
    发表于 12-28 19:23 8次下载
    STM32单片机<b class='flag-5'>端口</b><b class='flag-5'>复用</b>和<b class='flag-5'>端口</b>重映射

    STM32学习笔记--端口复用&重映射

    目录:一、端口复用二、端口重映射一、端口复用  STM32 有很多的内置外设,这些外设的外部引脚都是与 GPIO
    发表于 12-28 19:35 14次下载
    STM32学习笔记--<b class='flag-5'>端口</b><b class='flag-5'>复用</b>&重映射

    什么是bind?你真的熟悉bind吗?

    bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定 bind()的第一个参数,而其余参数将作为新函数的参数,供调用
    的头像 发表于 07-13 09:56 2769次阅读

    网络系统调用网络套接字入口函数

    网络套接字入口函数 //所有的网络套接字系统调用函数(socket bind listen connect )都使用一个共同的入口函数:sys_socketcall /* 第一个参数call表示被
    的头像 发表于 07-24 11:02 472次阅读

    Linux bind的核心执行函数

    bind的核心执行函数 bind系统调用的核心函数调用流程如下: SYSCALL_DEFINE3( bi
    的头像 发表于 07-31 10:51 574次阅读

    Linux内核分析 bind端口选择

    bind 传 递的地址参数中,port 字段为 0,那么就会自动选择参数。 如代码所示,当端口port没有指定时,调用inet_csk_find_open_port(sk, port): if (!port) { head
    的头像 发表于 07-31 11:08 960次阅读