服务端使用select模型处理多客户端

  • Post author:
  • Post category:其他


在这里插入图片描述
在这里插入图片描述

#include<WinSock2.h>

#include<Windows.h>

#include

#include<stdio.h>

#include

#pragma comment(lib,“ws2_32.lib”)

enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, 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;

};

std::vector g_client;

int process_solve(SOCKET _cSOCK)

{


//增加一个缓冲区

char szRecv[1024] = {};

//5.接收客户端新数据

int nLen = recv(_cSOCK, szRecv, sizeof(DataHeader), 0);

DataHeader

header = (DataHeader

)szRecv;

if (nLen <= 0)
{
    printf("客户端已退出!任务结束!");
    return -1;
}
switch (header->cmd){
case CMD_Login:
{
    recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
    Login *login = (Login*)szRecv;
    printf("收到命令:CMD_Login,数据长度:%d\nUserName:%s\nPassWord:%s\n", login->dataLength, login->username, login->password);
    //忽略判断用户密码是否正确的过程
    LoginResult ret;
    send(_cSOCK, (char *)&ret, sizeof(LoginResult), 0); //再发消息体

}
case CMD_Logout:
{

    recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
    Logout* logout = (Logout*)szRecv;
    printf("收到命令:CMD_Logout,数据长度:%d\nUserName:%s\n", logout->dataLength, logout->username);

    //忽略判断用户密码是否正确的过程
    LogoutResult let;
    send(_cSOCK, (char *)&let, sizeof(let), 0); //再发消息体
}
    break;
default:
{
           DataHeader header = { 0 };
           send(_cSOCK, (char *)&header.cmd, sizeof(header), 0);
}

    break;
}

}

int main()

{

WORD ver = MAKEWORD(2, 2);
WSADATA dat;
//WinSocket启动
WSAStartup(ver, &dat);

//1、建立一个socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //AF_INET创建一个IPV4的套接字,SOCK_STREAM面向数据流的,IPPROTO_TCP TCP
if (INVALID_SOCKET == _sock)
{
    printf("ERROR:建立失败!\n");
}
//2.绑定
sockaddr_in _sin = {};     //创建网络地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567); //Host to Network Short
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // IP地址 
if (bind(_sock, (sockaddr *)&_sin, sizeof(_sin)) == SOCKET_ERROR)
{
    printf("ERROR:绑定失败!\n");
}
else
{
    printf("服务器端绑定成功......\n");
}
//3.监听网络端口
if (listen(_sock, 5) == SOCKET_ERROR)//第二个参数为最大等待多少人可以同时连接
{
    printf("ERROR:监听失败!\n");
}
else
{
    printf("服务器端监听成功......\n");
}

while (1)
{
    //伯克利 socket
    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);

    for (int n = g_client.size() - 1; n >= 0; n--)
    {
        FD_SET(g_client[n], &fd_Read);
    }

    /*
    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
    int ret = select(_sock + 1, &fd_Read, &fd_Write, &fd_Exp, NULL);
    if (ret < 0)
    {
        printf("select任务结束!\n");
        break;
    }
    if (FD_ISSET(_sock, &fd_Read))
    {
        FD_CLR(_sock, &fd_Read);
        //4.等待接收客户端连接
        sockaddr_in clientAddr = {};
        int nAddrLen = sizeof(sockaddr_in);
        SOCKET _cSOCK = INVALID_SOCKET;

        _cSOCK = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen);
        if (_cSOCK == INVALID_SOCKET)
        {
            printf("ERROR:无效客户端SOCKET!\n");
        }
        g_client.push_back(_cSOCK);
        printf("新客户端加入:Socket=%d,IP = %s\n", (int)_cSOCK, inet_ntoa(clientAddr.sin_addr));//inet_ntoa(clientAddr.sin_addr)将接收到的IP地址转化为字符串
        
        
    }

    for (int n = 0; n < fd_Read.fd_count; n++)
    {
        if (process_solve(fd_Read.fd_array[n]) == -1)
        {
            auto iter = find(g_client.begin(), g_client.end(), process_solve(fd_Read.fd_array[n]));
            if (iter != g_client.end())
            {
                g_client.erase(iter);
            }
        }
    }
}

for (int n = g_client.size(); n >= 0; n--)
{
    //8.关闭自身的socket
    closesocket(g_client[n]);
}

//8.关闭自身的socket
closesocket(_sock);

//WinSocket关闭
WSACleanup();

system("pause");
return 0;

}



版权声明:本文为longyup原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。