首页 » 网站推广 » 端口复用php技巧_Linux收集编程端口复用多个套接字绑定同一个端口

端口复用php技巧_Linux收集编程端口复用多个套接字绑定同一个端口

访客 2024-12-06 0

扫一扫用手机浏览

文章目录 [+]

#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_DGRAM, 0); //创建UDP套接字oneif(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);// 端口为8000my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定,端口为8000err_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套接字twoif(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 个套接字绑在一个端口上而不出错。

端口复用php技巧_Linux收集编程端口复用多个套接字绑定同一个端口

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

端口复用php技巧_Linux收集编程端口复用多个套接字绑定同一个端口
(图片来自网络侵删)

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

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

当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
SO_REUSEADDR许可同一port上启动同一做事器的多个实例(多个进程)。
但每个实例绑定的IP地址是不能相同的。
在有多块网卡或用IP Alias技能的机器可以测试这种情形。
SO_REUSEADDR许可单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。
这和2很相似,差异请看UNPv1。
SO_REUSEADDR许可完备相同的地址和端口的重复绑定。
但这只用于UDP的多播,不用于TCP。

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

// sockfd_one, sockfd_two都要设置端口复用// 在sockfd_one绑定bind之前,设置其端口复用int opt = 1;setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, (const void )&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 void )&opt, sizeof(opt) );err_log = bind(sockfd_two, (struct sockaddr)&my_addr, sizeof(my_addr));

须要C/C++ Linux做事器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技能,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

端口复用完全代码如下:

#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_DGRAM, 0); //创建UDP套接字oneif(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);// 端口为8000my_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) ); // 绑定,端口为8000err_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套接字twoif(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);} close(sockfd_one);close(sockfd_two); return 0;}

端口复用许可在一个运用程序可以把 n 个套接字绑在一个端口上而不出错。
同时,这 n 个套接字发送信息都正常,没有问题。
但是,这些套接字并不是所有都能读取信息,只有末了一个套接字会正常吸收数据。

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

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <pthread.h> // 线程1的回调函数void recv_one(void 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=16socklen_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的回调函数void recv_two(void arg){printf("+++++++++recv_two++++++++++++++\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=16socklen_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_two @@@@@@@@@@@@@@@ data(%d):%s\n",recv_len,recv_buf);} return NULL;} int main(int argc, char argv[]){int err_log;/////////////////////////sockfd_oneint sockfd_one;sockfd_one = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字oneif(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);// 端口为8000my_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) ); // 绑定,端口为8000err_log = bind(sockfd_one, (struct sockaddr)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind sockfd_one");close(sockfd_one);exit(-1);}//吸收信息线程1pthread_t tid_one;pthread_create(&tid_one, NULL, recv_one, (void )sockfd_one);/////////////////////////sockfd_twoint sockfd_two;sockfd_two = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字twoif(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);}//吸收信息线程2pthread_t tid_two;pthread_create(&tid_two, NULL, recv_two, (void )sockfd_two);while(1){// 让程序壅塞在这,不绝止NULL;} close(sockfd_one);close(sockfd_two); return 0;}

接着,通过网络调试助手给这个做事器发送数据,结果显示,只有末了一个套接字sockfd_two会正常吸收数据:

我们上面的用法,实际上没有太大的意义。
端口复用最常用的用场该当是防止做事看重启时之前绑定的端口还未开释或者程序溘然退出而系统没有开释端口。
这种情形下如果设定了端口复用,则新启动的做事器进程可以直接绑定端口。
如果没有设定端口复用,绑定会失落败,提示ADDR已经在利用中——那只好等等再重试了,麻烦!

标签:

相关文章

phpsoa实例技巧_SOA在汽车行业的应用和前景

第一个问题智能汽车到底需不须要SOA?这里须要先看一下智能驾驶时期的汽车架构和汽车软件的实际需求:传统的整车架构,尤其是电子和电气...

网站推广 2024-12-08 阅读0 评论0