• 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;
    }
  • 相关阅读:
    利用gcc的__attribute__编译属性section子项构建初始化函数表
    Linux调试
    使用C++ stringstream来进行数据类型转换
    UseConcMarkSweepGC
    Django 3.1 发布,异步支持增强
    网易云音乐的消息队列改造之路
    二维码预生成:码上营销的并发之痛
    源码 redis 分布式锁
    跨度实际上是用来计算排位(rank) 目标节点在跳跃表中的排位 有序集 排序计算
    为什么有序集合需要同时使用跳跃表和字典来实现?
  • 原文地址:https://www.cnblogs.com/yzhuang/p/12603840.html
Copyright © 2020-2023  润新知