Socket编程之一个端口能建立多个TCP连接?

最爱的C 专栏收录该内容
36 篇文章 0 订阅

个人博客:www.saoguang.top

一、背景

记得上学期暑假的时候我基于MFC写了一个简单的聊天程序。那个聊天程序,两部分组成,监听客户端请求线程和客户端请求处理线程。

1.服务器接收到登陆请求,验证登陆信息后,如果通过验证建立新线程与其交互,并通知用户连接到新的端口,并创建好新端口的SOCKET连接。

2.然后将用户类和新端口传给新建立的客户端请求处理线程。

当时,可能是没理解好的原因,误以为,一个端口同一时间只能建立起一个TCP连接。所以写这个聊天程序时才会每一个用户分配一个新的端口。

之前,自己了解HTTP后,接触到web应用开发的时候,就疑惑了,web server接收浏览器的请求,都是从80端口接受请求。当时没仔细去想,就以为web server和我那个聊天程序一样,会去建立新的线程与其进行请求处理。

二、问题

最近,写爬虫的时候用到了Smsniff去抓包。发现,一个http请求中。往往是只与服务器的80端口进行通信。这就与我记忆中的SOCKET冲突了。于是今天写了个小代码测试了一下,一个端口,真的能建立多个连接。

三、代码逻辑流程

1.server

①服务端主线程:负责监听5174端口,如果有请求,accept到系统分配的SOCKET(为unsigned int, recv接受函数就需要这个SOCKET)于是建立一个新线程,将这个SOCKET通过lpParament传递给新线程。

②服务器信息接受线程:负责从lpParment从拿到SOCKET并,recv客户端发来的信息。

2.client

连接到服务器的5174端口,并发送消息。

四、执行结果

一个端口的确能同时建立多条TCP请求。

五、理解

综合部分网上看到的资料。我的理解是,一个连接的唯一标识是[server ip, server port, client ip, client port]也就是说。操作系统,接收到一个端口发来的数据时,会在该端口,产生的连接中,查找到符合这个唯一标识的并传递信息到对应缓冲区。

1.一个端口同一时间只能bind给一个SOCKET。就是同一时间一个端口只可能有一个监听线程(监听listen之前要bind)。

2.为什么一个端口能建立多个TCP连接,同一个端口也就是说 server ip和server port 是不变的。那么只要[client ip 和 client port]不相同就可以了。能保证接唯一标识[server ip, server port, client ip, client port]的唯一性。

 

六、疑问解答

1.如果监听的线程释放掉监听用的SOCKET了,会影响之前通过这个监听SOCKET建立的TCP连接么?

答案:并不会,SOCKET之间是独立的,不会有影响(我已经自己写了程序验证了,读者可以自己写代码验证)。

2.一个端口能建立多个UDP连接么?

答案:UPD本身就是无连接的。所以不存在什么多个UDP连接。只是,服务端接收UDP数据需要bind一个端口。一个SOCKET只能绑定到一个端口。

七、服务端和客户端代码

注意:如果用的是VS,记得把 SDL checks(安全开发生命周期检测关闭)

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
#include <memory.h>

#define BUF_SIZE 4096
#define QUEUE_SIZE 5

DWORD WINAPI ThreadProcServerConmunicate(
	_In_ LPVOID lpParameter
) {
	SOCKET * psc = (SOCKET *)lpParameter;

	int receByt = 0;
	while (1)
	{
		char buf[BUF_SIZE];
		receByt = recv(*psc, buf, BUF_SIZE, 0);
		buf[receByt] = '\0';
		if (receByt>0)
		{
			printf("%u : 接收的消息是:%s\n", *psc, buf);
		}
		else
		{
			printf("接收消息结束!");
			break;
		}

	}
	int ic = closesocket(*psc);
	free(psc);
	return 0;
}


int main() {
	
	WSADATA wsd;
	WSAStartup(MAKEWORD(2, 0), &wsd);

	SOCKET s = NULL;
	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	struct sockaddr_in ch;
	memset(&ch, 0, sizeof(ch));
	ch.sin_family = AF_INET;
	ch.sin_addr.s_addr = INADDR_ANY;
	ch.sin_port = htons(5174);
	int b = bind(s, (struct sockaddr *) &ch, sizeof(ch));
	
	int l = listen(s, QUEUE_SIZE);
	printf("正在监听本机的5174端口\n");

	while (1) {
		SOCKET * psc = (SOCKET *)malloc(sizeof(SOCKET));
		*psc = accept(s, 0, 0);
		printf("一个客户端已经连接到本机的5174端口,SOCKET是 : %u \n", *psc);

		CreateThread(NULL,
			0,
			&ThreadProcServerConmunicate,
			psc,
			0,
			NULL
		);
	}
	
	int is = closesocket(s);
	WSACleanup();
	return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
#include <memory.h>

#define BUF_SIZE 4096

void main()
{
	WSADATA wsd;
	WSAStartup(MAKEWORD(2, 0), &wsd);

	SOCKET s = NULL;
	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	struct sockaddr_in ch;
	memset(&ch, 0, sizeof(ch));
	ch.sin_family = AF_INET;
	ch.sin_addr.s_addr = inet_addr("127.0.0.1");
	ch.sin_port = htons(5174);

	int c = connect(s, (struct sockaddr *) &ch, sizeof(ch));
	printf("已经连接到服务器的5174端口,现在可以向服务器发送消息了!\n");

	char info[1024], buf[BUF_SIZE];

	while (1)
	{
		gets(info);
		if (info[0] == '\0')
			break;
		strcpy(buf, info);
		int nsend = send(s, buf, strlen(buf), 0);
		Sleep(500);
	}
	int ic = closesocket(s);
	WSACleanup();
	return 0;
}

 

评论 18 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:终极编程指南 设计师:CSDN官方博客 返回首页

打赏作者

HotIce0

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值