select()

本文详细解析了Unix系统中的select函数,包括其参数解释、使用方法及在多进程编程中的实际应用案例。通过实例演示如何高效地监控多个文件描述符的读写事件,提升程序响应速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数解析:

int   select(nfds,   readfds,   writefds,   exceptfds,   timeout)
参数1:
int   nfds;
ndfs:select监视的文件句柄数,视进程中打开的文件数而定,一般设为你要监视各文件中的最大文件号加一,在Windows中这个参数的值无所谓,可以设置不正确。
 参数2,3,4:
fd_set   *readfds,   *writefds,   *exceptfds;
readfds:select监视的可读文件句柄集合。
writefds:   select监视的可写文件句柄集合。
exceptfds:select监视的异常文件句柄集合。
typedef struct fd_set {
  u_int    fd_count;                 // how many are SET?
  SOCKET   fd_array[FD_SETSIZE];     // an array of SOCKETs
} fd_set;

具体解释select的参数:

struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,readset writeset exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为NULL。如果三个指针都为NULL,我们就有了一个比sleep()函数更为精确的定时器(sleep()以秒为最小单位,这个以微妙为单位)。 当readfds或writefds中映象的文件可读或可写或超时,本次select()就结束返回。程序员利用一组系统提供的宏在select()结束时便可判断哪一文件可读或可写。对Socket编程特别有用的就是readfds。
几只相关的宏解释如下:

FD_ZERO(fd_set   *fdset):清空fdset与所有文件句柄的联系,清空集合。
FD_SET(int   fd,   fd_set   *fdset):建立文件句柄fd与fdset的联系,将一个给定的文件描述符加入集合之中。
FD_CLR(int   fd,   fd_set   *fdset):清除文件句柄fd与fdset的联系,将一个给定的文件描述符从集合中删除。
FD_ISSET(int   fd,   fdset   *fdset):检查fdset联系的文件句柄fd是否可读写,> 0表示可读写,检查集合中指定的文件描述符是否可以读写。

参数5:
struct   timeval   *timeout;
2013.11.28 timeout:本次select()的超时结束时间。(见/usr/sys/select.h,可精确至百万分之一秒!) struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

例子:

...
int   sockfd;  //文件句柄
fd_set   fdR; // fd_set的数据结构,实际上是一long类型的数组
struct   timeval   timeout   =   ..;
...
for(;;)  {
FD_ZERO(&fdR);      //清空fdset与所有文件句柄的联系。
FD_SET(sockfd,   &fdR);   // 建立文件句柄fd与fdset的联系。
switch   (select(sockfd   +   1,   &fdR,   NULL,   &timeout))   {
case   -1:
error   handled   by   u;
case   0:
timeout   hanled   by   u;
default:
if   (FD_ISSET(sockfd))   {    //if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据
now   u   read   or   recv   something;
/*   if   sockfd   is   father   and
server   socket,   u   can   now
accept()   */
}
}
}

select模型
#include <winsock2.h>   
#include <stdio.h>   
#define PORT  5150   
#define MSGSIZE  1024   
#pragma comment(lib, "ws2_32.lib")   
int g_iTotalConn1 = 0;  

SOCKET g_CliSocketArr1[FD_SETSIZE];  
SOCKET g_CliSocketArr2[FD_SETSIZE];  

DWORD WINAPI WorkerThread1(LPVOID lpParam);   
int CALLBACK ConditionFunc1(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR * 

g,DWORD dwCallbackData);

int main(int argc, char* argv[])   
{   
 WSADATA wsaData;   
 SOCKET sListen, sClient;   
 SOCKADDR_IN local, client;   
 int iAddrSize = sizeof(SOCKADDR_IN);   
 DWORD dwThreadId;   
 // Initialize windows socket library   
 WSAStartup(0x0202, &wsaData);   
 // Create listening socket   
 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);   
 // Bind   

 local.sin_family = AF_INET;   
 local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);   
 local.sin_port = htons(PORT);   
 bind(sListen, (sockaddr*)&local, sizeof(SOCKADDR_IN));   

 // Listen   

 listen(sListen, 3);   

 // Create worker thread   

 CreateThread(NULL, 0, WorkerThread1, NULL, 0, &dwThreadId);  

 while (TRUE)    
 {     
  sClient = WSAAccept(sListen, (sockaddr*)&client, &iAddrSize, ConditionFunc1, 0);
  printf("1:Accepted client:%s:%d:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), g_iTotalConn1);   
  g_CliSocketArr1[g_iTotalConn1++] = sClient;  
 }   
 return 0;   
}   

DWORD WINAPI WorkerThread1(LPVOID lpParam)   
{   
 int i;   
 fd_set fdread;   
 int ret;   
 struct timeval tv = {1, 0};   
 char szMessage[MSGSIZE];   
 while (TRUE)    
 {   
  FD_ZERO(&fdread);   //1清空队列
  for (i = 0; i < g_iTotalConn1; i++)    
  {   
   FD_SET(g_CliSocketArr1[i], &fdread);   //2将要检查的套接口加入队列
  }   

  // We only care read event   
  ret = select(0, &fdread, NULL, NULL, &tv);   //3查询满足要求的套接字,不满足要求,出队
  if (ret == 0)    
  {   
   // Time expired   
   continue;   
  }   

  for (i = 0; i < g_iTotalConn1; i++)    
  {   
   if (FD_ISSET(g_CliSocketArr1[i], &fdread))    //4.是否依然在队列
   {   
    // A read event happened on g_CliSocketArr   

    ret = recv(g_CliSocketArr1[i], szMessage, MSGSIZE, 0);   
    if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))    
    {   
    // Client socket closed   
     printf("1:Client socket %d closed.\n", g_CliSocketArr1[i]);   
     closesocket(g_CliSocketArr1[i]);   
     if (i < g_iTotalConn1-1)    
     {   
      g_CliSocketArr1[i--] = g_CliSocketArr1[--g_iTotalConn1];   
     }   
    }    
    else    
    {   
     // We reveived a message from client   
     szMessage[ret] = '\0';   
     send(g_CliSocketArr1[i], szMessage, strlen(szMessage), 0);   
    }   
   }   
  }  
 }   
} 

int CALLBACK ConditionFunc1(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR * 

g,DWORD dwCallbackData)
{
 if (g_iTotalConn1 < FD_SETSIZE)
  return CF_ACCEPT;
 else
  return CF_REJECT;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山西茄子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值