假设两台主机分别位于两个局域网中,这两台主机不能直接通过TCP建立连接(TCP连接需要固定的ip和端口,通常路由器或者本机防火墙是不会将这个暴露在外的)
这时可以通过 UDP穿透来实现两台主机的跨局域网通信(当然前提是需要一台公网服务器)
(1)公网服务器开启一个UDP监听套接字
(2)主机1向公网服务器发送UDP报文,此时公网服务器就能知道主机1的ip和端口(此时的这个ip和端口是不会受到防火墙的限制的,因为是主机的主动行为),公网服务器存储该地址
(3)同样的,主机2向公网服务器发送UDP报文,此时公网服务器就能知道主机2的ip和端口,公网服务器存储该地址
(4)公网服务器收集到这两个地址以后,分别将对方的地址返还,公网服务器就可以关闭了
(5)主机1收到主机2的地址,主机2收到主机1的地址,只要它们不关闭套接字,就可以向对方发送UDP(UDP只管收,并不关心谁发的,发的啥)
服务器布设在远程linux服务器上 ip:47.113.150.167
局域网主机环境是window
//服务器代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 30
//using namespace std;
/*
* 服务器
*/
int main()
{
int serverSocket;
sockaddr_in serverAddress, clientAddress;
sockaddr_in clientAddress1, clientAddress2;
int flagAddress1 = 0, flagAddress2 = 0;
char message[BUF_SIZE];
int str_len;
socklen_t clientAddressLen;
serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
memset(&serverAddress, 0, sizeof(serverAddress));
memset(&clientAddress, 0, sizeof(serverAddress));
memset(&clientAddress1, 0, sizeof(serverAddress));
memset(&clientAddress2, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(8888);
bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
while (1)
{
clientAddressLen = sizeof(sockaddr);
std::cout << "等待接受来自客户端的UDP数据报....." << std::endl;
//接收消息
str_len = recvfrom(serverSocket, message, BUF_SIZE, 0, (sockaddr*)&clientAddress, &clientAddressLen);
//打印客户端信息
std::cout << "客户端地址:" << clientAddress.sin_addr.s_addr << std::endl;
std::cout << "客户端端口:" << clientAddress.sin_port << std::endl;
std::cout << "UDP内容:" << message << std::endl;
if (flagAddress1 == 0) {
memcpy(&clientAddress1, &clientAddress, sizeof(clientAddress));
flagAddress1 = 1;
}
else if (flagAddress2 == 0) {
memcpy(&clientAddress2, &clientAddress, sizeof(clientAddress));
flagAddress2 = 1;
}
if (flagAddress1 == 1 && flagAddress2 == 1) { //UDP双方准备就绪 将地址分别发给对方
sendto(serverSocket, (void*)&clientAddress1, sizeof(clientAddress1), 0, (struct sockaddr*)&clientAddress2, sizeof(clientAddress2));
sendto(serverSocket, (void*)&clientAddress2, sizeof(clientAddress2), 0, (struct sockaddr*)&clientAddress1, sizeof(clientAddress1));
break; //该服务器可以关闭了
}
}
close(serverSocket);
return 0;
}
//局域网主机端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include
#include
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main()
{
// 加载套接字库
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
// 创建套接字
SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in addrSrv, addTarget;
addrSrv.sin_addr.s_addr = inet_addr("192.168.147.128");
addrSrv.sin_port = htons(8888);
addrSrv.sin_family = AF_INET;
int len = sizeof(sockaddr);
char buff[100] = "hello i am client!";
//发送数据 获取目标主机UDP地址到addTarget
cout << sendto(sockCli, buff, strlen(buff), 0, (sockaddr*)&addrSrv, sizeof(sockaddr)) << endl;
cout << "等待服务器反馈!" << endl;
recvfrom(sockCli, (char*)&addTarget, sizeof(addTarget), 0, (sockaddr*)&addrSrv, &len);
cout <<"目标主机ip:" << addTarget.sin_addr.s_addr << endl;
cout << "目标主机端口:" << addTarget.sin_port << endl;
closesocket(sockCli);
system("pause");
return 0;
}