Socket I/O------select模型

 

 


   初学Socket的人来说都不太爱用Select写程序
,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常

 

#include <winsock.h>

原型

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

 

 

winsock中

nfds:              本参数忽略,仅起到兼容作用。
readfds:           可选)指针,指向一组等待可读性检查的套接口。
writefds:         (可选)指针,指向一组等待可写性检查的套接口。
exceptfds:        (可选)指针,指向一组等待错误检查的套接口。
timeout:           select()最多等待时间,对阻塞操作则为NULL。

 

USHORT nPort = 4567;    // 此服务器监听的端口号

// 创建监听套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    
sockaddr_in sin;
sin.sin_family 
= AF_INET;
sin.sin_port 
= htons(nPort);
sin.sin_addr.S_un.S_addr 
= INADDR_ANY;
// 绑定套节字到本地机器
if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
{
    printf(
" Failed bind() \n");
    
return -1;
}
// 进入监听模式
::listen(sListen, 5);

 

 

// select模型处理过程
// 1)初始化一个套节字集合fdSocket,添加监听套节字句柄到这个集合
fd_set fdSocket;        // 所有可用套节字集合
FD_ZERO(&fdSocket);
FD_SET(sListen, 
&fdSocket);


while(TRUE)
{
    
// 2)将fdSocket集合的一个拷贝fdRead传递给select函数,
    
// 当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套节字句柄,然后返回。
    fd_set fdRead = fdSocket;
    
int nRet = ::select(0&fdRead, NULL, NULL, NULL);
    
if(nRet > 0)
    {
        
// 3)通过将原来fdSocket集合与select处理过的fdRead集合比较,
        
// 确定都有哪些套节字有未决I/O,并进一步处理这些I/O。
        for(int i=0; i<(int)fdSocket.fd_count; i++)
        {
            
if(FD_ISSET(fdSocket.fd_array[i], &fdRead))
            {
                
if(fdSocket.fd_array[i] == sListen)        // (1)监听套节字接收到新连接
                {
                    
if(fdSocket.fd_count < FD_SETSIZE)
                    {
                        sockaddr_in addrRemote;
                        
int nAddrLen = sizeof(addrRemote);
                        SOCKET sNew 
= ::accept(sListen, (SOCKADDR*)&addrRemote, &nAddrLen);

                        FD_SET(sNew, 
&fdSocket);
                        printf(
"接收到连接(%s)\n", ::inet_ntoa(addrRemote.sin_addr));
                    }
                    
else
                    {
                        printf(
" Too much connections! \n");
                        
continue;
                    }
                }
                
else
                {
                    
char szText[256];
                    
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
                    
if(nRecv > 0)                        // (2)可读
                    {
                        szText[nRecv] 
= '\0';
                        printf(
"接收到数据:%s \n", szText);
                    }
                    
else                                // (3)连接关闭、重启或者中断
                    {
                        ::closesocket(fdSocket.fd_array[i]);
                        
                        printf(
"关闭\n");
                        FD_CLR(fdSocket.fd_array[i], 
&fdSocket);
                    }
                }
            }
        }
    }
    
else
    {
        printf(
" Failed select() \n");
        
break;
    }
}

posted @ 2011-08-04 20:17  稳扎稳打  Views(407)  Comments(1Edit  收藏  举报