Windows中的select网络模型

网络模型是为了更好的利用资源,优化服务器

因为一个连接产生一个socket,所以维护大量的客户端必须得用多线程处理不同的客户端,而用以前的一个客户端创建一个线程去维护处理又会有新问题
线程最大是一个2个字节的数量(65535),如果超过的话这种就处理不了,所以就希望一个线程处理多个客户端,所以可以建立一个socket的数组去处理
而一个线程处理多少个客户端合适呢,经过科学计算,处理64个是最好的,所以每次来一个客户端连接就把它放进数组中存储,每一个线程处理自己数组
中的socket,而recv是阻塞的,如果线程轮询数组的话,如果前面连接的socket没有数据那线程就会阻塞在这里(也称同步),后面的socket就处理不了
所以要把socket设置为非阻塞模式(也称作异步),这样的话线程处就能处理每一个socket了
而标准网络编程中就提供了这种处理模式,这就是select服务器模型(经过科学计算,一个线程同时处理64个客户端的连接是效率最高的,所有select模型轮询的socket数组时64个)
select模型简单代码如下

// Server.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <WinSock2.h>
#include <tchar.h>
#include <WS2tcpip.h>

#pragma comment(lib, "Ws2_32.lib")

struct select_info
{
    select_info()
    {
        m_nCount = 0;
        memset(clients, 0, sizeof(clients));
    }
    int m_nCount;
    SOCKET clients[FD_SETSIZE];
};

DWORD WINAPI workThread(LPVOID lpParameter)
{
    select_info *info = (select_info*)lpParameter;

    printf("workThread:%d run...\n");

    fd_set set;

    while (true)
    {
        //初始化
        FD_ZERO(&set);
        for (int i = 0; i < info->m_nCount; i++)
        {
            FD_SET(info->clients[i], &set);
        }

        //轮询
        timeval tv = { 2 };
        int recv_count = select(0, &set, NULL, NULL, &tv);
        if (recv_count == 0)
        {
            continue;
        }
        else if (recv_count < 0)
        {
            break;
        }
            
        //轮询判断那个连接是可以读
        for (int i = 0; i < set.fd_count; i++)
        {
            if (FD_ISSET(set.fd_array[i], &set))
            {
                char msg[260] = { 0 };
                int bytes = recv(set.fd_array[i], msg, sizeof(msg), 0);
                if (bytes > 0)
                {
                    printf("socket:%d bytes:%d msg:%s\n", set.fd_array[i], bytes, msg);
                }
                else
                {
                    //数组移除socket
                }
            }
        }
    }


    delete info;
    printf("workThread:%d exit...\n");
    return 0;
}

int main() {
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);


    int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s <= 0) {
        return 0;
    }

    puts("socket ok");

    //绑定端口
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(58888);
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);


    if (bind(s, (sockaddr*)&addr, sizeof(addr)) < 0) {
        return 0;
    }
    puts("bind ok");

    //监听
    if (listen(s, SOMAXCONN) < 0) {
        return 0;
    }
    puts("listen ok");


    puts("accept...");


    //接受客户端连接
    select_info* pSelectInfo = NULL;
    int nIndex = 0;
    int nAllCount = 0;
    while (true)
    {
        sockaddr_in caddr;
        int nlen = sizeof(addr);
        int cs = accept(s, (sockaddr*)&caddr, &nlen);
        int n = WSAGetLastError();
        if (cs < 0)
        {
            if (WSAGetLastError() != WSAEWOULDBLOCK)
                break;
        }
        else
        {
            //如果是64的倍数就创建一个线程去管理这个客户端连接的数组连接
            if (nAllCount % 64 == 0)
            {
                nIndex = 0;
                pSelectInfo = NULL;
                pSelectInfo = new select_info();
                pSelectInfo->clients[nIndex] = cs;
                ++pSelectInfo->m_nCount;
                CreateThread(NULL, 0, workThread, (LPVOID)pSelectInfo, 0, NULL);
            }
            else//否则就往这个轮询的数组添加连接socket
            {
                ++nIndex;
                ++pSelectInfo->m_nCount;
                pSelectInfo->clients[nIndex] = cs;
            }
            ++nAllCount;
           
        }
        printf("%d\n", nAllCount);

    }

    puts("server close");
    closesocket(s);
    WSACleanup();

    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值