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

在这里插入图片描述在这里插入图片描述
#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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值