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

描述

很久之前写过以上:套接字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是如何端口管理、复用的。

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

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分