小张在努力写代码

小张在努力写代码

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


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

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

1.1 代码编写

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

完整代码如下:

/*udpServer.cc*/
#include "udpServer.hpp"
#include <fstream>
#include <memory>
#include <string>
#include <map>

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;
}
/*udpServer.hpp*/
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <string.h>
#include <cerrno>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

namespace Server
{
    // using func_t = std::function<void(std::string, uint16_t, std::string)>;
    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;
    };
}
/*udpClient.hpp*/
#pragma once
#include <iostream>
#include <string>
#include <string.h>
#include <cerrno>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

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
/*udpCilent.cc*/
#include "udpClient.hpp"
#include <string>
#include <memory>

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 测试

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

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

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

/* udpServer.hpp */
#pragma once

#include <string>
#include <iostream>
#include <functional>
#include <cassert>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>


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 <iostream>
#include <memory>

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 <string>
#include <iostream>
#include <functional>
#include <cassert>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

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 <memory>

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

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

3. 网络聊天室

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

/* User.hpp */
#pragma once

#include <string>
#include <map>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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 <string>
#include <iostream>
#include <functional>
#include <cassert>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>


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 <iostream>
#include <memory>

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 <string>
#include <iostream>
#include <functional>
#include <cassert>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

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 <memory>

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

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

4. Windows客户端和Linux服务端

/* Window端代码,编译环境 Windows11, Visual Studio 2022 */

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)


#include <iostream>
#include <string>
#include <cstring>
#include <WinSock2.h>

#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 <string>
#include <map>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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 <string>
#include <iostream>
#include <functional>
#include <cassert>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>


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 <iostream>
#include <memory>

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

本节完…

02-27 08:49