WINDOWS SOCKET编程SELECT模型使用

int select(

        int nfds,            //忽略
        fd_ser* readfds,    //指向一个套接字集合,用来检测其可读性
        fd_set* writefds,   // 指向一个套接字结合,用来检测其可写性
        fd_ser* exceptfds, //指向一个套接字集合,用来检测错误
        const struct timeval * timeout   //指定此函数等待的最长时间,如果为NULL,则最长时间为无限大。
);
 
参数说明:
(1)   nfds  windows下未使用,linux下会使用此参数
(2)   readfds:   readfds集合中的套接字发送在以下三种情况中任意一种就会被视为可读,select函数返回后继续留在readfds集合中,不可读的会被移除readfds集合。
1.套接字有数据可读,可以对套接字调用recv函数接收数据
2.套接字的连接已经关闭,重启或者中断,此时应该对套接字进行关闭
3.listen被调用,并且有一个连接未决,对 listen套接字调用accept函数建立新的链接。
(3)   writefds:    readfds集合中的套接字在以下三种情况下会被视为可写
1.使用connect套接字首次建立链接
2.使用accept套接字被接收
3.使用send操作失败,返回WSAEWOULDBLOCK错误,而且缓冲区的空间变得可用
send出去的数据其实都先存在winsock的发送缓冲区中,然后才发送出去,如果缓冲区满了,那么再调用send(WSASend,sendto,WSASendTo)的话,就会返回一个 WSAEWOULDBLOCK的错误码,接下来随着发送缓冲区中的数据被发送出去,缓冲区中出现可用空间时,一个 FD_WRITE 事件才会被触发,这里比较容易混淆的是 FD_WRITE 触发的前提是 缓冲区要先被充满然后随着数据的发送又出现可用空间,而不是缓冲区中有可用空间
(4)   exceptfds   (未使用过,没有进行深入的研究)
1.如果一个非阻塞连接调用正在被处理,连接视图失败
2.OOB数据可读
(5)   timeout:   设置时间,如果超过设定时间,还没有网络事件发生,则返回0,如果此参数为NULL,select会无限等待,直到有一个描述字满足条件。
timeout指向一个timeval结构
typedef struct timeval
{
    long tv_sec;       //只是等待多少秒
    long tv_usec;     //指示等待多少毫秒
} timeval;
如果timeval为{0,0},则select()立即返回,这可用于探询所
选套接口的状态。如果处于这种状态,则select()调用可认为是非阻塞的,且一切适用于非阻塞调用的假设都适用于它
 
具体编程流程
1.初始化套接字集合fdSocket,向这个集合添加监听套接字
2.将fdSocket集合拷贝到fdRead传递给select函数,当有时间发送时,select函数移除fdRead集合中没有未决io操作的套接字
3.比较原来fdSocket集合与select处理后的fdRead集合。确定哪些套接字有未决IO
4.回到第2步继续处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
my_socket();
my_bind(port);
my_listen();
 
//PostMessage(h_hand,WM_USER_THREADEND,0,0);
 
 
//select模型处理过程
//(1).初始化套接字集合fdSocket.添加监听套接字句柄到这个集合
FD_ZERO(&fdSocket);
FD_SET(sSock,&fdSocket);
 
while  (1)
{
     
     //(2.)将fdSocket集合的一个拷贝fdRead传递给select函数
     //当有时间发生时,select函数一处fdRead集合中没有未决IO操作的套接字句柄,然后返回。
     fd_set fdRead = fdSocket;
     int  nRet = select(0,&fdRead,NULL,NULL,NULL);     //timeout参数控制select()完成的时间。若timeout参数为空指针,则select()将一直阻塞
                                                     //到有一个描述字满足条件。否则的话,timeout指向一个timeval结构,其中指定了select()
                                                     //调用在返回前等待多长时间
                                                     //fdwrite  1. 接成功的套接字  在第一次建立连接时,C/S端都会触发一个FD_WRITE事件
                                                             //2, 触发的前提是 缓冲区要先被充满然后随着数据的发送又出现可用空间
 
     if (nRet>0)
     {
         //(3)通过原来的fdSocket集合与select处理后的fdRead集合比较
         //确定哪些套接字有未决io,并进一步处理这些io
         for ( int  i=0;i<( int )fdSocket.fd_count;i++)
         {
             if (FD_ISSET(fdSocket.fd_array[i],&fdRead))
             {
                 if (fdSocket.fd_array[i] == sSock)    //(1)监听套接字收到新连接,有新的链接
                 {
                     if (fdSocket.fd_count<FD_SETSIZE)    //判断集合满了吗?
                     {
                         
                         int  socke_len =  sizeof (remoteAddr);
             
 
                         //4.accept
                         SOCKET cSock = accept(sSock,(SOCKADDR*)&remoteAddr,&socke_len);
 
                         if (cSock == INVALID_SOCKET)
                         {
                             AfxMessageBox( "accept failed!\n" );
                             printf ( "accept failed!\n" );
                             continue ;
                         }
                         FD_SET(cSock,&fdSocket);
                         
                         //printf("接收到一个连接请求!:%s\r\n",inet_ntoa(remoteAddr.sin_addr) );
                         //printf("当前连接到服务器的客户端有 %d 个\n",fdSocket.fd_count+1);
                         socket_id = cSock*(-1);
 
                         PostMessage(h_hand,WM_USER_THREADEND,0,0);
 
                     }
                     else
                     {
                         AfxMessageBox( "too much connections !\n" );
                         printf ( "too much connections \n" );
                         continue ;
                     }
 
                 }
                 else
                 {
                     
                     int  nRecv = recv(fdSocket.fd_array[i],readText, sizeof (readText),0);
                     socket_id = fdSocket.fd_array[i];
                     if (nRecv>0)     //(2)可读
                     {
                         readText[nRecv] =  '\0' ;
 
                         //HWND   g_WindowHandle=((CDialog *)AfxGetMainWnd())->GetSafeHwnd();
 
                         PostMessage(h_hand,WM_USER_THREADEND,0,0);
                     }
                     else            //(3)连接关闭,重启或中断
                     {
                         closesocket(fdSocket.fd_array[i]);
                         FD_CLR(fdSocket.fd_array[i],&fdSocket);
                         someone_out = TRUE;
                         PostMessage(h_hand,WM_USER_THREADEND,0,0);
                     }
                 }
             }
         
         }
 
 
     }
     else
     {
         AfxMessageBox( "failed select()]n" );
         printf ( "failed select()]n" );
         break ;
 
     }
 
}

 

以上只是我在项目中使用的部分代码,最开始的my_socket,my_bind,my_listen都是自己对socket,bind,listen自己重新做的封装

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值