下面内容为windows下select模型分析,原博客链接

http://blog.csdn.net/fish_55_66/article/details/50352080

https://www.cnblogs.com/Mr-Zhong/p/4160988.html

Select模型的原理和使用步骤

  select(选择)模型是Winsock中最常见的I/O模型。之所以称其为“ select模型”,是由于它的“中心思想”

便是利用select函数,实现对 I/O的管理!利用select函数,我们判断套接字上是否存在数据,或者能否向一

个套接字写入数据。之所以要设计这个函数,唯一的目的便是防止应用程序在套接字处于锁定模式中时,在

一次I/O绑定调用(如send或recv)过程中,被迫进入“锁定”状态;同时防止在套接字处于非锁定模式中时,

产生WSAEWOULDBLOCK错误。除非满足事先用参数规定的条件,否则select函数会在进行I/O操作时锁定。

select的函数原型如下:

int select (
int nfds,
fd_set FAR * readfds,
fd_set FAR * writefds,
fd_set FAR * exceptfds,
const struct timeval FAR * timeout
);

  其中,第一个参数nfds会被忽略。之所以仍然要提供这个参数,只是为了保持与早期的Berkeley套接字应用程

序的兼容。大家可注意到三个 fd_set参数:一个用于检查可读性(readfds),一个用于检查可写性(writefds),

另一个用于例外数据( excepfds)。从根本上说,fdset数据类型代表着一系列特定套接字的集合。其中,

readfds集合包括符合下述任何一个条件的套接字:

  ■ 有数据可以读入。
  ■ 连接已经关闭、重设或中止。
  ■ 假如已调用了listen,而且一个连接正在建立,那么accept函数调用会成功。
writefds集合包括符合下述任何一个条件的套接字:
  《windows网络编程技术》第八章内容:
  只有在三种条件下,才会发出F D _ W R I T E通知:
  ■ 使用c o n n e c t或W S A C o n n e c t,一个套接字首次建立了连接。
  ■ 使用a c c e p t或W S A A c c e p t,套接字被接受以后。
  ■ 若s e n d、W S A S e n d、s e n d t o或W S A S e n d To操作失败,返回了W S A E W O U L D B L O C K错
  误,而且缓冲区的空间变得可用
  因此,作为一个应用程序,自收到首条F D _ W R I T E消息开始,便应认为自己必然能在一个套接字上发出数据,直至一个s e n d、W S A S e n d、s e n d t o或      W S A S e n d To返回套接字错误
W S A E W O U L D B L O C K。经过了这样的失败以后,要再用另一条F D _ W R I T E通知应用程序再次发送数据。
最后,exceptfds集合包括符合下述任何一个条件的套接字:
  ■ 假如已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
  ■ 有带外(out-of-band,OOB)数据可供读取。

  例如,假定我们想测试一个套接字是否“可读”,必须将自己的套接字增添到readfds集合,再等待select函数

完成。select完成之后,必须判断自己的套接字是否仍为readfds集合的一部分。若答案是肯定的,便表明该套

接字“可读”,可立即着手从它上面读取数据。在三个参数中(readfds、writedfss和exceptfds),任何两个都

可以是空值(NULL);但是,至少有一个不能为空值!在任何不为空的集合中,必须包含至少一个套接字句柄;

否则, select函数便没有任何东西可以等待。最后一个参数timeout对应的是一个指针,它指向一个timeval结构,

用于决定select最多等待 I / O操作完成多久的时间。如 timeout是一个空指针,那么select调用会无限期地“锁定”

或停顿下去,直到至少有一个描述符符合指定的条件后结束。对timeval结构的定义如下:

struct timeval {
long tv_sec;
long tv_usec;

} ;

  若将超时值设置为(0,0),表明select会立即返回,允许应用程序对 select操作进行“轮询”。出于对性能方面

的考虑,应避免这样的设置。select成功完成后,会在 fd_set结构中,返回刚好有未完成的I/O操作的所有套接字

句柄的总量。若超过timeval设定的时间,便会返回0。不管由于什么原因,假如select调用失败,都会返回SOCKET_ERROR。

用select对套接字进行监视之前,在自己的应用程序中,必须将套接字句柄分配给一个集合,设置好一个或全部

读、写以及例外 fd_set结构。将一个套接字分配给任何一个集合后,再来调用select,便可知道一个套接字上是

否正在发生上述的I/O活动。Winsock提供了下列宏操作,可用来针对I/O活动,对 fd_set进行处理与检查:

  ■ FD_CLR(s, *set):从s e t中删除套接字 s。
  ■ FD_ISSET(s, *set):注意这里并不是检测s有没有调用FD_SET加入set集合,而是检测set集合中哪一个句柄也就是fd是就绪的,因为set中可能有很多fd
  ■ FD_SET(s, *set):将套接字 s加入集合 s e t。
  ■ F D _ Z E R O ( * s e t ):将s e t初始化成空集合。

  例如,假定我们想知道是否可从一个套接字中安全地读取数据,同时不会陷于无休止的“锁定”状态,便可使用

FD_SET宏,将自己的套接字分配给fd_set集合,再来调用select。要想检测自己的套接字是否仍属 fd_read集合

的一部分,可使用FD_ISSET宏。采用下述步骤,便可完成用select操作一个或多个套接字句柄的全过程:

1) 使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set。
2) 使用FD_SET宏,将套接字句柄分配给自己感兴趣的每个fd_set。
3) 调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。
select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,并对每个集合进行相应的更新。
4) 根据select的返回值,我们的应用程序便可判断出哪些套接字是就绪的,并对其进行下一步操作
的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。

下面一个例子

服务端

#include <WS2tcpip.h>
#include <WinSock2.H>
#include <iostream> #pragma comment(lib, "ws2_32.lib") int main()
{
/// 初始化socket
WSADATA wsaData;
WORD version = MAKEWORD(,);
int result = ;
result = WSAStartup(version, &wsaData);
if (result != )
{
std::cout << "WSAStartup() error." << std::endl;
return -;
} /// 创建socket
SOCKET socketListen;
socketListen = socket(AF_INET, SOCK_STREAM, );
if (socketListen == INVALID_SOCKET)
{
WSACleanup();
std::cout << "socket() error." << std::endl;
return -;
} /// 服务器地址结构
sockaddr_in svrAddress;
svrAddress.sin_family = AF_INET;
svrAddress.sin_addr.s_addr = INADDR_ANY;
svrAddress.sin_port = htons(); /// 绑定服务器套接字
result = bind(socketListen, (sockaddr*)&svrAddress, sizeof(svrAddress));
if (result == SOCKET_ERROR)
{
closesocket(socketListen);
WSACleanup();
std::cout << "bind() error." << std::endl;
return -;
} /// 开启监听
result = listen(socketListen, );
if (result == SOCKET_ERROR)
{
closesocket(socketListen);
WSACleanup();
std::cout << "listen() error." << std::endl;
return -;
}
std::cout << "服务器启动成功,监听端口:" << ntohs(svrAddress.sin_port) << std::endl; /// select模型
fd_set allSockSet;
FD_ZERO(&allSockSet); FD_SET(socketListen, &allSockSet); // 将socketListen加入套接字集合中 while (true)
{
fd_set readSet;
FD_ZERO(&readSet);
readSet = allSockSet; result = select(, &readSet, NULL, NULL, NULL);
if (result == SOCKET_ERROR)
{
std::cout << "listen() error." << std::endl;
break;
} if (FD_ISSET(socketListen, &readSet))
{
sockaddr_in clientAddr;
int len = sizeof(clientAddr); SOCKET clientSocket = accept(socketListen, (sockaddr*)&clientAddr, &len);
if (clientSocket == INVALID_SOCKET)
{
std::cout << "accept() error." << std::endl;
break;
}
FD_SET(clientSocket, &allSockSet); /// 将新创建的套接字加入到集合中 char ipAddress[] = { };
inet_ntop(AF_INET, &clientAddr, ipAddress, );
std::cout << "有新的连接[" << ipAddress << ":" << ntohs(clientAddr.sin_port)
<< "], 目前客户端的数量为:" << allSockSet.fd_count - << std::endl; continue;
} for (u_int i = ; i < allSockSet.fd_count; ++i)
{
SOCKET socket = allSockSet.fd_array[i]; sockaddr_in clientAddr;
int len = sizeof(clientAddr);
getpeername(socket, (struct sockaddr *)&clientAddr, &len);
char ipAddress[] = { };
inet_ntop(AF_INET, &clientAddr, ipAddress, ); /// 可读性监视,可读性指有连接到来、有数据到来、连接已关闭、重置或终止
if (FD_ISSET(socket, &readSet))
{
char bufRecv[];
result = recv(socket, bufRecv, , );
if (result == SOCKET_ERROR)
{
DWORD err = WSAGetLastError();
if (err == WSAECONNRESET) /// 客户端的socket没有被正常关闭,即没有调用closesocket
{
std::cout << "客户端[" << ipAddress << ":" << ntohs(clientAddr.sin_port) << "]被强行关闭, ";
}
else
{
std::cout << "recv() error," << std::endl;
} closesocket(socket);
FD_CLR(socket, &allSockSet); std::cout << "目前客户端的数量为:" << allSockSet.fd_count - << std::endl;
break;
}
else if (result == ) /// 客户端的socket调用closesocket正常关闭
{
closesocket(socket);
FD_CLR(socket, &allSockSet); std::cout << "客户端[" << ipAddress << ":" << ntohs(clientAddr.sin_port)
<< "]已经退出,目前客户端的数量为:" << allSockSet.fd_count - << std::endl;
break;
} bufRecv[result] = '\0';
std::cout << "来自客户端[" << ipAddress << ":" << ntohs(clientAddr.sin_port)
<< "]的消息:" << bufRecv << std::endl;
}
}
} for (u_int i = ; i < allSockSet.fd_count; ++i)
{
SOCKET socket = allSockSet.fd_array[i];
closesocket(socket);
} WSACleanup();
return ;
}

客户端

#include <iostream>
#include <WS2tcpip.h>
#include <WinSock2.H> #pragma comment(lib, "ws2_32.lib") #define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 8000 #define SOCKET_NUM 1 /// 客户端socket的个数,修改该值可以改变连接到服务器的客户端个数 int main()
{
WORD wVersionRequested = MAKEWORD(, );
WSADATA wsaData;
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != ) return ; if (LOBYTE(wsaData.wVersion) != ||
HIBYTE(wsaData.wVersion) != )
{
WSACleanup();
std::cout << "WSAStartup() error." << std::endl;
return -;
} SOCKET allSocketClients[SOCKET_NUM];
for (int i = ; i < SOCKET_NUM; i ++)
{
SOCKET socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketClient == INVALID_SOCKET)
{
WSACleanup();
std::cout << "socket() error." << std::endl;
return -;
}
allSocketClients[i] = socketClient;
} SOCKADDR_IN server;
memset(&server, , sizeof(SOCKADDR_IN));
server.sin_family = AF_INET;
server.sin_port = htons(SERVER_PORT);
inet_pton(server.sin_family, SERVER_ADDRESS, &server.sin_addr); for (int i = ; i < SOCKET_NUM; i++)
{
SOCKET socketClient = allSocketClients[i];
err = connect(socketClient, (struct sockaddr *)&server, sizeof(SOCKADDR_IN));
if (err == SOCKET_ERROR)
{
std::cout << "connect() error." << std::endl;
closesocket(socketClient);
WSACleanup();
return -;
} std::cout << "第 " << i + << " 个客户端连接服务器成功。" << std::endl;
} for (int i = ; i < SOCKET_NUM; i++)
{
SOCKET socketClient = allSocketClients[i];
char message[] = { };
sprintf_s(message, "我是第 %d 个客户端 ", i + );
send(socketClient, message, strlen(message), );
} /// 按 q 退出程序
do
{
} while (getchar() != 'q'); for (int i = ; i < SOCKET_NUM; i++)
{
SOCKET socketClient = allSocketClients[i];
closesocket(socketClient);
} WSACleanup(); return ;
}

libevent-select模型分析的更多相关文章

  1. 【转】libevent源码分析

    libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...

  2. libevent 网络IO分析

    libevent 网络IO分析 Table of Contents 1. 简介 2. 简单使用与入门 2.1. 定时器-timeout 超时回调 2.2. 信号事件 2.3. 读取 socket 3. ...

  3. 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记.

    说明 为何要写这篇文章 ,之前看过阿二的梦想船的<Poco::TCPServer框架解析> http://www.cppblog.com/richbirdandy/archive/2010 ...

  4. socket select模型

    由于socket recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他连接不能继续. 如果想改变这种一直等下去的焦急状态,可以多线程来实现(不 ...

  5. windows socket编程select模型使用

    int select(         int nfds,            //忽略         fd_ser* readfds,    //指向一个套接字集合,用来检测其可读性       ...

  6. socket编程的select模型

    在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...

  7. linux下多路复用模型之Select模型

    Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...

  8. 比较一下Linux下的Epoll模型和select模型的区别

    一. select 模型(apache的常用) 1. 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Sel ...

  9. Select模型及tcp select模型

    参考:http://m.blog.csdn.net/article/details?id=51420015 一.套接字模式 套接字模式简单的决定了操作套接字时,Winsock函数是如何运转的.Wins ...

随机推荐

  1. 通用数据库操作类,前端easyui-datagrid,form

    实现功能:     左端datagrid显示简略信息,右侧显示选中行详细信息,数据库增删改 (1)点击选中行,右侧显示详细信息,其中[新增].[修改].[删除]按钮可用,[保存]按钮禁用 (2)点击[ ...

  2. ubuntu 14.04安装搜狗输入法

    安装sougou for linux: 1.从官网http://pinyin.sogou.com/linux/?r=pinyin下载对应版本 2.由于版本原因需要卸载fcitx: sudo apt-g ...

  3. Windows MFC 两个OpenGL窗口显示与线程RC问题

    问题为:背景界面是一个OpenGL窗口(对话框),在其上弹出一个OpenGL窗口(模态对话框)时, 1.上方的OpenGL窗口能响应鼠标操作等并刷新: 2.当移动或放大缩小上方的OpenGL窗口时,其 ...

  4. css知多少(2)——学习css的思路

    两周之前写过该系列的第一篇,其实当时只是一个想法,这段时间迟迟未更新,是在思考一个解决过程.现在初有成效,就开更吧. 1. 一个段子 开题不必太严肃,写博客也不像写书,像聊天似的写东西是最好的表达方式 ...

  5. POJ 1144 Network(Tarjan)

    题目链接 题意 : 找出割点个数. 思路 : Tarjan缩点,u是割点的充要条件是:u要么是具有两个以上子女的深度优先生成树的根,要么不是根,而有一个子女v满足low[v]>=dfn[u]. ...

  6. google 访问

    http://maolihui.com/goagent-detail.htmlgoagent教程详细版https://aiguge.xyz/chrome-goagent-proxy-switchyom ...

  7. Bootstrap ACE后台管理界面模板-jquery已整理

    做后台通用模板,基于bootstrap,jquery写成的模板,非常齐全.国内不能正常访问google我将不能访问的jquery替换成cdn.bootcss.com网站下的jquery 链接: htt ...

  8. JavaScript动漫作品(闭幕)

    笔者:Steven Riche 发布时间:2014年2一个月18 原文链接:http://code.tutsplus.com/tutorials/javascript-animation-that-w ...

  9. C#中一些易混淆概念总结

    C#中一些易混淆概念 这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框 ...

  10. JavaScript推荐资料合集(前端必看)

    这份合集覆盖了所有的JavaScript基本知识,从基本网络编程技巧,如变量.函数和循环语句,到高级一些的专题,如表单验证.DOM操作.客户端对象.脚本程序调试.学习前端的你不容错过! 资料名称 下载 ...