UDP打洞的原理其实很简单,客户端A和B分别向服务器发送一条消息,这个时候服务记录下两个客户端的ip和port,转发给客户端,客户端就拿到了对方的地址信息,向对方发消息即可。
注意客户端互发消息的第一条数据可能会丢失,但是net会记录下地址信息,数据一来一回通道才算打通。还有如果第二个客户端如果在第一个客户端连接之后过了一段时间,第一个客户端的
地址信息可能已经不存在了,这个时间跟net设备有关系,跟客户端本身所在的计算机设置也有关系,可以让客户端A在收到B信息之前每隔几秒给服务器发送一个心跳保活。
看需求参考吧,直接上代码
头文件和公共部分定义
#include <Winsock2.h> #include <stdio.h> #include <string> #include <vector> #include <iostream> #pragma comment(lib, "ws2_32.lib") using namespace std; enum STATE{ empty, logon, wait, authed, waiteOther, otherConneted, }; struct ClientInfo{ u_long S_addr; USHORT sin_port; STATE state; string strAddr; ClientInfo(): S_addr(0), sin_port(0), state(empty){ } bool IsEmpty(){ return this->state == empty; } bool IsSame(u_long addr, USHORT port){ return this->S_addr == addr && this->sin_port == port; } bool IsSame(SOCKADDR_IN cli){ return this->S_addr == cli.sin_addr.S_un.S_addr && this->sin_port == cli.sin_port; } };
服务端代码
void Server(){ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(1, 1); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return; } if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return; } //创建套接字 SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0); //创建地址结构体. SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(6000); //绑定套接字和地址. bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR)); char recvBuf[100]; char tempBuf[200]; SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); ClientInfo info[2]; while (1){ //接收数据. recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrClient, &len); if ('q' == recvBuf[0]){ sendto(sockSrv, "q", 1, 0, (SOCKADDR *)&addrClient, sizeof(SOCKADDR)); printf("chat end ! "); break; } sprintf_s(tempBuf, "%s say: %s", inet_ntoa(addrClient.sin_addr), recvBuf); printf("%s ", tempBuf); string strRecv = string(recvBuf); if (strRecv == "logon"){ string buff; if (info[0].IsSame(addrClient)){ buff = "pls auth0"; } else if (info[1].IsSame(addrClient)){ buff = "pls auth1"; } else{ if (info[0].IsEmpty()){ info[0].S_addr = addrClient.sin_addr.S_un.S_addr; info[0].sin_port = addrClient.sin_port; info[0].strAddr = string(inet_ntoa(addrClient.sin_addr)); info[0].state = wait; buff = "pls auth0"; } else{ info[1].S_addr = addrClient.sin_addr.S_un.S_addr; info[1].sin_port = addrClient.sin_port; info[1].strAddr = string(inet_ntoa(addrClient.sin_addr)); info[1].state = wait; buff = "pls auth1"; } } sendto(sockSrv, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR)); } else if (strRecv == "auth0"){ if (info[0].IsSame(addrClient) && info[0].state == wait){ info[0].state = authed; //string buff = "you logon success"; //sendto(sockSrv, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR)); } else { string buff = "you auth info error"; sendto(sockSrv, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR)); } } else if (strRecv == "auth1"){ if (info[1].IsSame(addrClient) && info[1].state == wait){ info[1].state = authed; string buff = "client "; buff += info[0].strAddr; buff += " "; buff += std::to_string(info[0].sin_port); //string buff = "you logon success, you can send data to server now"; sendto(sockSrv, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR)); buff.clear(); buff = "client "; buff += info[1].strAddr; buff += " "; buff += std::to_string(info[1].sin_port); addrClient.sin_addr.S_un.S_addr = info[0].S_addr; addrClient.sin_port = info[0].sin_port; //string buff = "you logon success, you can send data to server now"; sendto(sockSrv, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR)); } else { string buff = "you auth info error"; sendto(sockSrv, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR)); } } else { printf("recv data from[IP:%s,PORT:%d],data[%s] ", inet_ntoa(addrClient.sin_addr), addrClient.sin_port, recvBuf); string buff = "server recieved success!"; sendto(sockSrv, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR)); } } //关闭套接字. closesocket(sockSrv); //关闭套接字库. WSACleanup(); }
客户端部分:
void Client(){ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(1, 1); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return; } if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return; } SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0); SOCKADDR_IN addrClient; addrClient.sin_addr.S_un.S_addr = inet_addr("111.229.152.192"); addrClient.sin_family = AF_INET; addrClient.sin_port = htons(6000); char recvBuf[100]; char sendBuf[100]; char tempBuf[200]; STATE state = empty; int num = 0; int len = sizeof(SOCKADDR); while (1){ if (state == otherConneted){ printf("please input date:"); gets_s(sendBuf); sendto(sockClient, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrClient, len); } else if (state == empty){ string buff = "logon"; sendto(sockClient, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, len); } else if (state == wait){ string buff; if (num == 0) buff = "auth0"; else buff = "auth1"; sendto(sockClient, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, len); } else if (state == waiteOther){ string buff; if (num == 0) buff = "i am auth0"; else buff = "i am auth1"; sendto(sockClient, buff.c_str(), buff.size() + 1, 0, (SOCKADDR*)&addrClient, len); printf("now send connect data to new client, IP:[%s],PORT[%d],buff[%s] ", inet_ntoa(addrClient.sin_addr), addrClient.sin_port, buff.c_str()); } recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len); sprintf_s(tempBuf, "%s say: %s", inet_ntoa(addrClient.sin_addr), recvBuf); printf("%s ", tempBuf); string recvData(recvBuf); if (recvData == "pls auth0"){ num = 0; state = wait; } else if (recvData == "pls auth1"){ num = 1; state = wait; } else if (recvData == "i am auth0" || recvData == "i am auth1"){ if (recvData == "i am auth0" && num == 1){ state = otherConneted; } else if (recvData == "i am auth1" && num == 0){ state = otherConneted; } } else if (recvData.find_first_of(" ") != string::npos){ vector<string> vecData; string sData(recvData); while (sData.find(" ") != string::npos){ string tem = sData.substr(0, sData.find(" ")); vecData.push_back(tem); printf("%s ", tem.c_str()); sData = sData.substr(sData.find(" ") + 1, sData.length()); } printf("%s ", sData.c_str()); vecData.push_back(sData); if (vecData.size() == 3 && vecData[0] == "client"){ state = waiteOther; addrClient.sin_addr.S_un.S_addr = inet_addr(vecData[1].c_str()); addrClient.sin_port = atoi(vecData[2].c_str()); printf("now connect new client IP:[%s],PORT[%d] ", inet_ntoa(addrClient.sin_addr), addrClient.sin_port); } } } closesocket(sockClient); WSACleanup(); }
程序入口
int main(){ #ifndef _CLIENT Server(); #else Client(); #endif return 0; }