• 【计算机网络】UDP的应用场景


    需要云服务器等云产品来学习Linux的同学可以移步/–>腾讯云<–/官网,轻量型云服务器低至112元/年,优惠多多。(联系我有折扣哦)


    前言:在上一篇文中,我们使用了udp协议进行socket编程,实现了简单的客户端和服务端通信,那么实际上,这里可以添加一些业务代码,下面是几个例子

    1. 翻译功能(网络翻译)

    1.1 代码编写

    在之前的代码基础上,我们需要修改的只有服务端业务逻辑部分和客户端run的部分

    完整代码如下:

    /*udpServer.cc*/
    #include "udpServer.hpp"
    #include 
    #include 
    #include 
    #include 
    
    using namespace Server;
    
    // demo1:网络翻译
    /**
     * 业务逻辑:
     * 1. 在服务器启动之前加载数据文件(对应的翻译),然后启动服务端
     * 2. 服务端接收客户端发来的原文,在数据文件中找到对应的翻译内容
     * 3. 将翻译内容发回给客户端
     * 4. 服务端提供热更新数据文件
     */
    
    // 配置文件:可以提供一个转门的配置文件存放,这里为了方便,就直接写死
    const std::string filename = "dict.txt";
    const std::string filepath = "./";
    std::map<std::string, std::string> dict;
    
    static bool cutString(const std::string &line, std::string *s1, std::string *s2, const std::string &sep)
    {
        auto pos = line.find(sep);
        if (pos == std::string::npos)
            return false;
        *s1 = line.substr(0, pos);
        *s2 = line.substr(pos + sep.size());
        return true;
    }
    
    static void initDict()
    {
        std::string file = filepath + filename;
        std::ifstream in(file.c_str(), std::ios::binary); // 二进制读取的方式打开文件
        if (!in.is_open())
        {
            std::cerr << "open file error " << errno << " : " << strerror(errno) << std::endl;
            exit(1);
        }
    
        std::string line;
        std::string key, value;
        while (getline(in, line))
        {
            if (cutString(line, &key, &value, ":"))
            {
                dict.insert(std::make_pair(key, value));
            }
        }
    
        in.close();
        std::cout << "load dict success" << std::endl;
    }
    
    void handleMessage(int sockfd, std::string clientIp, uint16_t clientPort, std::string message)
    {
        // 在这里做一些服务端需要完成的任务
        std::string response_message;
        auto it = dict.find(message);
        if(it != dict.end())
            response_message = it->second;
        else
            response_message = "can not find this word";
        // 将结果返回给客户端
        struct sockaddr_in client;
        bzero(&client, sizeof(client));
        client.sin_family = AF_INET;
        client.sin_port = htons(clientPort);
        client.sin_addr.s_addr = inet_addr(clientIp.c_str());
        
        sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }
    
    //  调用的指令 :./udpServer port
    int main(int argc, char *argv[])
    {
        // 解析指令
        if (argc != 2)
        {
            Usage();
            exit(USAGE_ERR);
        }
        uint16_t port = atoi(argv[1]);
    
        // 加载配置文件
        initDict();
    
        // 创建对象,进行通信
        std::unique_ptr<udpServer> usvr(new udpServer(handleMessage, port));
    
        usvr->initServer(); // 初始化服务进程
        usvr->start();      // 开始监听
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    /*udpServer.hpp*/
    #pragma once
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    namespace Server
    {
        // using func_t = std::function;
        typedef std::function<void(int, std::string, uint16_t, std::string)> func_t;
    
        static void Usage()
        {
            std::cout << "\nUsage:\n\t./udpServer local_port\n\n";
        }
        enum // 枚举出错类型
        {
            USAGE_ERR = 1,
            SOCKET_ERR,
            BIND_ERR
        };
        const static std::string defaultIP = "0.0.0.0";
        const int gnum = 1024; // 处理任务的缓冲区大小
        class udpServer
        {
        public:
            udpServer(const func_t &cb, const uint16_t &port, const std::string &ip = defaultIP)
                :_callback(cb), _port(port), _ip(ip) {}
            // 这里初始化要做的事情有两件: 1. 创建sockfd 2.bind端口号和ip
            void initServer()
            {
                _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 参数1:这里使用AF_INET表示使用IPv4进行网络通信;参数2:我们这里使用UDP策略;参数3:这里使用0表示默认
                if (_sockfd == -1)                        // 差错处理
                {
                    std::cerr << "socket error " << errno << strerror(errno) << std::endl;
                    exit(SOCKET_ERR);
                }
                // std::cout << "socket sucess : " << _sockfd << std::endl;
    
                // 在当前函数的栈帧上创建一个local对象,设置相关属性,然后将相关属性bind到系统内核中
                struct sockaddr_in local;      // 这里struct sockaddr_in类型需要头文件arpa/inet.h
                bzero(&local, sizeof(local));  // 在填充数据之前首先将对象内部元素清空,这里使用bzero
                local.sin_family = AF_INET;    // 设定协议家族
                local.sin_port = htons(_port); // 设置端口号,这里端口号需要首先转换成网络端口号
                // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 设置ip,这里的ip是string类型,但是实际在传输的时候使用的是整型,所以需要转换,这里使用inet_addr
                local.sin_addr.s_addr = INADDR_ANY;
                // inet_addr的作用有两个: 1.string -> uint32_t; 2. htonl()
                int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); // 将local设置到内核中,即bind
                if (n == -1)
                {
                    std::cerr << "bind error " << errno << strerror(errno) << std::endl;
                    exit(BIND_ERR);
                }
                // 至此初始化的操作完成
            }
            void start() // 让服务器开始跑起来
            {
                // 服务器的本质是一个死循环,在循环内部处理收到的任务
                char buffer[gnum];
                while (true)
                {
                    // 1. 读取数据
                    struct sockaddr_in peer; // 定义一个变量用于接收数据
                    socklen_t len = sizeof(peer);
                    ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                    // a. 数据是什么 b. 谁发的
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        std::string clientIp = inet_ntoa(peer.sin_addr); // 转换网络字节序, 点分十进制
                        uint16_t clientPort = ntohs(peer.sin_port);
                        std::string message = buffer;
    
                        std::cout << clientIp << "[" << clientPort << "]# " << message << std::endl;
                        // 2. 处理任务
                        _callback(_sockfd, clientIp, clientPort, message);
                    }
                }
            }
    
        private:
            // 成员变量分析:作为一个服务端进程,我们首先需要一个端口号port和一个本地ip
            // 还需要有一个文件描述符sockfd,用于进行通信(网络通信是基于文件的,所以使用的都是文件的一套内容,包括fd)
            int _sockfd;     // socket文件描述符
            std::string _ip; // 本地ip
            uint16_t _port;  // 服务进程端口号
    
            func_t _callback;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    /*udpClient.hpp*/
    #pragma once
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    namespace Client
    {
        enum // 枚举出错类型
        {
            USAGE_ERR = 1,
            SOCKET_ERR,
            BIND_ERR,
        };
        class udpClient
        {
        public:
            udpClient(const std::string &serverIp, const uint16_t &serverPort)
                : _serverIp(serverIp), _serverPort(serverPort), _sockfd(-1), _quit(false) {}
            void initClient()
            {
                // 1.创建套接字
                _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                if (_sockfd == -1)
                {
                    std::cerr << "socket error: " << errno << " : " << strerror(errno) << std::endl;
                    exit(SOCKET_ERR);
                }
                std::cout << "socket success: " << _sockfd << std::endl;
            }
            void run()
            {
                struct sockaddr_in server;
                memset(&server, 0, sizeof server);
                server.sin_family = AF_INET;
                server.sin_addr.s_addr = inet_addr(_serverIp.c_str());
                server.sin_port = htons(_serverPort);
    
                std::string message;
    #define NUM 1024
                char buffer[NUM];
                while(!_quit)
                {
                    std::cout << "Please Enter# ";
                    std::cin >> message;
                    // 1. 向服务端发送
                    sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
    
                    // 2. 接收服务端返回的结果                
                    struct sockaddr_in peer; // 用于接收发送端的相关信息
                    socklen_t len = sizeof peer;
                    ssize_t n = recvfrom(_sockfd, buffer, sizeof buffer, 0, (struct sockaddr *)&peer, &len);
                    if(n >= 0) buffer[n] = 0;
                    std::cout << "服务器翻译结果# " << buffer << std::endl;
                }
            }
    
        private:
            int _sockfd;           // 套接字
            std::string _serverIp; // 服务端IP
            uint16_t _serverPort;  // 服务端端口号
            bool _quit;            // 客户端退出标志
        };
    } // namespace Client
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    /*udpCilent.cc*/
    #include "udpClient.hpp"
    #include 
    #include 
    
    using namespace Client;
    static void Usage(std::string proc)
    {
        std::cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
    }
    
    // ./udpClient server_ip server_port
    int main(int argc, char *argv[])
    {
        // 解析指令
        if (argc != 3)
        {
            Usage(argv[0]);
            exit(USAGE_ERR);
        }
        std::string serverIp = argv[1];
        uint16_t serverPort = atoi(argv[2]);
    
        std::unique_ptr<udpClient> ucli(new udpClient(serverIp, serverPort));
        ucli->initClient();
        ucli->run();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    1.2 测试

    image-20240220193734778

    2. 命令行解析(远程shell)

    实际上命令行解析需要做的事情就是客户端发送命令行数据,然后服务器接收,进行解析处理,然后将结果再发送回客户端

    /* udpServer.hpp */
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    namespace Server
    {
        using func_t = std::function<void(int, std::string, uint16_t, std::string)>;
        class udpServer
        {
        public:
            udpServer(func_t cp, uint16_t &port)
                : _callback(cp), _port(port), _sockfd(-1) {}
            void initServer()
            {
                // 1. 创建socket
                _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                if(_sockfd == -1)
                {
                    std::cerr << "socket error" << std::endl;
                    exit(1);
                }
                // 2. bind
                struct sockaddr_in local;
                bzero(&local, sizeof local);
                local.sin_family = AF_INET;
                local.sin_port = htons(_port);
                local.sin_addr.s_addr = INADDR_ANY;
                int n = bind(_sockfd, (sockaddr *)&local, sizeof local);
                assert(n == 0);
                (void)n;
            }
            void start()
            {
    #define NUM 1024
                char buffer[NUM];
                while(true)
                {
                    struct sockaddr_in peer;
                    socklen_t len = sizeof peer;
                    int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                    if(n > 0)
                    {
                        buffer[n] = 0;
                        std::string clientIP = inet_ntoa(peer.sin_addr);
                        uint16_t clientPort = ntohs(peer.sin_port);
    
                        std::cout << clientIP << "[" << clientPort << "]" << buffer << std::endl;
                        _callback(_sockfd, clientIP, clientPort, buffer);
                    }
                }
            }
        private:
            int _sockfd;
            uint16_t _port;
    
            func_t _callback;
        };
    }
    /* udpServer.cc */
    #include "udpServer.hpp"
    #include 
    #include 
    
    using namespace Server;
    
    static void remoteShell(int sockfd, std::string clientIp, uint16_t clientPort, std::string cmd)
    {
        // cmd : ls -a -l   
        std::string response;
        FILE* fp = popen(cmd.c_str(), "r");
        if(fp == nullptr) response = cmd + "exec fail";
        char line[1024];
        while(fgets(line, sizeof line, fp))
        {
            response += line;
        }
        pclose(fp);
        
        // 开始返回
        struct sockaddr_in client;
        client.sin_family = AF_INET;
        client.sin_addr.s_addr = inet_addr(clientIp.c_str());
        client.sin_port = htons(clientPort);
        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof client);
    }
    
    static void Usage(std::string proc)
    {
        std::cout << "\n\tUsage: " << proc << " local_port\n";
    }
    
    int main(int argc, char* argv[])
    {
        if(argc != 2)
        {
            Usage(argv[0]);
            exit(1);
        }
        uint16_t port = atoi(argv[1]);
        std::unique_ptr<udpServer> usvr(new udpServer(remoteShell, port));
        usvr->initServer();
        usvr->start();
        return 0;
    }
    /* udpClient.hpp */
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    namespace Client
    {
        class udpClient
        {
        public:
            udpClient(const std::string &serverIP, const uint16_t &serverPort)
                : _serverIP(serverIP), _serverPort(serverPort), _sockfd(-1)
            {
            }
            void initClient()
            {
                _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                if(_sockfd == -1)
                {
                    std::cerr << "create socket error" << std::endl;
                    exit(1);
                }
                // OS在发起通信的时候自动bind
            }
            void run()
            {
                struct sockaddr_in server;
                bzero(&server, sizeof server);
                server.sin_family = AF_INET;
                server.sin_addr.s_addr = inet_addr(_serverIP.c_str());
                server.sin_port = htons(_serverPort);
                char buffer[1024];
                std::string message;
                while(true)
                {
                    std::cout << "Enter# ";
                    std::getline(std::cin, message);
                    sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof server);
    
                    struct sockaddr_in peer;
                    bzero(&peer, sizeof peer);
                    socklen_t len = sizeof peer;
                    int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)& peer, &len);
                    if(n > 0) buffer[n] = 0;
                    std::cout << "服务器运行结果# \n" << buffer << std::endl;
                    buffer[0] = 0;//清空上次运行结果
                }
            }
            ~udpClient()
            {
            }
    
        private:
            int _sockfd;
            std::string _serverIP;
            uint16_t _serverPort;
        };
    }
    /* udpClient.cc */
    #include "udpClient.hpp"
    #include 
    
    using namespace Client;
    static void Usage(std::string proc)
    {
        std::cout << "\n\tUsage: " << proc << " local_ip local_port\n";
    }
    
    int main(int argc, char* argv[])
    {
        if(argc != 3)
        {
            Usage(argv[0]);
            exit(1);
        }
        std::string IP = argv[1];
        uint16_t Port = atoi(argv[2]);
        std::unique_ptr<udpClient> uclt(new udpClient(IP, Port));
        uclt->initClient();
        uclt->run();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202

    image-20240221194328190

    3. 网络聊天室

    对于网络聊天室,实现逻辑就是:首先需要在线用户的管理,当任意一个在线用户发送消息的时候,把消息广播给所有在线用户

    /* User.hpp */
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    class User
    {
    public:
        User(const std::string &ip, const uint16_t &port)
            : _ip(ip), _port(port)
        {
        }
        std::string ip() { return _ip; }
        uint16_t port() { return _port; }
    
    private:
        std::string _ip;
        uint16_t _port;
    };
    
    class OnlineUser
    {
    public:
        void addUser(const std::string &ip, const uint16_t &port)
        {
            std::string id = ip + "-" + std::to_string(port);
            _users.insert(std::make_pair(id, User(ip, port)));
        }
        void delUser(const std::string &ip, const uint16_t &port)
        {
            std::string id = ip + "-" + std::to_string(port);
            _users.erase(id);
        }
        bool isOnline(const std::string &ip, const uint16_t &port)
        {
            std::string id = ip + "-" + std::to_string(port);
            return _users.find(id) == _users.end() ? false : true;
        }
        void boardcastMessage(int sockfd, const std::string &ip, const uint16_t &port, std::string &massage) // 给当前在线的所有用户都发送消息(广播以实现群聊天)
        {
            for(auto &user : _users)
            {
                struct sockaddr_in client;
                client.sin_family = AF_INET;
                client.sin_port = htons(user.second.port());
                client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
                std::string s = "[" + ip + "-" + std::to_string(port) + "]# ";
                s += massage;
                sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr*)&client, sizeof client);
            }
        }
    private:
        std::map<std::string, User> _users;
    };
    /* udpServer.hpp */
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    namespace Server
    {
        using func_t = std::function<void(int, std::string, uint16_t, std::string)>;
        class udpServer
        {
        public:
            udpServer(func_t cp, uint16_t &port)
                : _callback(cp), _port(port), _sockfd(-1) {}
            void initServer()
            {
                // 1. 创建socket
                _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                if(_sockfd == -1)
                {
                    std::cerr << "socket error" << std::endl;
                    exit(1);
                }
                // 2. bind
                struct sockaddr_in local;
                bzero(&local, sizeof local);
                local.sin_family = AF_INET;
                local.sin_port = htons(_port);
                local.sin_addr.s_addr = INADDR_ANY;
                int n = bind(_sockfd, (sockaddr *)&local, sizeof local);
                assert(n == 0);
                (void)n;
            }
            void start()
            {
    #define NUM 1024
                char buffer[NUM];
                while(true)
                {
                    struct sockaddr_in peer;
                    socklen_t len = sizeof peer;
                    int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                    if(n > 0)
                    {
                        buffer[n] = 0;
                        std::string clientIP = inet_ntoa(peer.sin_addr);
                        uint16_t clientPort = ntohs(peer.sin_port);
    
                        std::cout << clientIP << "[" << clientPort << "]" << buffer << std::endl;
                        _callback(_sockfd, clientIP, clientPort, buffer);
                    }
                }
            }
        private:
            int _sockfd;
            uint16_t _port;
    
            func_t _callback;
        };
    }
    /* udpServer.cc */
    #include "udpServer.hpp"
    #include "User.hpp"
    #include 
    #include 
    
    using namespace Server;
    
    
    OnlineUser onlineUser;
    
    static void remoteMessage(int sockfd, std::string clientIp, uint16_t clientPort, std::string message)
    {
        // 在进入聊天室之前需要先输入online
        if(message == "online") onlineUser.addUser(clientIp, clientPort);
        if(message == "offline") onlineUser.delUser(clientIp, clientPort); // 退出聊天室
        if(onlineUser.isOnline(clientIp, clientPort))
        {
            //在线,进行消息的路由
            onlineUser.boardcastMessage(sockfd, clientIp, clientPort, message);
        }
        else
        {
            struct sockaddr_in client;
            bzero(&client, sizeof(client));
            client.sin_family = AF_INET;
            client.sin_port = htons(clientPort);
            client.sin_addr.s_addr = inet_addr(clientIp.c_str());
    
            std::string response = "你还没上线,请发送online上线";
            sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
        }
    }
    
    static void Usage(std::string proc)
    {
        std::cout << "\n\tUsage: " << proc << " local_port\n";
    }
    
    int main(int argc, char* argv[])
    {
        if(argc != 2)
        {
            Usage(argv[0]);
            exit(1);
        }
        uint16_t port = atoi(argv[1]);
        std::unique_ptr<udpServer> usvr(new udpServer(remoteMessage, port));
        usvr->initServer();
        usvr->start();
        return 0;
    }
    /* udpClient.hpp */
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    namespace Client
    {
        class udpClient
        {
        public:
            udpClient(const std::string &serverIP, const uint16_t &serverPort)
                : _serverIP(serverIP), _serverPort(serverPort), _sockfd(-1)
            {
            }
            void initClient()
            {
                _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                if(_sockfd == -1)
                {
                    std::cerr << "create socket error" << std::endl;
                    exit(1);
                }
                // OS在发起通信的时候自动bind
            }
            static void *readMessage(void *args)
            {
                int sockfd = *static_cast<int*>(args);
                pthread_detach(pthread_self());
                while(true)
                {
                    char buffer[1024];
                    struct sockaddr_in temp;
                    socklen_t temp_len = sizeof(temp);
                    size_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &temp_len);
                    if (n >= 0)
                        buffer[n] = 0;
                    std::cout << buffer << std::endl;
                }
    
                return nullptr;
            }
            void run()
            {
                pthread_create(&_reader, nullptr, readMessage, &_sockfd);
    
                struct sockaddr_in server;
                bzero(&server, sizeof server);
                server.sin_family = AF_INET;
                server.sin_addr.s_addr = inet_addr(_serverIP.c_str());
                server.sin_port = htons(_serverPort);
                char buffer[1024];
                std::string message;
                while(true)
                {
                    std::cout << "Enter# ";
                    std::getline(std::cin, message);
                    sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof server);
                }
            }
            ~udpClient()
            {
            }
    
        private:
            int _sockfd;
            std::string _serverIP;
            uint16_t _serverPort;
    
            pthread_t _reader;
        };
    }
    /* udpClient.cc */
    #include "udpClient.hpp"
    #include 
    
    using namespace Client;
    static void Usage(std::string proc)
    {
        std::cout << "\n\tUsage: " << proc << " local_ip local_port\n";
    }
    
    int main(int argc, char* argv[])
    {
        if(argc != 3)
        {
            Usage(argv[0]);
            exit(1);
        }
        std::string IP = argv[1];
        uint16_t Port = atoi(argv[2]);
        std::unique_ptr<udpClient> uclt(new udpClient(IP, Port));
        uclt->initClient();
        uclt->run();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280

    image-20240221185947428

    4. Windows客户端和Linux服务端

    /* Window端代码,编译环境 Windows11, Visual Studio 2022 */
    
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    
    #include 
    #include 
    #include 
    #include 
    
    #pragma comment(lib,"ws2_32.lib") // 表示需要链接库 ws2_32.lib
    
    using namespace std;
    
    // 写死端口号和ip
    uint16_t serverport = 8080;
    string serverip = "8.134.152.121";
    
    int main()
    {
    	// 创建WSAData对象并初始化
    	WSAData wsd; 
    	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    	{
    		cout << "WSAStartup Error = " << WSAGetLastError() << endl;
    		return 0;
    	}
    	else
    	{
    		cout << "WSAStartup Success" << endl;
    	}
    	// 创建socket
    	SOCKET csock = socket(AF_INET, SOCK_DGRAM, 0);
    	if (csock == SOCKET_ERROR)
    	{
    		cout << "socket Error = " << WSAGetLastError() << endl;
    		return 1;
    	}
    	else
    	{
    		cout << "socket success" << endl;
    	}
    	// 创建sockaddr_in对象进行通信
    	struct sockaddr_in server;
    	memset(&server, 0, sizeof(server));
    	server.sin_family = AF_INET;
    	server.sin_port = htons(serverport);
    	server.sin_addr.s_addr = inet_addr(serverip.c_str());
    #define NUM 1024
    	char inbuffer[NUM];
    	string line;
    	while (true)
    	{
    		cout << "Please Enter#";
    		getline(cin, line);
    
    		int n = sendto(csock, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));
    		if (n < 0)
    		{
    			cerr << "sendto error!" << endl;
    			break;
    		}
    		struct sockaddr_in peer;
    		int peerlen = sizeof(peer);
    		inbuffer[0] = 0;
    		n = recvfrom(csock, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);
    		if (n > 0)
    		{
    			inbuffer[n] = 0;
    			cout << "server 返回的消息是#" << inbuffer << endl;
    		}
    		else break;
    	}
    	// 回收相关资源
    	closesocket(csock);
    	WSACleanup();
    	return 0;
    }
    /* Linux端,编译环境 centOS7 g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)*/
    /* User.hpp */
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    class User
    {
    public:
        User(const std::string &ip, const uint16_t &port)
            : _ip(ip), _port(port)
        {
        }
        std::string ip() { return _ip; }
        uint16_t port() { return _port; }
    
    private:
        std::string _ip;
        uint16_t _port;
    };
    
    class OnlineUser
    {
    public:
        void addUser(const std::string &ip, const uint16_t &port)
        {
            std::string id = ip + "-" + std::to_string(port);
            _users.insert(std::make_pair(id, User(ip, port)));
        }
        void delUser(const std::string &ip, const uint16_t &port)
        {
            std::string id = ip + "-" + std::to_string(port);
            _users.erase(id);
        }
        bool isOnline(const std::string &ip, const uint16_t &port)
        {
            std::string id = ip + "-" + std::to_string(port);
            return _users.find(id) == _users.end() ? false : true;
        }
        void boardcastMessage(int sockfd, const std::string &ip, const uint16_t &port, std::string &massage) // 给当前在线的所有用户都发送消息(广播以实现群聊天)
        {
            for(auto &user : _users)
            {
                struct sockaddr_in client;
                client.sin_family = AF_INET;
                client.sin_port = htons(user.second.port());
                client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
                std::string s = "[" + ip + "-" + std::to_string(port) + "]# ";
                s += massage;
                sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr*)&client, sizeof client);
            }
        }
    private:
        std::map<std::string, User> _users;
    };
    /* udpServer.hpp */
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    namespace Server
    {
        using func_t = std::function<void(int, std::string, uint16_t, std::string)>;
        class udpServer
        {
        public:
            udpServer(func_t cp, uint16_t &port)
                : _callback(cp), _port(port), _sockfd(-1) {}
            void initServer()
            {
                // 1. 创建socket
                _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                if(_sockfd == -1)
                {
                    std::cerr << "socket error" << std::endl;
                    exit(1);
                }
                // 2. bind
                struct sockaddr_in local;
                bzero(&local, sizeof local);
                local.sin_family = AF_INET;
                local.sin_port = htons(_port);
                local.sin_addr.s_addr = INADDR_ANY;
                int n = bind(_sockfd, (sockaddr *)&local, sizeof local);
                assert(n == 0);
                (void)n;
            }
            void start()
            {
    #define NUM 1024
                char buffer[NUM];
                while(true)
                {
                    struct sockaddr_in peer;
                    socklen_t len = sizeof peer;
                    int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                    if(n > 0)
                    {
                        buffer[n] = 0;
                        std::string clientIP = inet_ntoa(peer.sin_addr);
                        uint16_t clientPort = ntohs(peer.sin_port);
    
                        std::cout << clientIP << "[" << clientPort << "]" << buffer << std::endl;
                        _callback(_sockfd, clientIP, clientPort, buffer);
                    }
                }
            }
        private:
            int _sockfd;
            uint16_t _port;
    
            func_t _callback;
        };
    }
    /* udpServer.cc */
    #include "udpServer.hpp"
    #include "User.hpp"
    #include 
    #include 
    
    using namespace Server;
    
    
    OnlineUser onlineUser;
    
    static void remoteMessage(int sockfd, std::string clientIp, uint16_t clientPort, std::string message)
    {
        // 在进入聊天室之前需要先输入online
        if(message == "online") onlineUser.addUser(clientIp, clientPort);
        if(message == "offline") onlineUser.delUser(clientIp, clientPort); // 退出聊天室
        if(onlineUser.isOnline(clientIp, clientPort))
        {
            //在线,进行消息的路由
            onlineUser.boardcastMessage(sockfd, clientIp, clientPort, message);
        }
        else
        {
            struct sockaddr_in client;
            bzero(&client, sizeof(client));
            client.sin_family = AF_INET;
            client.sin_port = htons(clientPort);
            client.sin_addr.s_addr = inet_addr(clientIp.c_str());
    
            std::string response = "你还没上线,请发送online上线";
            sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
        }
    }
    
    static void Usage(std::string proc)
    {
        std::cout << "\n\tUsage: " << proc << " local_port\n";
    }
    
    int main(int argc, char* argv[])
    {
        if(argc != 2)
        {
            Usage(argv[0]);
            exit(1);
        }
        uint16_t port = atoi(argv[1]);
        std::unique_ptr<udpServer> usvr(new udpServer(remoteMessage, port));
        usvr->initServer();
        usvr->start();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258

    本节完…

  • 相关阅读:
    [Qt网络编程]之UDP通讯的简单编程实现
    直播回顾 | 子芽&CCF TF:云原生场景下软件供应链风险治理技术浅谈
    Verifiable Secret Sharing
    在 VMware vSphere 中构建 Kubernetes 存储环境
    c++视觉处理---拉普拉斯金字塔和高斯金字塔
    改性三磷酸盐研究:Lumiprobe氨基-11-ddUTP
    【润学】计算机网络八股文英文版(2)
    Java垃圾收集机制
    Java中的Date类型、LocalTime类型、LocalDate类型、LocalDateTime类型有什么区别?
    基于费舍尔判别分析的故障与诊断(lunwen+文献综述+翻译及原文+MATLAB程序)
  • 原文地址:https://blog.csdn.net/weixin_63249832/article/details/136219385