zoukankan      html  css  js  c++  java
  • Windsock套接字I/O模型学习 --- 第二章

    1. select模型

    select模型主要借助于apiselect来实现,所以先介绍一下select函数

    int select(
    int nfds, // 忽略,仅是为了与 Berkeley 套接字兼容
    fd_set* readfds, // 指向一个套接字集合,用来检查其可读性
    fd_set* writefds, // 指向一个套接字集合,用来检查其可写性
    fd_set* exceptfds, // 指向一个套接字集合,用来检查错误
    const struct timeval* timeout // 指定此函数等待的最长时间,如果为 NULL,则最长时间为无限大
    );
    
    // fd_set结构体
    typedef struct fd_set {
    u_int fd_count; // 下面数组的大小
    SOCKET fd_array[FD_SETSIZE]; // 套接字句柄数组
    } fd_set;
    
    FD_ZERO(*set) //初始化 set 为空集合。集合在使用前应该总是清空
    FD_CLR(s, *set) //从 set 移除套接字 s
    FD_ISSET(s, *set) //检查 s 是不是 set 的成员,如果是返回 TRUE
    FD_SET(s, *set) //添加套接字到集合
    

    2.例子

    一个简单的流程:

    1. 初始化套接字集合 fdSocket,向这个集合添加监听套接字句柄。
    2. 将 fdSocket 集合的拷贝 fdRead 传递给 select 函数,当有事件发生时, select 函数移除 fdRead 集合中没有未决 I/O 操作的套接字句柄,然后返回。
    3. 比较原来 fdSocket 集合与 select 处理过的 fdRead 集合,确定哪些套接字有未决 I/O,并进一步处理这些 I/O。
    4. 回到第 2 步继续进行选择处理。
    CInitSock theSock; // 初始化 Winsock 库
    int main()
    {
    	USHORT nPort = 4567; // 此服务器监听的端口号
    						 // 创建监听套接字
    	SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	sockaddr_in sin;
    	sin.sin_family = AF_INET;
    	sin.sin_port = htons(nPort);
    	sin.sin_addr.S_un.S_addr = INADDR_ANY;
    	// 绑定套接字到本地机器
    	if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
    	{
    		printf(" Failed bind() 
    ");
    		return -1;
    	}
    	// 进入监听模式
    	::listen(sListen, 5);
    	// select 模型处理过程
    	// 1)初始化一个套接字集合 fdSocket,添加监听套接字句柄到这个集合
    	fd_set fdSocket; // 所有可用套接字集合
    	FD_ZERO(&fdSocket);
    	FD_SET(sListen, &fdSocket);
    	while (TRUE)
    	{ // 2)将 fdSocket 集合的一个拷贝 fdRead 传递给 select 函数,
    	  // 当有事件发生时, select 函数移除 fdRead 集合中没有未决 I/O 操作的套接字句柄,然后返回。
    		fd_set fdRead = fdSocket;
    		int nRet = ::select(0, &fdRead, NULL, NULL, NULL);
    		if (nRet > 0)
    		{ // 3)通过将原来 fdSocket 集合与 select 处理过的 fdRead 集合比较,
    		  // 确定都有哪些套接字有未决 I/O,并进一步处理这些 I/O。
    			for (int i = 0; i < (int)fdSocket.fd_count; i++)
    			{
    				if (FD_ISSET(fdSocket.fd_array[i], &fdRead))
    				{
    					if (fdSocket.fd_array[i] == sListen) // ( 1)监听套接字接收到新连接
    					{
    						if (fdSocket.fd_count < FD_SETSIZE)
    						{
    							sockaddr_in addrRemote;
    							int nAddrLen = sizeof(addrRemote);
    							SOCKET sNew =
    								::accept(sListen, (SOCKADDR*)&addrRemote, &nAddrLen);
    							FD_SET(sNew, &fdSocket);
    							printf("接收到连接( %s) 
    ", ::inet_ntoa(addrRemote.sin_addr));
    						}
    						else
    						{
    							printf(" Too much connections! 
    ");
    							continue;
    						}
    					}
    					else
    					{
    						char szText[256];
    						int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
    						if (nRecv > 0) // ( 2)可读
    						{
    							szText[nRecv] = '';
    							printf("接收到数据: %s 
    ", szText);
    						}
    						else // ( 3)连接关闭、重启或者中断
    						{
    							::closesocket(fdSocket.fd_array[i]);
    							FD_CLR(fdSocket.fd_array[i], &fdSocket);
    						}
    					}
    				}
    			}
    		}
    		else
    		{
    			printf(" Failed select() 
    ");
    			break;
    		}
    	}
    	return 0;
    }
    

    3.好处

    使用 select 的好处是程序能够在单个线程内同时处理多个套接字连接,这避免了阻塞模
    式下的线程膨胀问题。但是,添加到 fd_set 结构的套接字数量是有限制的,默认情况下,最
    大值是 FD_SETSIZE,它在 winsock2.h 文件中定义为 64。为了增加套接字数量,应用程序可
    以将 FD_SETSIZE 定义为更大的值(这个定义必须在包含 winsock2.h 之前出现)。不过,自
    定义的值也不能超过 Winsock 下层提供者的限制(通常是 1024)

  • 相关阅读:
    标签的讲解
    属性分类
    LeetCode 003. 无重复字符的最长子串 双指针
    Leetcode 136. 只出现一次的数字 异或性质
    Leetcode 231. 2的幂 数学
    LeetCode 21. 合并两个有序链表
    象棋博弈资源
    acwing 343. 排序 topsort floyd 传播闭包
    Leetcode 945 使数组唯一的最小增量 贪心
    Leetcode 785 判断二分图 BFS 二分染色
  • 原文地址:https://www.cnblogs.com/zjzyh/p/5459581.html
Copyright © 2011-2022 走看看