三-select模型
select模型是对简单C/S模型的优化,他解决了accept函数阻塞等待连接的问题。并且允许应用程序同时监视多个套接字,从而实现简单的并发请求。通过调用select函数确认一个或多个套接字当前的状态,并根据当前状态进行相应操作。在select模型模型中,select函数是最关键的。
select模型工作原理
select模型维护了一个Socket数组,通过遍历该数组检查当前是否存在就绪socket,并将所有就绪的socket返回。我们遍历该数组提供相应服务。工作原理大致如下:
- 将服务端Socket添加至socket数组中。
- 调用select()函数遍历socket数组。
- 返回就绪socket数组,对该数组集中处理。
- 如果是服务端Socket,调用accept()函数接收连接请求,并将该客户端数组添加至socket数组。
- 如果是客户端Socket,调用send()、recv()函数进行通信,当客户端下线时,从socket数组中移除该Socket。
- 重复执行2-6步。
select模型使用步骤
- 启动Socket服务
- 创建套接字
- 为套接字绑定端口信息
- 监听套接字
- 使用select模型监听套接字集合,处理数据
- 关闭套接字和网络服务
select()函数
该函数定义如下:
int select(int nfds, fd_set readfds, fd_set writefds,
fd_set exceptfds, const struct timeval timeout);
参数
- nfds:忽略,传入0。为了与Berkeley 套接字兼容。
- readfds:可读取套接字的集合,调用函数时传入要监视的套接字,函数返回时保存可读套接字。
- writefds:可写套接字的集合。只要建立连接,则任何时候都可写入,可以传NULL。
- exceptfds:异常套接字集合。
- timeout:指定超时时间。
如果是服务端socket套接字,可读表示当前有客户端进行连接。如果是客户端套接字,可读表示当前有数据发送至服务端。select()函数是一个阻塞函数,如果指定了超时时间且没有socket就绪,select()函数返回。
返回值
- 0:超时
- >0:当前就绪的套接字数量,就绪的套接字保存在readfds中。
- -1:出现错误,可以通过WSAGetLasterror()函数获取错误码。
fd_set结构体
fd_set结构体定义如下:
typedef struct fd_set {
u_int fd_count; //fd_array数组中当前元素个数
SOCKET fd_array[FD_SETSIZE]; //socket数组
} fd_set;
在给select函数传递参数时,我们将需要监听的socket封装进fd_set结构体。select函数会顺序遍历数组,当发现有socket就绪,select函数返回,就绪的socket集合通常保存在readfds中。
下面列出了几个关于fd_set结构体的操作宏:

示例
1 #define _WINSOCK_DEPRECATED_NO_WARNINGS
2
3 #include <iostream>
4 #include <WinSock2.h>
5 #pragma comment(lib, "ws2_32.lib")
6 using namespace std;
7 const int nMajorVersion = 2;
8 const int nMinorVersion = 2;
9
10 int main()
11 {
12 DWORD dwVersion = MAKEWORD(nMajorVersion, nMinorVersion);
13 WSADATA wsaData;
14 int nStartRet = WSAStartup(dwVersion, &wsaData);
15 if (nStartRet != 0)
16 {
17 cout << "WSAStartup failed with error :" << nStartRet << endl;
18 return 1;
19 }
20
21 if (LOBYTE(wsaData.wVersion) != nMajorVersion || HIBYTE(wsaData.wVersion) != nMinorVersion)
22 {
23 cout << "version failed" << endl;
24 WSACleanup();
25 return 1;
26 }
27
28 SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
29 if (sockServer == INVALID_SOCKET)
30 {
31 cout << "socket create failed with code: "<< WSAGetLastError() << endl;
32 WSACleanup();
33 return 1;
34 }
35
36 sockaddr_in addInfo;
37 addInfo.sin_family = AF_INET;
38 addInfo.sin_port = htons(12345);
39 addInfo.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
40 int nBindRet = bind(sockServer, reinterpret_cast<sockaddr*>(&addInfo), sizeof(addInfo));
41 if (SOCKET_ERROR == nBindRet)
42 {
43 cout << "bind failed with code: " << WSAGetLastError() << endl;
44 closesocket(sockServer);
45 WSACleanup();
46 return 1;
47 }
48
49 int nListenRet = listen(sockServer, SOMAXCONN);
50 if (SOCKET_ERROR == nBindRet)
51 {
52 cout << "bind failed with code: " << WSAGetLastError() << endl;
53 closesocket(sockServer);
54 WSACleanup();
55 return 1;
56 }
57
58 fd_set allSockets;
59 FD_ZERO(&allSockets);
60 FD_SET(sockServer, &allSockets);
61 struct timeval tv = { 3,0 };
62
63 while (1)
64 {
65 fd_set tmpAllSockets = allSockets;
66 int nSelectRet = select(0, &tmpAllSockets, NULL, NULL, &tv);
67 if(nSelectRet == 0) continue; //超时
68 else if (nSelectRet == -1) //出错
69 {
70 cout << "select failed with code: " << WSAGetLastError() << endl;
71 break;
72 }
73 else if (nSelectRet > 0)
74 {
75 for (int i = 0; i < tmpAllSockets.fd_count; i++)
76 {
77 SOCKET socketID = tmpAllSockets.fd_array[i];
78
79 if (socketID == sockServer)
80 {
81 SOCKET socketClient = accept(socketID, NULL, NULL);
82 if (socketClient == INVALID_SOCKET)
83 continue;
84
85 FD_SET(socketClient, &allSockets);
86 }
87 else
88 {
89 char buf[1024] = { 0 };
90 int nRecvRet = recv(socketID, buf, 1024, 0);
91 if (nRecvRet == 0) //客户端断开连接
92 {
93 FD_CLR(socketID, &allSockets);
94 closesocket(socketID);
95 continue;
96 }
97 else if (nRecvRet > 0)
98 {
99 cout << socketID << " : " << buf << endl;
100 send(socketID, "我收到了你发送的数据!", sizeof("我收到了你发送的数据!"), 0);
101 }
102 else if (nRecvRet == SOCKET_ERROR)
103 {
104 int nError = WSAGetLastError();
105 if (nError == 10054)
106 {
107 FD_CLR(socketID, &allSockets);
108 closesocket(socketID);
109 continue;
110 }
111 else
112 cout << "recv error." << endl;
113 }
114 }
115 }
116 }
117 }
118
119 for (int i = 0; i < allSockets.fd_count; i++)
120 {
121 closesocket(allSockets.fd_array[i]);
122 }
123 FD_ZERO(&allSockets);
124 WSACleanup();
125 return 0;
126 }
总结
select模型有以下优点:
- select允许同时监视多个套接字的读写状态,使得在单个线程中可以处理多个套接字的I/O操作,提高了系统的效率。
- select模型使用简单,适合初学者设计简单的网络通信。
select模型有以下缺点:
- select()函数本身会造成阻塞。
- 由于需要遍历整个被监视的套接字集合,在大规模并发场景下,select模型性能低下。
- 受限于FD_SET集合有大小限制,后续的拓展性差,无法适用于高并发场景。
- 无法处理大量数据,如果有大量数据需要处理,会导致阻塞其他套接字的读写。
三-select模型的更多相关文章
- windows socket编程select模型使用
int select( int nfds, //忽略 fd_ser* readfds, //指向一个套接字集合,用来检测其可读性 ...
- socket编程的select模型
在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...
- linux下多路复用模型之Select模型
Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...
- 比较一下Linux下的Epoll模型和select模型的区别
一. select 模型(apache的常用) 1. 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Sel ...
- 基于select模型的udp客户端实现超时机制
参考:http://www.cnblogs.com/chenshuyi/p/3539949.html 多路选择I/O — select模型 其思想在于使用一个集合,该集合中包含需要进行读写的fd,通过 ...
- Winsock IO模型之select模型
之所以称其为select模型是因为它主要是使用select函数来管理I/O的.这个模型的设计源于UNIX系统,目的是允许那些想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字. int sele ...
- Windows I/O模型之一:Select模型
1.概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock) 四种调用模式: 同步:所谓同步,就是在发出一个功能调用时,在没有得到结果 ...
- socket select模型
由于socket recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他连接不能继续. 如果想改变这种一直等下去的焦急状态,可以多线程来实现(不 ...
- socket select()模型
转载:http://www.cnblogs.com/xiangshancuizhu/archive/2012/10/05/2711882.html 由于socket recv()方法是阻塞式的,当有多 ...
- select模型
在Windows中所有的socket函数都是阻塞类型的,也就是说只有网络中有特定的事件发生时才会返回,在没有发生事件时会一直等待,虽说我们将它们设置为非阻塞状态,但是在对于服务器段而言,肯定会一直等待 ...
随机推荐
- QQ/微信域名防红方法,打开网站跳转浏览器打开
简单通用QQ/微信跳转浏览器打开代码 使用方法: 将代码全部复制 粘贴到 网站根目录下index.php文件的顶端 注意:不要覆盖了 index.php里面的原代码,原代码保留(请尽快把样式以及图片本 ...
- Flink - [04] 窗口(Windows)
题记部分 一.Flink中的窗口是什么 (1)一般真实的流都是无界的,怎样处理无界的数据? (2)可以把无限的数据流进行切分,得到有限的数据集进行处理 -- 也就是得到有界流 (3)窗口(Window ...
- 解密注意力机制:为什么Flash Attention这么屌?
背景回顾:什么是大语言模型(LLM)? 在进入注意力机制的细节之前,我们先了解一下什么是大语言模型(LLM).简单来说,LLM是一种通过深度学习技术训练的大规模神经网络模型,用于处理和生成自然语言.L ...
- pnpm:无法加载文件 C:\Users\Five\AppData\Roaming\npm\pnpm.ps1 ,因为在此系统上禁止运行脚本
前言 重装完了电脑系统,运行pnpm 无法加载文件,pnpm -V也不行 解决方案 用管理员方式启动power shell 输入命令:set-ExecutionPolicy RemoteSigned ...
- 2个月搞定计算机二级C语言——真题(11)解析
1. 前言 今天双 11,正好轮到讲第 11 篇,直接来个三 11. 那么本篇我们讲解2个月搞定计算机二级C语言--真题11 2. 程序填空题 2.1 题目要求 2.2 提供的代码 #include ...
- k8s Error: failed to prepare subPath for volumeMount "custom-logo" of container "grafana"
前言 使用 k8s 挂载卷文件时,使用了 hostPath,type: File volumeMounts: - mountPath: /usr/share/grafana/public/img/gr ...
- go errors转string
前言 在 Go 中如果声明了两个字符相同的错误,但命名是新的变量,此时两个错误不相等 package main import ( "errors" "fmt" ...
- 权限获得第一步-NTLM暴力破解
题目: 你猜这是什么东西,记得破解后把其中的密码给我.答案为非常规形式. Administrator:500:806EDC27AA52E314AAD3B435B51404EE:F4AD50F57683 ...
- Redis 原理 - Sorted Set (ZSet)
Sorted Set (ZSet) 数据结构 Sorted Set (ZSet), 即有序集合, 底层使用 压缩列表(ziplist) 或者 跳跃表(skiplist) 使用 压缩列表(ziplist ...
- MySQL 的 JSON 查询
MySQL 的 JSON 路径格式 MySQL 使用特定的 JSON 路径表达式语法来导航和提取 JSON 文档中的数据 基本结构 MySQL 中的 JSON 路径遵循以下通用格式 $[路径组件] 路 ...