UDP协议应用

需求:

实现局域网穿透

环境背景:

某一台主机通过互联网即外网和另外一台主机进行通信,主机首先通过拨号上网,这台主机就会分配一个临时ip地址,这时主机就可以和互联网进行通信;同样的另外一台主机也通过相同的方式进行上网,两边就可以进行通信了。在因互联网有很多的服务器,其ip地址是固定的,我们主机通过拨号上网向服务器发起请求,在互联网有个域名服务器会将请求转发给对应的服务器,服务器收到请求后进行验证,验证用户名和密码,验证通过就保持连接,否则断开连接。

产生问题:

①每一台主机都有ip,那么当前主机怎么知道另外一台主机的IP?
②由于主机的IP是临时IP,每天上网的IP都可能不一样,那么当IP发生改变是,当前主机和另外一台主机的连接怎么保持?
③主机进行上网都需要通过路由器或者猫,路由器和猫都有防火墙功能,一般不允许主机放到外网当做服务器的,不允许主机的IP暴露在互联网上,假如主机IP能够暴露在公网上,由于在互联网中需要通过域名服务器进行转发,假如另外一台主机所在的域名服务器没有你主机的ip,他需要向其他域名服务器进行查询,这个过程比较缓慢。而进行上网的是整个局域网的统一一个临时IP暴露在互联网上,那么当前主机怎么知道另外一台主机在局域网的IP。

协议分析:

TCP协议:是可靠、稳定的传输,会有三次握手来建立连接。
UDP协议:是不可靠、不稳定的传输,没有三次握手。

方案:

①准备一个公网服务器,分配一个用于UDP通信的IP和端口。
②准备两台主机,主机使用UDP连接服务器进行登录,服务器发送数据到主机,这时防火墙会打开一个开口,这时主机不要关闭连接,服务器进行通信后就知道了这台主机的IP和端口,另外一台主机也进行相同的操作,服务器再将另外一台主机的IP和端口发送给当前主机,当前主机就使用UDP和另外一台主机进行通信。
③因为UDP是不可靠的传输,所以当另外一台主机接收到数据时不会验证数据是从哪里发过来的,UDP不安全,用户可以在上海模拟长沙的登录;而TCP是可靠传输,发送数据前需要通过三次握手建立连接,而握手请求实发送到服务器的,主机1不能向主机2发起握手,因此TCP无法实现局域网穿透。

示例

服务端代码:

#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 1024

int udp_server();

sockaddr_in addrCli[2];//目的套接字地址族
int len[2] = { sizeof(addrCli[0]), sizeof(addrCli[1]) };
int size = 0;
int main()
{
	//printf("%s(%d):%s\r\n", __FILE__,__LINE__,__FUNCTION__);
	printf("this is server\r\n");
    udp_server();//服务器代码
	return 0;
}

int udp_server()
{
	WSADATA data;
	if (WSAStartup(MAKEWORD(2, 2), &data) != 0) {
		printf("WSAStartup errorNum = %d\r\n", GetLastError());
		return -1;


	}
	SOCKET server = socket(PF_INET, SOCK_DGRAM, 0);
	if (server == INVALID_SOCKET) {
		printf("socket errorNum = %d\r\n", GetLastError());
		return -1;
	}

	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(9527);
	addr.sin_addr.s_addr = inet_addr("0.0.0.0");
	if (SOCKET_ERROR == bind(server, (sockaddr*)&addr, sizeof(addr)))
	{
		printf("bind errorNum = %d\r\n", GetLastError());
		return -1;
	}

	

	char recvBuf[BUFFER_SIZE] = { 0 };
	char sendBuf[BUFFER_SIZE] = { 0 };
	for (int i = 0; i < 2; i++) {
		memset(&addrCli[i], 0, len[i]);
	}


	while (size<2)
	{
		memset(recvBuf, 0, BUFFER_SIZE);
		recvfrom(server, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrCli[size], &len[size]);
		printf("user%d ip=[%s] port=[%d]\n", size+1, inet_ntoa(addrCli[size].sin_addr), htons(addrCli[size].sin_port));
		size++;						
	}
	for (int i = 0; i < 2; i++) {
		sendto(server, (char*)&addrCli[i], len[i], 0, (sockaddr*)&addrCli[2-i-1], len[2-i-1]);
	}
	getchar();
	closesocket(server);
	WSACleanup();
	return 0;
}

客户端代码:

#include <iostream>
#include <WinSock2.h>
#include<process.h>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 409600

int udp_client();

unsigned WINAPI SendMsg(void* arg);

unsigned WINAPI RecvMsg(void* arg);

sockaddr_in addrServer;
int len = sizeof(addrServer);

sockaddr_in addrUser;
int lenSend = sizeof(addrUser);
bool isExit = false;

int main()
{
	printf("this is client\r\n");
	udp_client();
	return 0;
}


int udp_client()
{
	WSADATA data;
	if (WSAStartup(MAKEWORD(2, 2), &data) != 0) {
		printf("WSAStartup errorNum = %d\r\n", GetLastError());
		return -1;

	}

	HANDLE hSendThread;
	HANDLE hRecvThread;

	SOCKET client = socket(PF_INET, SOCK_DGRAM, 0);
	if (client == INVALID_SOCKET) {
		printf("socket errorNum = %d\r\n", GetLastError());
		return -1;
	}
	
	memset(&addrServer, 0, len);
	addrServer.sin_family = AF_INET;
	addrServer.sin_port = htons(9527);
	addrServer.sin_addr.s_addr = inet_addr("127.0.0.1");

	std::cout <<"服务器:"<<inet_ntoa(addrServer.sin_addr)<<","<<ntohs(addrServer.sin_port) << std::endl;

	char sendBuf[BUFFER_SIZE] = { 0 };
	char recvBuf[BUFFER_SIZE] = { 0 };
	sendto(client, sendBuf, strlen(sendBuf) + 1, 0, (sockaddr*)&addrServer, len);
	int ret = recvfrom(client, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrServer, &len);
	memcpy(&addrUser, recvBuf, sizeof(addrUser));

	hSendThread = (HANDLE)_beginthreadex(NULL, 0, SendMsg, (void*)&client, 0, NULL);
	hRecvThread = (HANDLE)_beginthreadex(NULL, 0, RecvMsg, (void*)&client, 0, NULL);

	if (hSendThread != NULL)
		WaitForSingleObject(hSendThread, INFINITE);
	if (hRecvThread != NULL)
		WaitForSingleObject(hRecvThread, INFINITE);

	closesocket(client);
	WSACleanup();
	return 0;
}

unsigned WINAPI SendMsg(void* arg)
{
	SOCKET client = *((SOCKET*)arg);
	char sendBuf[BUFFER_SIZE] = {0};
	while (true)
	{	
		memset(sendBuf, 0, BUFFER_SIZE);
		fgets(sendBuf, BUFFER_SIZE, stdin);
		if (sendBuf == NULL)
			continue;
		if (!strcmp(sendBuf, "Q\n") || !strcmp(sendBuf, "q\n"))
		{		
			isExit = true;
			break;
		}
		sendto(client, sendBuf, strlen(sendBuf), 0, (sockaddr*)&addrUser, lenSend);
	}
	return 0;
}

unsigned WINAPI RecvMsg(void* arg)
{
	SOCKET client = *((SOCKET*)arg);
	char recvBuf[BUFFER_SIZE] = {0};
	while (!isExit)
	{
		memset(recvBuf, 0, BUFFER_SIZE);		
		int ret = recvfrom(client, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrServer, &len);
		if (ret > 0) {
			std::cout << recvBuf << std::endl;
		}		
	}
	return 0;	
}

运行结果图片: 在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易17

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值