zoukankan      html  css  js  c++  java
  • 封装Server类和Client类

    服务器端:

    EasyTcpServer.hpp

    #ifndef _EasyTcpServer_hpp_
    #define _EasyTcpServer_hpp_
    
    #ifdef _WIN32
    #define WIN32_LEAN_AND_MEAN
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    #include<WinSock2.h>
    #include<Windows.h>
    #pragma comment(lib,"ws2_32.lib")
    #else
    #include<unistd.h>
    #include<arpa/inet.h>
    #include<string.h>
    
    #define SOCKET int
    #define INVALID_SOCKET (SOCKET)(-0)
    #define SOCKET_ERROR           (-1)
    #endif
    #include<stdio.h>
    #include<vector>
    #include "MessageHeader.hpp"
    
    class EasyTcpServer
    {
    private:
        SOCKET _sock;
        std::vector<SOCKET> g_client;  //容器里装的是所有的客户端
    
    public :
        EasyTcpServer()
        {
            _sock = INVALID_SOCKET;//将Socket设置为一个无效的套接字
        }
        virtual ~EasyTcpServer()
        {
            Close();
        }
        //初始化Socket
        SOCKET InitSock()
        {
            //启动Win Socket2.x环境
    #ifdef _WIN32
            WORD ver = MAKEWORD(2, 2);
            WSADATA dat;
            WSAStartup(ver, &dat);
    #endif
            //1.建立一个socket
            if (INVALID_SOCKET != _sock)  // 如果当前的socket并不是无效的,则将其关闭
            {
                printf("<socket=%d>关闭旧连接......
    ", _sock);
                Close();
            }
            _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 新建一个socket,此时的Socket为有效
            if (INVALID_SOCKET == _sock)
            {
                printf("ERROR:建立失败!
    ");
            }
            else{
                printf("客户端绑定成功......
    ");
            }
            return _sock;
        }
        //绑定端口号
        int Bind(const char* ip,unsigned short port)
        {
            /*if (_sock == INVALID_SOCKET)
            {
                InitSock();
            }*/
            sockaddr_in _sin = {};     //创建网络地址
            _sin.sin_family = AF_INET; //IPV4
            _sin.sin_port = htons(port); //Host to Network Short
    #ifdef _WIN32
            if (ip) //验证IP是否存在
            {
                _sin.sin_addr.S_un.S_addr = inet_addr(ip); // IP地址 
            }
            else{
                _sin.sin_addr.S_un.S_addr = INADDR_ANY;
            }
            
    #else 
            _sin.sin_addr.s_addr=INADDR_ANY;
    #endif
            int ret = bind(_sock, (sockaddr *)&_sin, sizeof(_sin));
            if (SOCKET_ERROR == ret)
            {
                printf("错误:绑定网络端口<%d>失败......
    ",port);
            }
            else
            {
                printf("绑定网络窗口<%d>成功......
    ", port);
            }
            return ret;
        }
        //监听端口号
        int Listen(int n)
        {
            //3.监听网络端口
            int ret = listen(_sock, n);//第二个参数为最大等待多少人可以同时连接
            if (SOCKET_ERROR == ret)
            {
                printf("<Socket=%d>错误:监听网络端口失败......
    ",_sock);
            }
            else
            {
                printf("<Socket=%d>服务器端监听成功......
    ",_sock);
            }
            return ret;
        }
        //接受客户端连接
        SOCKET Accept()
        {
            //4.等待接收客户端连接
            sockaddr_in clientAddr = {}; //创建客户端网络地址
            int nAddrLen = sizeof(sockaddr_in);
            SOCKET _cSOCK = INVALID_SOCKET; //将SOCKET的对象_cSOCK初始化无效的Socket
    #ifdef _WIN32
            _cSOCK = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen);//接收来自客户端传来的SOCKET
    #else
            _cSOCK = accept(_sock, (sockaddr *)&clientAddr, (socklen_t *)&nAddrLen);
    #endif
            if (_cSOCK == INVALID_SOCKET)
            {
                printf("<Socket=%d>错误,接收到无效客户端SOCKET!
    ",_sock);
            }
            else
            {
                //向客户端发送新来的用户
                NewUserJoin userJoin;
                SendDataToAll(&userJoin);
    
                //将服务端收到的socket存入容器中
                g_client.push_back(_cSOCK);
                printf("<Socket=%d>新客户端加入:Socket=%d,IP = %s
    ", _sock,(int)_cSOCK, inet_ntoa(clientAddr.sin_addr));//inet_ntoa(clientAddr.sin_addr)将接收到的IP地址转化为字符串
            }
            return _cSOCK;
        }
        //关闭socket
    
        void Close()
        {
            if (_sock != INVALID_SOCKET)
            {
    #ifdef _WIN32
                for (int n = (int)g_client.size(); n >= 0; n--)
                {
                    //8.关闭自身的socket
                    closesocket(g_client[n]);
                }
    
                //8.关闭自身的socket
                closesocket(_sock);
                //清除Windows socket环境
                WSACleanup();
    #else
                for (int n = (int)g_client.size(); n >= 0; n--)
                {
                    //8.关闭自身的socket
                    closesocket(g_client[n]);
                }
                close(_sock);
    #endif
            }
    
    
        }
        //处理网络消息
        bool onRun()
        {
            if (isRun())
            {
                fd_set fd_Read;
                fd_set fd_Write;
                fd_set fd_Exp;
    
                FD_ZERO(&fd_Read);//FD_ZERO 清空集合里的数据
                FD_ZERO(&fd_Write);
                FD_ZERO(&fd_Exp);
    
                FD_SET(_sock, &fd_Read);//FD_SET 可以进行操作的宏
                FD_SET(_sock, &fd_Write);
                FD_SET(_sock, &fd_Exp);
                SOCKET maxSock = _sock;
                for (int n = g_client.size() - 1; n >= 0; n--)
                {
                    FD_SET(g_client[n], &fd_Read);
                    if (maxSock < g_client[n])
                    {
                        maxSock = g_client[n];
                    }
                }
    
                /*
                select(
                _In_ int nfds,
                _Inout_opt_ fd_set FAR * readfds,
                _Inout_opt_ fd_set FAR * writefds,
                _Inout_opt_ fd_set FAR * exceptfds,
                _In_opt_ const struct timeval FAR * timeout
                );
                */
    
                //nfds是一个整数值,是指fd_set集合所有的描述符(select里的第一个参数)的范围(而不是数量)
                //既是所有文件描述符最大值+1
                timeval t = { 1, 0 };
    
                int ret = select(_sock + 1, &fd_Read, &fd_Write, &fd_Exp, &t); //系统提供select函数来实现多路复用输入/输出模型
                if (ret < 0)
                {
                    printf("select任务结束!
    ");
                    Close();
                    return false;
                }
                if (FD_ISSET(_sock, &fd_Read))
                {
                    FD_CLR(_sock, &fd_Read);
                    Accept();
                }
                for (int n = (int)g_client.size() - 1; n >= 0; n--)
                {
                    if (FD_ISSET(g_client[n], &fd_Read))
                    {
                        if (RecvData(g_client[n]) == -1)
                        {
                            auto iter = g_client.begin() + n;
                            if (iter != g_client.end())
                            {
                                g_client.erase(iter);
                            }
                        }
                    }
    
                }
                return true;
            }
            return false;
        }
        //是否工作中
        bool isRun()
        {
            return _sock != INVALID_SOCKET;
        }
        //接收数据 处理粘包和拆包
        int RecvData(SOCKET _cSOCK)
        {
            //增加一个缓冲区
            char szRecv[1024] = {};
            //5.接收客户端新数据
            int nLen = recv(_cSOCK, szRecv, sizeof(DataHeader), 0);
            DataHeader *header = (DataHeader*)szRecv;
    
            if (nLen <= 0)
            {
                printf("客户端已退出!任务结束!");
                return -1;
            }
            recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
            OnNetMsg(_cSOCK, header);
            return 0;
        }
        //响应网络消息
        virtual void OnNetMsg(SOCKET _cSOCK, DataHeader* header)
        {
            switch (header->cmd){
            case CMD_Login:
            {
                Login *login = (Login*)header;
                printf("收到客户端<Socket=%d>请求:CMD_Login,数据长度:%d
    UserName:%s
    PassWord:%s
    ", _cSOCK, login->dataLength, login->username, login->password);
                //忽略判断用户密码是否正确的过程
                LoginResult ret;
                send(_cSOCK, (char *)&ret, sizeof(LoginResult), 0); //再发消息体
    
            }
            case CMD_Logout:
            {
                Logout* logout = (Logout*)header;
                printf("收到命令:CMD_Logout,数据长度:%d
    UserName:%s
    ", logout->dataLength, logout->username);
    
                //忽略判断用户密码是否正确的过程
                LogoutResult let;
                send(_cSOCK, (char *)&let, sizeof(let), 0); //再发消息体
            }
                break;
            case CMD_New_User_Join:
            {
                NewUserJoin* UserJoin = (NewUserJoin*)header;
                printf("收到命令:CMD_Logout,数据长度:%d
    UserName:%s
    ", UserJoin->dataLength);
    
                //忽略判断用户密码是否正确的过程
                NewUserJoin let;
                send(_cSOCK, (char *)&let, sizeof(let), 0); //再发消息体
            }
                break;
            default:
            {
                DataHeader header = { 0 };
                send(_cSOCK, (char *)&header.cmd, sizeof(header), 0);
            }
    
                break;
            }
    
        }
        //单次发送指定socket数据
        int SendData(SOCKET _cSOCK, DataHeader* header)
        {
            if (isRun() && header)
            {
                return send(_cSOCK, (const char*)header, header->dataLength, 0);
            }
            return SOCKET_ERROR;
        }
        //群发指定socket数据
        void SendDataToAll(DataHeader* header)
        {
            if (isRun() && header)
            {
                for (int n = (int)g_client.size() - 1; n >= 0; n--)
                {
                    SendData(g_client[n], header);
                }
            }
        }
    };
    
    
    #endif

    MessageHeader.hpp

    enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, CMD_New_User_Join, CMD_ERROR };
    
    //包头
    struct DataHeader
    {
        short dataLength;
        short cmd;
    };
    //包体
    struct Login :public DataHeader
    {
        Login()
        {
            dataLength = sizeof(Login);
            cmd = CMD_Login;
        }
        char username[32];
        char password[32];
    };
    
    struct LoginResult :public DataHeader
    {
        LoginResult()
        {
            dataLength = sizeof(LoginResult);
            cmd = CMD_Login_Result;
            result = 0;
        }
        int result;
    };
    
    struct Logout :public DataHeader
    {
        Logout()
        {
            dataLength = sizeof(Logout);
            cmd = CMD_Logout;
        }
        char username[32];
    };
    
    struct LogoutResult :public DataHeader
    {
        LogoutResult()
        {
            dataLength = sizeof(LogoutResult);
            cmd = CMD_Logout_Result;
            result = 0;
        }
        int result;
    };
    
    struct NewUserJoin :public DataHeader
    {
        NewUserJoin()
        {
            dataLength = sizeof(LogoutResult);
            cmd = CMD_New_User_Join;
            sock = 0;
        }
        int sock;
    };

    Server.cpp

    #include "EasyTcpServer.hpp"
    
    int main()
    {
        EasyTcpServer server;
        server.InitSock();
        server.Bind(nullptr,4567);
        server.Listen(5);
    
    
        while (server.isRun())
        {
            server.onRun();
            //printf("空闲时间处理其他业务.......
    ");
        }
        server.Close();
        system("pause");
        return 0;
    }

    客户端:

    EasyTcpClient.hpp

    #ifndef _EasyTcpClient_hpp_
    #define _EasyTcpClient_hpp_
    #ifdef _WIN32
    
    #define WIN32_LEAN_AND_MEAN
    #include<WinSock2.h>
    #include<Windows.h>
    #pragma comment(lib,"ws2_32.lib")
    
    #else
    #include<unistd.h>
    #include<arpa/inet.h>
    #include<string.h>
    
    #define SOCKET int
    #define INVALID_SOCKET (SOCKET)(-0)
    #define SOCKET_ERROR           (-1)
    #endif
    #include<stdio.h>
    #include "MessageHeader.hpp"
    
    class EasyTcpClient
    {
        SOCKET _sock;
    public:
        EasyTcpClient()
        {
            _sock = INVALID_SOCKET;//将Socket设置为一个无效的套接字
        }
    
        //虚构函数
        virtual ~EasyTcpClient()
        {
            Close();
        }
    
        //初始化socket
        void initSocket()
        {
            //启动Win Socket2.x环境
        #ifdef _WIN32
            WORD ver = MAKEWORD(2, 2);
            WSADATA dat;
            WSAStartup(ver, &dat);
        #endif
            //1.建立一个socket
            if (INVALID_SOCKET != _sock)  // 如果当前的socket并不是无效的,则将其关闭
            {
                printf("<socket=%d>关闭旧连接......
    ",_sock);
                Close();
            }
            _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 新建一个socket
            if (INVALID_SOCKET == _sock)
            {
                printf("ERROR:建立失败!
    ");
            }
            else{
                printf("客户端绑定成功......
    ");
            }
        }
    
        //连接服务器
        int Connet(char* ip, unsigned short port)
        {
            if (INVALID_SOCKET == _sock)
            {
                initSocket();
            }
            sockaddr_in _sin = {};     //创建网络地址
            _sin.sin_family = AF_INET;
            _sin.sin_port = htons(port); //Host to Network Short
        #ifdef _WIN32
            _sin.sin_addr.S_un.S_addr = inet_addr(ip);//inet_addr("127.0.0.1"); // IP地址 
        #else
            _sin.sin_addr.s_addr = inet_addr(ip);
        #endif
            int ret = connect(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in));
            if (SOCKET_ERROR == ret)
            {
                printf("ERROR:连接失败!
    ");
            }
            else
            {
                printf("客户端连接成功......
    ");
            }
            return ret;
        }
    
        //关闭服务器
        void Close()
        {
            if (_sock != INVALID_SOCKET)
            {
                //关闭Win Socket2.x环境
        #ifdef _WIN32
                closesocket(_sock);
                //WinSocket关闭
                WSACleanup();
        #else
                close(_sock);
        #endif
                _sock = INVALID_SOCKET;
            }
    
        }
    
        //查询网络消息
        bool onRun()
        {
            if (isRun())
            {
                //伯克利 socket
                fd_set fd_Read;
                FD_ZERO(&fd_Read);//FD_ZERO 清空集合里的数据
                FD_SET(_sock, &fd_Read);//FD_SET 可以进行操作的宏
                timeval t = { 1, 0 };
                int ret = select(_sock, &fd_Read, 0, 0, &t);
                if (ret < 0)
                {
                    printf("<socket=%d>select任务结束1!", _sock);
                    return false;
                }
                if (FD_ISSET(_sock, &fd_Read))
                {
                    FD_CLR(_sock, &fd_Read);
    
                    if (RecvData(_sock) == -1)
                    {
                        printf("<socket = %d>select任务结束2!", _sock);
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
    
        //是否工作中
        bool isRun()
        {
            return _sock != INVALID_SOCKET;
        }
    
        //接收数据 处理粘包和拆包
        int RecvData(SOCKET _cSOCK)
        {
            //增加一个缓冲区
            char szRecv[1024] = {};
            //5.接收客户端新数据
            int nLen = recv(_cSOCK, szRecv, sizeof(DataHeader), 0);
            DataHeader *header = (DataHeader*)szRecv;
    
            if (nLen <= 0)
            {
                printf("与服务器断开连接!任务结束!");
                return -1;
            }
            recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
            OnNetMsg(header);
            return 0;
        }
    
        //响应网络消息
        void OnNetMsg(DataHeader* header)
        {
            switch (header->cmd){
            case CMD_Login_Result:
            {
                
                LoginResult *loginresult = (LoginResult*)header;
                printf("收到服务端消息请求:CMD_Login_Result,数据长度:%d
    ", loginresult->dataLength);
            }
                break;
            case CMD_Logout_Result:
            {
                LogoutResult* logoutresult = (LogoutResult*)header;
                printf("收到服务端消息请求:CMD_Logout_Result,数据长度:%d
    UserName:%s
    ", logoutresult->dataLength);
            }
                break;
            case CMD_New_User_Join:
            {
                NewUserJoin* newuserjoin = (NewUserJoin*)header;
                printf("收到服务端消息请求:CMD_New_User_Join,数据长度:%d
    UserName:%s
    ", newuserjoin->dataLength);
            }
                break;
            }
        }
    
        //发送数据
        int SendData(DataHeader* header)
        {
            if (isRun() && header)
            {
                return send(_sock, (const char*)header, header->dataLength, 0);
            }
            return SOCKET_ERROR;
        }
    
    private:
    
    };
    
    
    #endif

    MessageHeader.hpp

    enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, CMD_New_User_Join, CMD_ERROR };
    
    //包头
    struct DataHeader
    {
        short dataLength;
        short cmd;
    };
    //包体
    struct Login :public DataHeader
    {
        Login()
        {
            dataLength = sizeof(Login);
            cmd = CMD_Login;
        }
        char username[32];
        char password[32];
    };
    
    struct LoginResult :public DataHeader
    {
        LoginResult()
        {
            dataLength = sizeof(LoginResult);
            cmd = CMD_Login_Result;
            result = 0;
        }
        int result;
    };
    
    struct Logout :public DataHeader
    {
        Logout()
        {
            dataLength = sizeof(Logout);
            cmd = CMD_Logout;
        }
        char username[32];
    };
    
    struct LogoutResult :public DataHeader
    {
        LogoutResult()
        {
            dataLength = sizeof(LogoutResult);
            cmd = CMD_Logout_Result;
            result = 0;
        }
        int result;
    };
    
    struct NewUserJoin :public DataHeader
    {
        NewUserJoin()
        {
            dataLength = sizeof(LogoutResult);
            cmd = CMD_New_User_Join;
            sock = 0;
        }
        int sock;
    };

    client.cpp

    #include "EasyTcpClient.hpp"
    //线程库头文件
    #include<thread>
    
    void cmdThread(EasyTcpClient* client)
    {
        while (true)
        {
            char cmdBuf[256] = {};
            scanf("%s", cmdBuf);
            if (0 == strcmp(cmdBuf, "exit"))
            {
                client->Close();
                printf("退出!
    ");
                break;
            }
            else if (0 == strcmp(cmdBuf, "login"))
            {
                Login login;
                strcpy(login.username, "sutaoyu");
                strcpy(login.password, "sutaoyu01");
                client->SendData(&login);
            }
            else if (0 == strcmp(cmdBuf, "logout"))
            {
                Logout logout;
                strcpy(logout.username, "sutaoyu");
                client->SendData(&logout);
            }
            else{
                printf("不支持的命令!");
            }
        }
        
    }
    
    int main()
    {
        EasyTcpClient client;
        client.initSocket();
        client.Connet("127.0.0.1", 4567);
    
        //启动线程
        std::thread t1(cmdThread, &client);
        t1.detach();
    
        while (client.isRun())
        {
            client.onRun();
    
        }
        client.Close();
    
        printf("已退出!
    ");
        getchar();
        return 0;
    }
  • 相关阅读:
    kubernetes之配置Pod的Volume存储
    kubernetes之配置Pod的QoS
    kubernetes之定义容器和Pod的CPU资源
    kubernetes之定义容器和Pod的内存资源
    kubernetes之配置namespace中Pod的总数
    kubernetes之配置namespace中CPU最大最小值约束
    kubernetes之配置namespace中内存最大最小值约束
    kubernetes之配置namespace的默认CPU请求和限制
    Kubernetes之配置namespace的默认内存请求和限制
    信步漫谈之Jmeter—引入外部函数加解密报文
  • 原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/11007761.html
Copyright © 2011-2022 走看看