select模型的优点:
1.
跨平台性:
select
模型是跨平台的,几乎所有的操作系统都支持
select
模型,包括
Linux
、
Windows
等。
2.
简单易用:
select
模型的
API
相对简单,使用起来比较方便,适用于简单的网络编程需求。
3.
灵活性:
select
模型支持监视多个文件描述符,并且可以通过设置超时参数来实现超时等待,具有一定的灵活性。
4.
可移植性:
select
模型是标准的
POSIX
函数,具有较好的可移植性,适用于各种不同平台的开发。
select模型的缺点:
1.
效率低:
select
模型在大规模并发的场景下效率较低,因为需要遍历所有的文件描述符,性能随着监视的文件描述符数量增加而下降。
2.
文件描述符数量限制:
select
模型对监视的文件描述符数量有限制,通常最多支持
1024
个,超过这个数量会导致程序崩溃或运行异常。
3.
IO
效率不高:
select
模型每次都需要将监视的所有文件描述符传递给内核,效率较低,特别是在文件描述符数量较大时。
使用select模型的步骤方法:
1.
准备文件描述符集合:
使用
fd_set
类型的变量准备需要监视的文件描述符集合。
2.
设置超时参数(可选):
可以设置超时参数,指定
select
函数的等待时间,超时后
select
函数返回。
3.
调用
select
函数:
调用
select
函数进行文件描述符的监视,等待有事件发生。
4.
检查事件类型:
使用
FD_ISSET
宏检查每个文件描述符对应的事件类型,判断事件的类型。
5.
处理事件:
根据事件的类型进行相应的处理,比如接收数据、发送数据等。
6.
重复调用
select
函数:
如果有需要,可以重复调用
select
函数,持续监视文件描述符的状态,直到程序结束。
总的来说,select模型适用于简单的网络编程需求,但在处理大规模并发连接时效率较低,不适合高性能的网络编程场景。
#include <iostream>
#include <string>
#include <WS2tcpip.h>
#include <vector>
#include <algorithm>
#pragma comment(lib, "ws2_32.lib")
int main() {
// 初始化Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOK = WSAStartup(ver, &wsData);
if (wsOK != 0) {
std::cerr << "Error: Can't initialize Winsock! Quitting" << std::endl;
return 1;
}
// 创建监听socket
SOCKET listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener == INVALID_SOCKET) {
std::cerr << "Error: Can't create listener socket! Quitting" << std::endl;
WSACleanup();
return 1;
}
// 绑定地址和端口
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; // 监听所有网卡上的连接
bind(listener, (sockaddr*)&hint, sizeof(hint));
// 开始监听
listen(listener, SOMAXCONN);
// 设置fd_set,包含监听socket和所有活跃socket
fd_set master;
FD_ZERO(&master);
FD_SET(listener, &master);
// 记录活跃的客户端socket
std::vector<SOCKET> clients;
std::cout << "Server listening..." << std::endl;
while (true) {
fd_set copy = master;
// 使用select检查socket的状态
int socketCount = select(0, ©, nullptr, nullptr, nullptr);
for (int i = 0; i < socketCount; ++i) {
SOCKET sock = copy.fd_array[i];
// 如果是监听socket有新连接
if (sock == listener) {
SOCKET client = accept(listener, nullptr, nullptr);
// 将新客户端socket添加到master集合中
FD_SET(client, &master);
clients.push_back(client);
std::cout << "New client connected" << std::endl;
}
else {
// 处理客户端发来的消息
char buffer[4096];
memset(buffer, 0, sizeof(buffer));
int bytesReceived = recv(sock, buffer, sizeof(buffer), 0);
if (bytesReceived <= 0) {
// 客户端关闭连接
std::cout << "Client disconnected" << std::endl;
closesocket(sock);
FD_CLR(sock, &master);
clients.erase(std::remove(clients.begin(), clients.end(), sock), clients.end());
}
else {
// 将消息打印到控制台
std::cout << "Received from client: " << std::string(buffer, bytesReceived) << std::endl;
// 将消息发送给其他所有客户端
for (SOCKET outSock : clients) {
if (outSock != listener && outSock != sock) {
send(outSock, buffer, bytesReceived, 0);
}
}
}
}
}
}
// 关闭监听socket
closesocket(listener);
// 关闭Winsock
WSACleanup();
return 0;
}