UDP穿透

1 UDP穿透作用

假设两台主机分别位于两个局域网中,这两台主机不能直接通过TCP建立连接(TCP连接需要固定的ip和端口,通常路由器或者本机防火墙是不会将这个暴露在外的)

这时可以通过 UDP穿透来实现两台主机的跨局域网通信(当然前提是需要一台公网服务器)

2 UDP穿透原理

(1)公网服务器开启一个UDP监听套接字

(2)主机1向公网服务器发送UDP报文,此时公网服务器就能知道主机1的ip和端口(此时的这个ip和端口是不会受到防火墙的限制的,因为是主机的主动行为),公网服务器存储该地址

(3)同样的,主机2向公网服务器发送UDP报文,此时公网服务器就能知道主机2的ip和端口,公网服务器存储该地址

(4)公网服务器收集到这两个地址以后,分别将对方的地址返还,公网服务器就可以关闭了

(5)主机1收到主机2的地址,主机2收到主机1的地址,只要它们不关闭套接字,就可以向对方发送UDP(UDP只管收,并不关心谁发的,发的啥)

3 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;

}

你可能感兴趣的:(udp,网络,网络协议)