zoukankan      html  css  js  c++  java
  • select服务器端模型封装——回调方式快速建立服务端

    #pragma once
    
    #ifndef WINSOCK2_H
    
        #define _WINSOCK_DEPRECATED_NO_WARNINGS 
        
        #include<WinSock2.h>
        #include<Windows.h>
        #pragma comment(lib, "ws2_32.lib")
    
    #endif
    #include<iostream>
    #include<thread>
    #include<vector>
    
    static bool ServerRun = true;
    static bool _isInit = false;    //WSA是否初始化
    
    struct ClientSock
    {
        SOCKET s;
        sockaddr_in addr;
    };
    
    class SelectServer
    {
    private:
        int _lastError;            //最后一次错误
        
        sockaddr_in _addr;        //服务器绑定的地址
        SOCKET _sServer;        //服务器监听套接字
        int _maxClientNum = 5;    //客户端最大连接数量
    
        std::vector<ClientSock> ClientList;    //客户端列表
    
        //初始化WSA,成功返回1,失败返回0
        int _Init();
    public:
        SelectServer(int m_port);
        ~SelectServer();
        //获取错误,返回错误码
        inline int getLastError();
        //设置绑定端口
        void setHost(int m_port);
        //绑定地址
        bool bindAddr();
        //设置最多连接客户端数量
        void setMaxClientNum(int n);
        //向客户端发送消息
        int sendToClient(int id,char *buf,int len);
        //向所有客户端发送消息
        void sendAllClient(char * buf, int len);
        //从客户端接收消息
        int recvFromClient(int id, char *buf, int len);
        //获取客户端列表长度
        int getClientListLen();
        //获取客户端地址
        sockaddr_in *getAddr(int id);
        //开始工作
        void start(
            std::function<void(int clientId, SelectServer *server)> const &AcceptMethod,
            std::function<bool (int clientId, SelectServer *server) > const &ReadMethod,
            std::function<void(int clientId, SelectServer *server)> const &WriteMethod,
            std::function<void(int clientId, SelectServer *server)> const &ExpMethod
        );
        //停止工作
        void quit();
        
    };
    #include "pch.h"
    #include "SelectServer.h"
    
    
    SelectServer::SelectServer(int m_port)
    {
        this->_Init();
        this->_lastError = 0;
    
        this->_addr.sin_port = htons(m_port);
        this->_addr.sin_family = AF_INET;
        this->_addr.sin_addr.S_un.S_addr = INADDR_ANY;
    
        this->_sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (INVALID_SOCKET == this->_sServer) {
            this->_lastError = WSAGetLastError();
            return;
        }
    
        //初始化WSA
        if (!_isInit) {
            if (this->_Init()) {
                _isInit = true;
            }
        }
    }
    
    
    SelectServer::~SelectServer()
    {
        for (std::vector<ClientSock>::iterator it = ClientList.begin(); it != ClientList.end();it++) {
            shutdown(it->s, 2);
            closesocket(it->s);
        }
        closesocket(this->_sServer);
        WSACleanup();
    }
    
    int SelectServer::_Init()
    {
        WSADATA wsadata;
        int ret = WSAStartup(MAKEWORD(2, 2), &wsadata);
        if (0 == ret) {
            return 1;
        }
        this->_lastError = ret;
        return 0;
    }
    
    inline int SelectServer::getLastError()
    {
        return WSAGetLastError();
    }
    
    void SelectServer::setHost(int m_port)
    {
        this->_addr.sin_port = htons(m_port);
    }
    
    bool SelectServer::bindAddr()
    {
        if (SOCKET_ERROR == bind(this->_sServer, (sockaddr *)&this->_addr, sizeof(sockaddr_in))) {
            this->_lastError = WSAGetLastError();
            return false;
        }
        if (SOCKET_ERROR == listen(this->_sServer, this->_maxClientNum)) {
            this->_lastError = WSAGetLastError();
            return false;
        }
        return true;
    }
    
    void SelectServer::setMaxClientNum(int n)
    {
        this->_maxClientNum = n;
    }
    
    int SelectServer::sendToClient(int id,char * buf, int len)
    {
        if (id < 0 || id >= ClientList.size()) {
            return -1;
        }
        return send(ClientList[id].s, buf, len, NULL);
    }
    
    void SelectServer::sendAllClient(char * buf, int len)
    {
        for (std::vector<ClientSock>::iterator it = ClientList.begin(); it != ClientList.end(); it++)
        {
            send(it->s, buf, len, 0);
        }
    }
    
    int SelectServer::recvFromClient(int id, char * buf, int len)
    {
        return recv(ClientList[id].s, buf, len, 0);
    }
    
    int SelectServer::getClientListLen()
    {
        return ClientList.size();
        return 0;
    }
    
    sockaddr_in *SelectServer::getAddr(int id)
    {
        if (id < 0 && id >= ClientList.size()) {
            return NULL;
        }
        return &ClientList[id].addr;
    }
    
    void SelectServer::start(
        std::function<void(int clientId, SelectServer *server)> const &AcceptMethod,
        std::function<bool(int clientId, SelectServer *server)> const &ReadMethod,
        std::function<void(int clientId, SelectServer *server)> const &WriteMethod,
        std::function<void(int clientId, SelectServer *server)> const &ExpMethod
    ){
        std::cout << "Start" << std::endl;
        fd_set fdRead, fdWrite, fdExp;
    
        //开始select轮询
        while (true)
        {
            if (!ServerRun) {
                return;
            }
    
            FD_ZERO(&fdRead);
            FD_ZERO(&fdWrite);
            FD_ZERO(&fdExp);
    
            FD_SET(this->_sServer, &fdRead);
            FD_SET(this->_sServer, &fdWrite);
            FD_SET(this->_sServer, &fdExp);
    
            for (int i = 0; i < ClientList.size(); i++)
            {
                FD_SET(ClientList[i].s, &fdRead);
            }
    
            int ret = select(this->_sServer, &fdRead, &fdWrite, &fdExp, NULL);
            if (ret == -1) {
                std::cout << WSAGetLastError() << std::endl;
            }
            
            //返回错误退出
            if (ret < 0){
                break;
            }
            //fd_set没有变化
            else if (ret == 0) {
                continue;
            }
            
            if (FD_ISSET(this->_sServer, &fdRead)) {
                FD_CLR(this->_sServer, &fdRead);
                ClientSock client;
                int len = sizeof(client.addr);
                client.s = accept(this->_sServer, (sockaddr *)&client.addr, &len);
                if (SOCKET_ERROR == client.s) {
                    continue;
                }
                ClientList.push_back(client);
                AcceptMethod(ClientList.size()-1, this);
            }
            for (int i = 0; i < ClientList.size(); i++)
            {
                if (FD_ISSET(ClientList[i].s, &fdRead)) {
                    FD_CLR(ClientList[i].s, &fdRead);
                    if (ReadMethod != NULL) {
                        if (!ReadMethod(i,this)) {
                            ClientList.erase(ClientList.begin() + i);
                            continue;
                        }
                    }
                }
                if (FD_ISSET(ClientList[i].s, &fdWrite)) {
                    FD_CLR(ClientList[i].s, &fdWrite);
                    if (WriteMethod != NULL) {
                        WriteMethod(i, this);
                        continue;
                    }
                }
                if (FD_ISSET(ClientList[i].s, &fdExp)) {
                    FD_CLR(ClientList[i].s, &fdExp);
                    if (ExpMethod != NULL) {
                        ExpMethod(i, this);
                        continue;
                    }
                }
            }
        }
    }
    
    void SelectServer::quit()
    {
        ServerRun = false;
    }

    使用样例:

    // select库.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    #include "pch.h"
    #include "SelectServer.h"
    #include <iostream>
    #include <vector>
    
    void acceptFun(int clientId, SelectServer *server)
    {
        std::cout << "客户端 "<<inet_ntoa(server->getAddr(clientId)->sin_addr)<<' '<<ntohs(server->getAddr(clientId)->sin_port)<<" 连接" << std::endl;
    }
    bool readFun(int clientId, SelectServer *server)
    {
        char buf[256];
        //int len = recv(client.s, buf, 256, 0);
        int len =  server->recvFromClient(clientId, buf, 256);
        if (len < 0) {
            std::cout << "客户端 " << inet_ntoa(server->getAddr(clientId)->sin_addr) << ' ' << ntohs(server->getAddr(clientId)->sin_port) << " 断开连接" << std::endl;
            return false;
        }
        buf[len] = 0;
        std::cout << "客户端 " << inet_ntoa(server->getAddr(clientId)->sin_addr) << ' ' << ntohs(server->getAddr(clientId)->sin_port) << "发送数据:" << std::endl;
        std::cout << buf << std::endl;
        return true;
    }
    
    int main()
    {
        SelectServer *server = new SelectServer(2059);
        //server->setHost(2059);
        
        if (server->bindAddr()==-1) {
            std::cout << "监听失败 Error code: " << WSAGetLastError() << std::endl;
            system("pause<nul");
            return 0;
        }
        std::cout << "等待连接" << std::endl;
        server->start(
            acceptFun,
            readFun,
            NULL,
            NULL
        );
        std::cout << WSAGetLastError() << std::endl;
        system("pause>nul");
        return 0;
    }
  • 相关阅读:
    一片非常有趣的文章 三分钟读懂TT猫分布式、微服务和集群之路
    mysql+mycat搭建稳定高可用集群,负载均衡,主备复制,读写分离
    mycat读写分离+垂直切分+水平切分+er分片+全局表 测试
    LVS Nginx HAProxy 优缺点
    nginx map配置根据请求头不同分配流量到不同后端服务
    Javamail发送邮件
    java发送html模板的高逼格邮件
    学习openresty时,nginx的一个坑
    mysql数据导出golang实现
    mysql支持原生json使用说明
  • 原文地址:https://www.cnblogs.com/HadesBlog/p/11636185.html
Copyright © 2011-2022 走看看