服务器和客户端简单的使用select网络模型(一)

服务器:

#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#define _AFXDLL
#include"afx.h"
#include<windows.h>
#include "WinSock2.h"
#include<iostream>
#include<vector>
#include<stdlib.h>

std::vector<SOCKET> g_clients;

enum CMD
{
    CMD_LOGIN,
    CMD_LOGIN_RESULT,
    CMD_LOGIN_OUT,
    CMD_LOGIN_OUT_RESULT,
    CMD_NEW_USER_JOIN,
    CMD_ERROR,
};

struct DataHeader
{
    short cmd;
    short dataLength;
};

struct Login :public DataHeader
{
    Login()
    {
        cmd = CMD_LOGIN;
        dataLength = sizeof(Login);
    }
    char userName[32];
    int passWord;
};

struct LoginOut :public DataHeader
{
    LoginOut()
    {
        cmd = CMD_LOGIN_OUT;
        dataLength = sizeof(LoginOut);
    }
    char userName[32];
};

struct LoginResult :public DataHeader
{
    LoginResult()
    {
        cmd = CMD_LOGIN_RESULT;
        dataLength = sizeof(LoginResult);
        result = 0;
    }
    int result;
};

struct LoginOutResult :public DataHeader
{
    LoginOutResult()
    {
        cmd = CMD_LOGIN_OUT_RESULT;
        dataLength = sizeof(LoginOutResult);
        result = 0;
    }
    int result;
};

struct NewUserJoin :public DataHeader
{
    NewUserJoin()
    {
        cmd = CMD_NEW_USER_JOIN;
        dataLength = sizeof(NewUserJoin);
        sock = 0;
    }
    int sock;
};

void InitSock()
{
    WSADATA wsaData;

    WORD wVersionRequested = MAKEWORD(2, 2);

    if (WSAStartup(wVersionRequested, &wsaData) != 0)
        return;

    if (LOBYTE(wsaData.wVersion) != 2 ||
        HIBYTE(wsaData.wVersion) != 2)
    {

        WSACleanup();
        return;
    }
}

int ProcessPart(SOCKET cSock)
{
    char szRecv[1024] = {};
    //接受客户端请求数据
    int nLen = recv(cSock, szRecv, sizeof(DataHeader), 0);
    DataHeader *header = (DataHeader*)szRecv;
    if (nLen <= 0)
    {
        printf("客户端<socket=%d>已退出,任务结束\n", cSock);
        return -1;
    }
    printf("收到命令:%d  数据长度:%d\n", header->cmd, header->dataLength);

    switch (header->cmd)
    {
        case CMD_LOGIN:
        {
            recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
            Login* login = (Login*)szRecv;
            printf("收到命令  用户名:%s密码%d\n", login->userName, login->passWord);
            LoginResult ret = {};
            ret.result = 0;

            send(cSock, (const char *)&ret, sizeof(LoginResult), 0);
            break;
        }
        case CMD_LOGIN_OUT:
        {
            recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
            LoginOut* loginOut = (LoginOut*)szRecv;
            printf("收到命令: 数据长度:%d 用户名:%s密码%s\n", loginOut->dataLength, loginOut->userName);

            LoginOutResult retOut = {};
            retOut.result = 0;
            send(cSock, (const char *)&retOut, sizeof(LoginResult), 0);
            break;
        }
        default:
        {
            DataHeader header = { CMD_ERROR, 0 };
            send(cSock, (const char *)&header, sizeof(DataHeader), 0);
            break;
        }
    }
    return 0;
}

int main()
{
    //启动Windows socket 2的环境
    InitSock();
    //建立socket
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (SOCKET_ERROR == sock)
    {
        TRACE(_T("ERROR:创建socket失败\n"));
        return 0;
    }
    sockaddr_in sin = {};
    sin.sin_family = AF_INET;
    sin.sin_port = htons(10000);    //host to net unsigned short
    sin.sin_addr.S_un.S_addr = INADDR_ANY;     //本机上的外网和局域网ip任意一个拿来用  
    //绑定
    if (SOCKET_ERROR == bind(sock, (sockaddr *)&sin, sizeof(sockaddr_in)))
    {
        int nError = GetLastError();
        TRACE(_T("ERROR:绑定socket失败\n"));
        return 0; 
    }        
    //监听
    if (SOCKET_ERROR == listen(sock, 5))
    {
        TRACE(_T("ERROR:socket监听失败"));
        return 0;
    }
    else
    {
        printf("监听开始\n");
    }
    
    char recvBuf[128] = {};
    while (true)
    {
        fd_set fdRead;
        fd_set fdWrite;
        fd_set fdExp;
        FD_ZERO(&fdRead);
        FD_ZERO(&fdWrite);
        FD_ZERO(&fdExp);

        //将要监听的socket添加到数组
        FD_SET(sock, &fdRead);
        FD_SET(sock, &fdWrite);
        FD_SET(sock, &fdExp);

        //每次进来都把所有要监视的socket添加到相应的数组中
        for (std::vector<SOCKET>::iterator it = g_clients.begin(); it != g_clients.end(); it++)
        {
            FD_SET(*it, &fdRead);
        }

        //开始查询,会清空3个数组所有的socket,然后如果哪个socket有事件发送,则会把此socket放入到数组返回
        //当有客户端连接或者客户端发送消息都会进来
        timeval time = { 2, 0 };
        int ret = select(sock + 1, &fdRead, &fdWrite, &fdExp, &time);if (ret < 0)
        {
            printf("select任务结束\n");
            break;
        }
        if (FD_ISSET(sock, &fdRead))
        {
            FD_CLR(sock, &fdRead);
            //4接收客户端连接
            SOCKET cSock = INVALID_SOCKET;
            sockaddr_in clientAddr = {};
            int nAddrLen = sizeof(sockaddr_in);
            char msgBuf[100];
            cSock = accept(sock, (sockaddr*)&clientAddr, &nAddrLen);
            if (INVALID_SOCKET == cSock)
            {
                TRACE(_T("ERROR:接受客户端连接失败\n"));
                return 0;
            }
            //广播所有客户端,新加入一个新的客户端
            for (std::vector<SOCKET>::iterator it = g_clients.begin(); it != g_clients.end(); it++)
            {
                NewUserJoin newUser;
                newUser.sock = cSock;
                send(*it, (const char *)&newUser, sizeof(NewUserJoin), 0);
            }
            g_clients.push_back(cSock);
            printf("新客户端加入socket = %d,ip:%s\n",cSock, inet_ntoa(clientAddr.sin_addr));
        }

        for (size_t n = 0; n < fdRead.fd_count; n++)
        {
            if (-1 == ProcessPart(fdRead.fd_array[n]))
            {
                auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[n]);
                if (iter != g_clients.end())
                {
                    g_clients.erase(iter);
                }
            }
        }
    }
    
    for (std::vector<SOCKET>::iterator it = g_clients.begin(); it != g_clients.end(); it++)
    {
        closesocket(*it);
    }

    //关闭socket
    closesocket(sock);

    WSACleanup();
    return 0;
}

客户端:

#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#define _AFXDLL
#include"afx.h"
#include<windows.h>
#include "WinSock2.h"
#include<iostream>
#include<thread>
#include<stdlib.h>
using namespace std;

bool g_bRun = TRUE;

enum CMD
{
    CMD_LOGIN,
    CMD_LOGIN_RESULT,
    CMD_LOGIN_OUT,
    CMD_LOGIN_OUT_RESULT,
    CMD_NEW_USER_JOIN,
    CMD_ERROR,
};

struct DataHeader
{
    short cmd;
    short dataLength;
};

struct Login:public DataHeader
{
    Login()
    {
        cmd = CMD_LOGIN;
        dataLength = sizeof(Login);
    }
    char userName[32];
    int passWord;
};

struct LoginOut:public DataHeader
{
    LoginOut()
    {
        cmd = CMD_LOGIN_OUT;
        dataLength = sizeof(LoginOut);
    }
    char userName[32];
};

struct LoginResult:public DataHeader
{
    LoginResult()
    {
        cmd = CMD_LOGIN_RESULT;
        dataLength = sizeof(LoginResult);
    }
    int result;
};

struct LoginOutResult:public DataHeader
{
    LoginOutResult()
    {
        cmd = CMD_LOGIN_OUT_RESULT;
        dataLength = sizeof(LoginOutResult);
    }
    int result;
};

struct NewUserJoin :public DataHeader
{
    NewUserJoin()
    {
        cmd = CMD_NEW_USER_JOIN;
        dataLength = sizeof(NewUserJoin);
        sock = 0;
    }
    int sock;
};

void InitSock()
{
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 2);
    if (WSAStartup(wVersionRequested, &wsaData) != 0)
        return;

    if (LOBYTE(wsaData.wVersion) != 2 ||
        HIBYTE(wsaData.wVersion) != 2)
    {
        WSACleanup();
        return;
    }
}

int ProcessPart(SOCKET cSock)
{
    char szRecv[1024] = {};
    //接受客户端请求数据
    int nLen = recv(cSock, szRecv, sizeof(DataHeader), 0);
    DataHeader *header = (DataHeader*)szRecv;
    if (nLen <= 0)
    {
        printf("与服务器断开连接,任务结束\n", cSock);
        return -1;
    }
    printf("收到命令:%d  数据长度:%d\n", header->cmd, header->dataLength);

    switch (header->cmd)
    {
        case CMD_LOGIN_RESULT:
        {
            recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
            LoginResult* loginRes = (LoginResult*)szRecv;
            printf("收到服务器消息:CMD_LOGIN_RESULT  数据长度%d结果%d\n", loginRes->dataLength, loginRes->result);
            break;
        }
        case CMD_LOGIN_OUT_RESULT:
        {
            recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
            LoginOutResult* loginOutRes = (LoginOutResult*)szRecv;
            printf("收到服务器消息:CMD_LOGIN_OUT_RESULT: 数据长度:%d 结果:%d\n", loginOutRes->dataLength, loginOutRes->result);
            break;
        }
        case CMD_NEW_USER_JOIN:
        {
            recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
            NewUserJoin* loginOutRes = (NewUserJoin*)szRecv;
            printf("收到服务器消息:CMD_NEW_USER_JOIN: 数据长度:%d\n", loginOutRes->dataLength);
            break;
        }
        default:
        {
            DataHeader header = { CMD_ERROR, 0 };
            send(cSock, (const char *)&header, sizeof(DataHeader), 0);
            break;
        }
    }
    return 0;
}

void CmdThread(SOCKET sock)
{
    while (g_bRun)
    {
        char cmdBuf[256] = {};
        scanf("%s", cmdBuf);
        if (0 == strcmp(cmdBuf, "exit"))
        {
            printf("退出\n");

            return;
        }
        else if (0 == strcmp(cmdBuf,"login"))
        {
            Login login = {};
            strcpy(login.userName, "赵文军");
            login.passWord = 123;
            send(sock, (const char *)&login, sizeof(login), 0);
        }
        else if (0 == strcmp(cmdBuf, "loginOut"))
        {
            LoginOut loginOut = {};
            strcpy(loginOut.userName, "赵文军");
            send(sock, (const char *)&loginOut, sizeof(loginOut), 0);
        }    
    }
}

int main()
{
    //启动Windows socket 2的环境
    InitSock();
    //建立socket
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (SOCKET_ERROR == sock)
    {
        TRACE(_T("ERROR:创建socket失败"));
        return 0;
    }
    sockaddr_in sin = {};
    sin.sin_family = AF_INET;
    sin.sin_port = htons(10000);    //host to net unsigned short
    sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");      
    //连接
    if (SOCKET_ERROR == connect(sock, (sockaddr *)&sin, sizeof(sockaddr_in)))
    {
        int nError = GetLastError();
        TRACE(_T("ERROR:连接socket失败"));
        return 0;
    }
    char cmdBuf[128] = {};
    //启动线程
    std::thread t1(CmdThread, sock);
    //主线程和子线程分离,防止主线程退出,子线程还在运行报错
    t1.detach();

    while (g_bRun)
    {
        fd_set fdReads;
        FD_ZERO(&fdReads);
        FD_SET(sock, &fdReads);

        timeval time = { 2, 0 };
        int ret = select(sock, &fdReads, NULL, NULL, &time);if (ret < 0)
        {
            printf("select任务结束1\n");
            break;
        }
        if (FD_ISSET(sock, &fdReads))
        {
            FD_CLR(sock, &fdReads);

            if (-1 == ProcessPart(sock))
            {
                printf("select任务结束2\n");
                break;
            }
        }
    }
    //关闭socket
    closesocket(sock);

    WSACleanup();
    return 0;
}

 

posted @ 2020-07-07 22:55  zwj鹿港小镇  阅读(271)  评论(0编辑  收藏  举报