UDP 服务器和客户端实例,实现2个客户端通过UDP服务器打洞穿透

  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;
}
原文地址:https://www.cnblogs.com/yzhuang/p/12603840.html