学习笔记(04):C++网络编程进阶-IO模型之select

立即学习:https://edu.csdn.net/course/play/6082/113754?utm_source=blogtoedu

多线程并发的解决方案:

主服务线程accept,每监听到一个新的连接,就为它创建一个线程,在子服务线程里recv,send数据,但是一旦客户端连接数增多,线程的开销将非常大。

利用select函数实现IO管理,通过对selet函数的调用,应用程序可以判断套接字是否存在数据,能否向该套接字写入数据,使用该模式的好处是可以等待多个套接字。

 

select函数:检查当前各个套接字状态

int selelct(IN int nfds,IN OUT fd-set *readfds,IN OUT fd_set * writefds,IN OUT fd_set*exceptfds , IN const struct timeval*timeout);

参数:

第一个参数:0,无意义

第二个参数:检查可读性集合

第三个参数:检查可写性集合

第四个参数:例外数据集合

第五个参数:函数的返回时间

返回值:

成功返回处于就绪状态并且已经包含在fd_set结构中的描述字总数,超时则返回0 , 否则返回socket_error错误,通过WSAGetLastError()获取错误代码。

timeval结构体

structure timeval

{

long tv_sec ;//秒

long tv_usec;//毫秒

}

当timeval为空指针时,select会一直等待,直到有符合条件的套接字才返回

当t_sec和tv_usec之和为0时,无论是否有符合条件的套接字,select立即返回。

当tv_set和tv_usec之和为非0时,如果在等待时间内有套接字满足条件,函数返回符合条件的套接字,如果在等待时间内没有套接字满足设置的条件,则select会在时间用完时返回0 。

 

typefef stuct fd_set

{

u_int fd_count ;

socket fd_arra[FD_SETSIZE];

}fd_set ;

fd_cout 表示该集合套接字数量,最大为64

fd_array套接字组。

 

在select函数返回时,会在fd_set结构填入相应的套接字

readfds数组将包括满足一下条件的套接字

1、有数据可读,此时在此套接字上调用recv,立即收到对方的数据。

2、连接已经关闭,终止或终止

3、正在请求建立连接的套接字,此时调用accept函数会成功

writefds数据满足下列条件的套接字

1、有数据可以发出,此时在此套接字上调用send,可以向对方发送数据

2、调用connect函数,并连接成功的套接字。

exceptfds数据包括下列条件的套接字

1、调用connection函数,但连接失败的套接字

2、有带外(out of band)数据可读。

select 判断套接字是否可读的步骤

1、将该套接字加入到readfds集合。

2、以readfds作为第二个参数调用select函数

3、当select函数返回时,应用程序判断该套接字是否仍然存在readfds集合

4、如果该套接字存在readfds集合,则表明该套接字可读,此时就可以调用recv函数接收数据,否则该套接字不可读。

在调用select函数时,readfds,writefds和exceptfds三个参数至少有一个为非空,并且在该非空的参数中,必须至少包含一个套接字,否则select函数将没有任何套接字可以等待。

Windows Socket 提供了下列宏来简化对fd_set的操作

FD_CLF(s ,*set) 从set集合中删除s套接字

FD_ISSET(s ,*set) 检查s是否为set集合的成员

FD_SET(s,*set) 将套接字加入到set集合中

FD_ZERO(*set)将set集合初始化为空集合。

服务端代码

#include <iostream>
#include<WinSock2.h>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
    WSADATA wd;
    if (WSAStartup(MAKEWORD(2, 2), &wd) != 0)
    {
        cout << "wsastartup error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET)
    {
        cout << "socket error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    sockaddr_in addr;
    addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8000);
    int bindRet = bind(s, (sockaddr*)&addr, sizeof sockaddr);
    if (bindRet == SOCKET_ERROR)
    {
        cout << "bind error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    int listenRet = listen(s, 0);
    if (listenRet == SOCKET_ERROR)
    {
        cout << "listen error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    //定义一个可读的集合
    fd_set readSet;
    //初始化集合,
    FD_ZERO(&readSet);
    //将套接字s加入到集合中
    FD_SET(s, &readSet);
    //while循环
    while (true)
    {
        //定义一个临时的集合
        fd_set tempRead;
        //初始化临时集合
        FD_ZERO(&tempRead);
        //将套接字赋值给临时集合
        tempRead = readSet;
        //利用select选择出集合中可读写的套接字
        int selectRet = select(0, &tempRead, NULL, NULL, NULL);
        if (selectRet == SOCKET_ERROR)
        {
            continue;
        }
        int ret;
        //成功筛选出来可发送或接受的socket
        for (int i = 0; i < tempRead.fd_count; i++)
        {
            SOCKET tempS = tempRead.fd_array[i];
            if (tempS == s)
            {
                sockaddr_in ClienAddr;
                int addrLen = sizeof sockaddr;
                SOCKET c = accept(s, (sockaddr*)&ClienAddr, &addrLen);
                if (readSet.fd_count < FD_SETSIZE)
                {
                    FD_SET(c, &readSet);
                    cout << "欢迎" << c << "进入聊天室" << endl;
                    char buf[100] = { '\0' };
                    sprintf_s(buf, "欢迎%d进入聊天室", c);
                    send(c, buf, 100, 0);
                }
                else
                {
                    cout << "达到客户端上限" << endl;
                }
            }
            else
            {
                char buf[100] = { '\0' };
                ret = recv(tempS, buf, 100, 0);
                if (ret == SOCKET_ERROR || ret == 0)
                {
                    closesocket(tempS);
                    FD_CLR(tempS, &readSet);
                    cout << tempS << "离开聊天室" << endl;
                }
                else
                {
                    cout << tempS << "说:" << buf << endl;
                }
            }
        }
    }
    closesocket(s);
    WSACleanup();
}
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值