首页 / 美文 / 正文

socketbind(Linux网络编程——端口复用(多个套接字绑定同一个端口))

放大字体  缩小字体 来源:aj和nike什么关系 2026-04-15 13:35  浏览次数:8

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

Linux网络编程——端口复用(多个套接字绑定同一个端口)nerror="javascript:errorimg.call(this);">

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

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

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

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

端口复用完整代码如下:

#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套接字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_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 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;}

下面,我们在之前的代码上,添加两个线程,分别负责接收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=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的回调函数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=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_two @@@@@@@@@@@@@@@ data(%d):%s\n",recv_len,recv_buf);		} 	return NULL;} int main(int argc, char *argv[]){	int err_log;		/////////////////////////sockfd_one	int sockfd_one;	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 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);	}		//接收信息线程1	pthread_t tid_one;	pthread_create(&tid_one, NULL, recv_one, (void *)sockfd_one);		/////////////////////////sockfd_two	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 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);	}	//接收信息线程2	pthread_t tid_two;	pthread_create(&tid_two, NULL, recv_two, (void *)sockfd_two);			while(1){	// 让程序阻塞在这,不结束		NULL;	} 	close(sockfd_one);	close(sockfd_two); 	return 0;}

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

打赏
0相关评论
热门搜索排行
精彩图片
友情链接
声明:本站信息均由用户注册后自行发布,本站不承担任何法律责任。如有侵权请告知立立即做删除处理。
违法不良信息举报邮箱:115904045
头条快讯网 版权所有
中国互联网举报中心