当前位置 : 主页 > 手机开发 > 其它 >

select服务器端模型封装——回调方式快速建立客户端

来源:互联网 收集:自由互联 发布时间:2021-06-19
#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
#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;
}
网友评论