linux网络编程之绑定端口注意事项及端口复用.docx

上传人:b****5 文档编号:7846840 上传时间:2023-01-26 格式:DOCX 页数:15 大小:328.61KB
下载 相关 举报
linux网络编程之绑定端口注意事项及端口复用.docx_第1页
第1页 / 共15页
linux网络编程之绑定端口注意事项及端口复用.docx_第2页
第2页 / 共15页
linux网络编程之绑定端口注意事项及端口复用.docx_第3页
第3页 / 共15页
linux网络编程之绑定端口注意事项及端口复用.docx_第4页
第4页 / 共15页
linux网络编程之绑定端口注意事项及端口复用.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

linux网络编程之绑定端口注意事项及端口复用.docx

《linux网络编程之绑定端口注意事项及端口复用.docx》由会员分享,可在线阅读,更多相关《linux网络编程之绑定端口注意事项及端口复用.docx(15页珍藏版)》请在冰豆网上搜索。

linux网络编程之绑定端口注意事项及端口复用.docx

linux网络编程之绑定端口注意事项及端口复用

Linux网络编程之绑定端口注意事项及端口复用

所谓绑定(bind)是指别人连接我只能通过我所绑定的端口,相当于,我买了一个手机,别人要想联系我,必须要知道我的手机号码,这时候,我需要怎么办呢?

我需要给手机插上电话卡,固定一个电话号码,这样别人就能通过这个电话号码联系我。

手机插上电话卡,固定一个电话号码,类似于绑定(bind)的过程,绑定(bind)为了固定一个端口号,别的网络程序就可以找到这个端口号,找到这个端口号就能找到这个端口号所对应的网络应用程序。

在网络编程里,通常都是在服务器里绑定(bind)端口,这并不是说客户端里不能绑定(bind)端口,但这里需要注意的是,一个网络应用程序只能绑定一个端口(一个套接字只能绑定一个端口)。

一个套接字不能同时绑定多个端口,如下:

#include    

#include    

#include    

#include    

#include    

#include    

#include    

   

int main(int argc, charchar *argv[])   

{   

    char server_ip[30] = "10.221.20.12";   

   

    int sockfd;   

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);        //创建UDP套接字   

    if(sockfd < 0)   

    {   

        perror("socket");   

        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);   

    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);   

   

    // 第一次绑定端口8000   

    int err_log;   

    err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));   

    if(err_log !

= 0)   

    {   

        perror("bind 8000");   

        close(sockfd);         

        exit(-1);   

    }   

   

    // 又一次绑定别的端口9000, 会绑定失败   

    my_addr.sin_port = htons(9000);   

    err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));   

    if(err_log !

= 0)   

    {   

        perror("bind 9000");   

        close(sockfd);         

        exit(-1);   

    }   

   

    close(sockfd);   

    return 0;   

}   

程序编译运行后结果如下:

 

 

如果客户端想绑定端口号,一定要调用发送信息函数之前绑定(bind)端口,因为在发送信息函数(sendto,或write),系统会自动给当前网络程序分配一个随机端口号,这相当于随机绑定了一个端口号,这里只会分配一次,以后通信就以这个随机端口通信,我们再绑定端口号的话,就会绑定失败。

如果我们放在发送信息函数(sendto,或write)之前绑定,那样程序将以我们绑定的端口号发送信息,不会再随机分配一个端口号。

绑定失败例子(UDP)如下:

#include    

#include    

#include    

#include    

#include    

#include    

#include    

   

int main(int argc, charchar *argv[])   

{   

    char server_ip[30] = "10.221.20.12";   

       

    int sockfd;   

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);        //创建UDP套接字   

    if(sockfd < 0)   

    {   

        perror("socket");   

        exit(-1);   

    }   

       

    struct sockaddr_in dest_addr;   

    bzero(&dest_addr, sizeof(dest_addr));   

    dest_addr.sin_family = AF_INET;   

    dest_addr.sin_port   = htons(8080); // 服务器的端口   

    inet_pton(AF_INET, server_ip, &dest_addr.sin_addr);   

       

    char send_buf[512] = "this is for test";   

    // 如果前面没有绑定端口,sendto()系统会随机分配一个端口   

    sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));//发送数据   

       

    // 初始化本地网络信息   

    struct sockaddr_in my_addr;   

    bzero(&my_addr, sizeof(my_addr));   

    my_addr.sin_family = AF_INET;   

    my_addr.sin_port   = htons(8000);   

    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);   

       

    // sendto()后面绑定端口,绑定失败   

    int err_log;   

    err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));   

    if(err_log !

= 0)   

    {   

        perror("bind 8000");   

        close(sockfd);         

        exit(-1);   

    }   

   

    close(sockfd);   

    return 0;   

}   

程序编译运行后结果如下:

 

 

在上面提到:

一个网络应用程序只能绑定一个端口(一个套接字只能绑定一个端口)。

实际上,默认的情况下,如果一个网络应用程序的一个套接字绑定了一个端口(占用了8000),这时候,别的套接字就无法使用这个端口(8000),验证例子如下:

#include    

#include    

#include    

#include    

#include    

#include    

#include    

   

int main(int argc, charchar *argv[])   

{   

    int sockfd_one;   

    int err_log;   

    sockfd_one = socket(AF_INET, SOCK_DGRAM, 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);   

   

    // 绑定,端口为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_DGRAM, 0);  //创建UDP套接字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;   

}   

程序编译运行后结果如下:

 

 

那如何让sockfd_one,sockfd_two两个套接字都能成功绑定8000端口呢?

这时候就需要要到端口复用了。

端口复用允许在一个应用程序可以把n个套接字绑在一个端口上而不出错。

设置socket的SO_REUSEADDR选项,即可实现端口复用:

int opt = 1;   

// sockfd为需要端口复用的套接字   

setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const voidvoid *)&opt, sizeof(opt));   

SO_REUSEADDR可以用在以下四种情况下。

(摘自《Unix网络编程》卷一,即UNPv1)

1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。

但每个实例绑定的IP地址是不能相同的。

在有多块网卡或用IPAlias技术的机器可以测试这种情况。

3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。

这和2很相似,区别请看UNPv1。

4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。

但这只用于UDP的多播,不用于TCP。

需要注意的是,设置端口复用函数要在绑定之前调用,而且只要绑定到同一个端口的所有套接字都得设置复用:

// sockfd_one, sockfd_two都要设置端口复用   

// 在sockfd_one绑定bind之前,设置其端口复用   

int opt = 1;   

setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, (const voidvoid *)&opt, sizeof(opt) );   

err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));   

   

// 在sockfd_two绑定bind之前,设置其端口复用   

opt = 1;   

setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,(const voidvoid *)&opt, sizeof(opt) );   

err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));   

端口复用完整代码如下:

#include    

#include    

#include    

#include    

#include    

#include    

#include    

   

int main(int argc, charchar *argv[])   

{   

    int sockfd_one;   

    int err_log;   

    sockfd_one = socket(AF_INET, SOCK_DGRAM, 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 voidvoid *)&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_DGRAM, 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 voidvoid *)&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);   

    }   

   

    close(sockfd_one);   

    close(sockfd_two);   

   

    return 0;   

}   

端口复用允许在一个应用程序可以把n个套接字绑在一个端口上而不出错。

同时,这n个套接字发送信息都正常,没有问题。

但是,这些套接字并不是所有都能读取信息,只有最后一个套接字会正常接收数据。

下面,我们在之前的代码上,添加两个线程,分别负责接收sockfd_one,sockfd_two的信息:

#include    

#include    

#include    

#include    

#include    

#include    

#include    

#include    

   

// 线程1的回调函数   

voidvoid *recv_one(voidvoid *arg)   

{   

    printf("===========recv_one==============\n");   

    int sockfd = (int )arg;   

    while

(1){   

        int recv_len;   

        char recv_buf[512] = "";   

        struct sockaddr_in client_addr;   

        char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16   

        socklen_t cliaddr_len = sizeof(client_addr);   

           

        recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);   

        inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);   

        printf("\nip:

%s ,port:

%d\n",cli_ip, ntohs(client_addr.sin_port));   

        printf("sockfd_one =========== data(%d):

%s\n",recv_len,recv_buf);   

       

    }   

   

    return NULL;   

}   

   

// 线程2的回调函数   

voidvoid *recv_two(voidvoid *arg)   

{   

    printf("+++++++++recv_two++++++++++++++\n");   

    int sockfd = (int )arg;   

    while

(1){   

XX文库-让每个人平等地提升自我        int recv_len;   

        char recv_buf[512] = "";   

        struct sockaddr_in client_addr;   

        char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16   

        socklen_t cliaddr_len = sizeof(client_addr)

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 自我管理与提升

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1