Select模型学习
来源:互联网 发布:手机淘宝取消退款流程 编辑:程序博客网 时间:2024/06/05 02:54
Select模型是最常见的I/O模型。
使用 int select( int nfds , fd_set FAR* readfds , fd_set FAR* writefds,fd_set FAR* exceptfds,const struct timeval FAR * timeout ) ;
函数来检查你要调用的Socket套接字是否已经有了需要处理的数据。
select包含三个Socket队列,分别代表:
readfds ,检查可读性,
writefds,检查可写性,
exceptfds,例外数据。
timeout是select函数的返回时间。
例如,我们想要检查一个套接字是否有数据需要接收,我们可以把套接字句柄加入可读性检查队列中,然后调用select,如果,该套接字没有数据需要接收, select函数会把该套接字从可读性检查队列中删除掉,所以我们只要检查该套接字句柄是否还存在于可读性队列中,就可以知道到底有没有数据需要接收了。
WinSock提供了一些宏用来操作套接字队列fd_set。
FD_CLR( s,*set) 从队列set删除句柄s。
FD_ISSET( s, *set) 检查句柄s是否存在与队列set中。
FD_SET( s,*set )把句柄s添加到队列set中。
FD_ZERO( *set ) 把set队列初始化成空队列。涉及到的结构的定义:a、 fd_set结构:
#define FD_SETSIZE 64
typedef struct fd_set
{
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
fd_count为已设定socket的数量
fd_array为socket列表,FD_SETSIZE为最大socket数量,建议不小于64。这是微软建 议的。(是否是不应该大于64)
timeval结构:
struct timeval
{
long tv_sec; /* 时间的秒值 */
long tv_usec; /*时间的毫秒值 */
};
这个结构主要是设置select()函数的等待值,如果将该结构设置为(0,0),则select()函数会立即返回。
select函数各参数的作用:
1. nfds:没有任何用处,主要用来进行系统兼容用,一般设置为0。
2. readfds:等待可读性检查的套接字组。
3. writefds;等待可写性检查的套接字组。
4. exceptfds:等待错误检查的套接字组。
5. timeout:超时时间。
函数失败的返回值:调用失败返回SOCKET_ERROR,超时返回0。
readfds、writefds、exceptfds三个变量至少有一个不为空,同时这个不为空的套接字组种至少有一个socket,道理很简单,否则要select干什么呢。
举例:测试一个套接字是否可读:
fd_set fdread;
FD_SET(s,&fdread); //加入套接字
if(FD_ISSET(s,&fread) //是否存在fread中
1:select模型(选择模型)
先看一下下面的这句代码:
int iResult = recv(s, buffer,1024);
这是用来接收数据的,在默认的阻塞模式下的套接字里,recv会阻塞在那里,直到套接字连接上有数据可读,把数据读到buffer里后recv函数才会返回,不然就会一直阻塞在那里。在单线程的程序里出现这种情况会导致主线程(单线程程序里只有一个默认的主线程)被阻塞,这样整个程序被锁死在这里,如果永远没数据发送过来,那么程序就会被永远锁死。这个问题可以用多线程解决,但是在有多个套接字连接的情况下,这不是一个好的选择,扩展性很差。Select模型就是为了解决这个问题而出现的。
再看代码:
int iResult = ioctlsocket(s, FIOBIO, (unsigned long *)&ul);
iResult = recv(s, buffer,1024);
这一次recv的调用不管套接字连接上有没有数据可以接收都会马上返回。原因就在于我们用ioctlsocket把套接字设置为非阻塞模式了。不过你跟踪一下就会发现,在没有数据的情况下,recv确实是马上返回了,但是也返回了一个错误:WSAEWOULDBLOCK,意思就是请求的操作没有成功完成。看到这里很多人可能会说,那么就重复调用recv并检查返回值,直到成功为止,但是这样做效率很成问题,开销太大。
感谢天才的微软工程师吧,他们给我们提供了好的解决办法。
// link with Ws2_32.lib#pragma comment(lib,"Ws2_32.lib")#include <winsock2.h>#include <ws2tcpip.h>#include <stdio.h>#include <stdlib.h> // Needed for _wtoiint __cdecl wmain(int argc, wchar_t **argv){system("title 服务器");// Declare and initialize variablesWSADATA wsaData = {0};int iResult = 0;iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (iResult != 0) {wprintf(L"WSAStartup failed: %d\n", iResult);return 1;}SOCKET sock = socket(AF_INET, SOCK_STREAM ,0);if (sock == INVALID_SOCKET) {wprintf(L"socket function failed with error = %d\n", WSAGetLastError() );iResult = closesocket(sock);if (iResult == SOCKET_ERROR) {wprintf(L"closesocket failed with error = %d\n", WSAGetLastError() );WSACleanup();return 1;}}sockaddr_in aSerAddr;aSerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");aSerAddr.sin_family = AF_INET;aSerAddr.sin_port = htons(1989);int iRetBind = bind(sock,(sockaddr*)&aSerAddr,sizeof(sockaddr_in));int iRetLs = listen(sock,10);sockaddr_in aClientAddr;int iLen= sizeof(sockaddr_in);SOCKET sockClient = INVALID_SOCKET;fd_set fdRead;timeval fdTime;fdTime.tv_sec = 2;fdTime.tv_usec= 0;while(1){FD_ZERO(&fdRead);//初始化fD_SETFD_SET(sock,&fdRead);//分配套接字句柄到相应的fd_setselect(0,&fdRead,NULL,NULL,&fdTime);//如果套接字句柄还在fd_set里,说明客户端已经有connect的请求发过来了,马上可以accept成功if(FD_ISSET(sock,&fdRead))sockClient = accept(sock,(sockaddr*)&aClientAddr,&iLen);else{printf("没有客户端连接\n");continue;}if(sockClient!=INVALID_SOCKET){printf("%s连接到服务器!\n",inet_ntoa(aClientAddr.sin_addr));send(sockClient,"连接成功",strlen("连接成功"),0);char buffer[100]={0};recv(sockClient,buffer,100,0);printf("客户端:%s",buffer);}}return 0;}
- Select模型的学习
- Select模型学习
- WinSock学习(1):Select模型
- WinSock学习笔记3:Select模型
- SOCKET学习第三阶段(SELECT模型)
- [转]学习libevent的select模型
- 网络编程中select模型和poll模型学习(linux)
- select 模型
- select模型
- Select模型
- Select模型
- Select模型
- SELECT模型
- select模型
- Select模型
- SELECT模型
- select 模型
- select模型
- TCP连接的状态转换深度剖析
- csr 蓝牙
- Maven入门学习
- opencv 中SVM + PCA 人脸识别
- 解决android导入多个第三方jar包后项目代码混淆的问题
- Select模型学习
- Common Tasks :Virtual Machine Guest Operations
- php 对 mysql数据库的增,删,该,查
- C++的替代者
- 创建计算字段 汇总数据 分组数据
- Common Tasks :Powering Off or Suspending a Virtual Machine
- 第八周最后任务—我爱你!!!
- Common Tasks :Importing a Legacy Virtual Machine
- C++的营养——swap手法