用端口复用技术解决tcp连接下服务器主动关闭连接后不能立即重启的问题

本例中端口复用可以让服务器中多个socket共同使用同一个端口号。


问题描述:在tcp连接下,如果服务器主动关闭连接(比如ctrl+c结束服务器进程),那么由于服务器这边会出现time_wait状态,所以不能立即重新启动服务器进程。


解决这个问题就可以用端口复用,让多个socket可以同时绑定在一个ip和端口上,这样,就算是上一个socket处于time_wait状态占用着该端口,也不会影响其它socket使用该端口。  在使用端口复用的时候,服务器这边只需要在每次对socket绑定(bind)的时候对这个socket的选项进行设置就可以了。这样就可以让这个socket可以在该端口被占用的情况下依然能够使用该端口。 设置socket选项的函数为: setsockopt(int sockfd,int level,int optname,const void* optval,socklen_t optlen);

下面检测一下使用端口复用的效果:

NO1、不使用端口复用,代码如下:

服务器:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in saddr,caddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//不使用端口复用,屏蔽起来
	/*
	int opt = 1;
	setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt));
	*/

	int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	assert(res != -1);
	
	listen(sockfd,5);

	while(1)
	{
		int len = sizeof(caddr);
		int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
		if( c < 0)
		{
			continue;
		}

		printf("accept c = %d\n",c);
		while(1)
		{
			char buff[128] = {0};
			recv(c,buff,127,0);//read
			printf("buff=%s\n",buff);

			send(c,"OK",2,0);
			if(strncmp(buff,"end",3)==0)
			{
				break;
			}
		}

		close(c);
	}
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in saddr,caddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	assert(res != -1);
	

	while(1)
	{
		printf("input:\n");
		char buff[128]={0};
		fgets(buff,128,stdin);
		char s[128];
		strcpy(s,buff);

		buff[strlen(buff)-1]=0;
		send(sockfd,buff,strlen(buff),0);//write

		memset(buff,0,128);
		recv(sockfd,buff,127,0);//read
		if(strncmp(s,"end",3)==0)
		{
			break;
		}
		printf("buff=%s\n",buff);
	}
		close(sockfd);
}


上述是不使用端口复用的代码,看看运行结果

第一次运行起来进行正常的通信。然后先关闭服务器再关闭客户端,再立即启动服务器,出现下图情况:

通过netstat命令查看发现该端口处于time_wait状态,所以服务器不能立即重启:

NO2、上述是没有使用端口复用的服务器,再看看使用端口复用的服务器:

客户端代码和上次的一样不用修改,服务器的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in saddr,caddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	//使用端口复用,在bind之前先设置好socket的选线,让其可以绑定在已经正在被别的socket使用的端口上。
	int opt = 1;
	setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt));

	int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	assert(res != -1);
	
	listen(sockfd,5);

	while(1)
	{
		int len = sizeof(caddr);
		int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
		if( c < 0)
		{
			continue;
		}

		printf("accept c = %d\n",c);
		while(1)
		{
			char buff[128] = {0};
			recv(c,buff,127,0);//read
			printf("buff=%s\n",buff);

			send(c,"OK",2,0);
			if(strncmp(buff,"end",3)==0)
			{
				break;
			}
		}

		close(c);
	}
}


使用刚才同样的方法进行测试,结果如下:

可见,在服务器主动关闭连接后,我还是可以立即重新启动服务器的,而不受time_wait的影响。但是通过netstat命令查看这个端口上还是有time_wait的出现:

这时的time_wait是上次连接中与该地址和端口绑定的socket出现了time_wait状态,但是采用了端口复用后,可以多个socket和这个端口进行绑定,所以新的一次的连接使用的是新创建的socket和该端口进行绑定,并不会受到上一个socket处于time_wait的影响。

虽然可以立即启动服务器,但是对于高并发模式下的服务器在短时间内也是不能使用已经处于time_wait状态的socket的,要解决这样的问题就要用其它的方法(比如通过设置内核参数避免出现time_wait状态)。 上述中服务器能够立即重启是因为使用了新的socket和端口进行了绑定,而上次已经断开连接的socket还是在处于time_wait中的,这个socket在短时间内是不能再非配出去继续使用的。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值