非阻塞套接字的模式

非阻塞套接字的模式

 

1 .非阻塞套接字的模式
(
1 )服务器端
    通常socket运行后默认为阻塞模式。要调用ioctlsocket函数设置非阻塞模式。
如:

    WSAData Data;
    WSAStartup(MAKEWORD(
2 2 ),  & Data);
    SerSocket 
=  socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
if (INVALID_SOCKET  ==  SerSocket)
        cout
<< " Invalid Socket!\n " ;
    u_long iMode 
=   1 ;
    ioctlsocket(SerSocket, FIONBIO, 
& iMode);
    
    在接受客户端请求的线程中,若接受成功就返回客户端的套接字,否则返回INVALID_SOCKET错误,
若错误代码为WSAEWOULDBLOCK,说明当前没有客户端请求。
如:
// 接受客户端请求线程
DWORD WINAPI AcceptClientPro(LPVOID LpP)
{
    SOCKADDR_IN ClientAdrr;
    
int  AddrLen  =   sizeof (SOCKADDR);
    
// 非阻塞模式
     while  ( ! IsConnet)
    {
        ClientSock 
=  accept(SerSocket, (SOCKADDR  * ) & ClientAdrr,  & AddrLen );
        
if (INVALID_SOCKET  ==  ClientSock)
        {
            
int  n  =  WSAGetLastError();
            
// 没有客户端请求
             if (WSAEWOULDBLOCK  ==  n)    
            {
                cout
<< " 没有客户端发出请求! " << endl;
                Sleep(
1000 );
                
continue ;
            }
else
            {
                cout
<< " 出现错误! " << endl;
                Sleep(
1000 );
            }
        }
else
        {
            cout
<< " 已连接客户端! " << endl;
            IsConnet 
=   true ;
            
break ;
        }
    }
    
return   0 ;
}
    
    就recv函数来说,在阻塞模式中,如果没有客户端发送数据过来,线程到这里会阻塞,直到有数
据发送过来为止。在非阻塞模式中,没有客户端发送数据过来,返回SOCKER_ERROR,错误代码为WSAEWOULDBLOCK。
如:
// 接收数据线程
DWORD WINAPI ReceiveDataPro(LPVOID LpP)
{
    
while ( ! IsConnet);         // 保证连接后再接受数据
     while ( 1 )
    {
        
if (IsReadyRecei)         // 保证缓冲区在未处理时不受新来的数据的影响
        {
            
            
int  ReceiLen  =  recv(ClientSock, ( char   * ) & DataPack,  sizeof (DataPack),  0 );
            
if (SOCKET_ERROR  ==  ReceiLen)
            {
                
int  Err  =  WSAGetLastError();
                
if (WSAEWOULDBLOCK  ==  Err)
                {
                    cout
<< " 没有收到数据 " << endl;
                    
continue ;
                }
                
else   if  (WSAENETDOWN  ==  Err  || // 客户端关闭了连接
                    WSAETIMEDOUT  ==  Err  ||
                    WSAECONNRESET 
==  Err )    
                {
                    cout
<< " 服务器关闭了连接 " << endl;
                    
break ;
                }
            }
            
if ( 0   ==  ReceiLen)         // 客户端关闭了连接
            {
                cout
<< " ReceiLen = 0 " << endl;
                
break ;
            }
            
if (ReceiLen  >=   sizeof (DataPack))         // 成功接收
            {
                cout
<< " 已收到数据: " << DataPack.buf << endl;
                IsReadyRecei 
=   false ;
                
break ;
            }
        }
    }
    
return   0 ;
}    

(
2 )客户端
    在客户端的连接请求线程中,connect函数会返回SOCKET_ERROR,这并不是说明连接失败,具体情况
要看它的WSAGetLastError()返回值,若它三次返回SOCKET_ERROR的Error代码依次为WSAEWOULDBLOCK,
WSAEINVAL,WSAEISCONN,就说明连接服务器成功,否则失败。但有的时候WSAEINVAL没有出现就有WSAEISCONN
了,所以我还是以WSAEISCONN为连接完成的标志,但要注意其实在三次返回代码中,第一次的WSAEWOULDBLOCK
之前的connect操作就成功了,如果没出意外服务器就要响应了。
如:
// 连接服务器线程
DWORD WINAPI ConnetServerPro(LPVOID LpP)
{
    SOCKADDR_IN ServerAddr;
    ServerAddr.sin_family 
=  AF_INET;
    ServerAddr.sin_port 
=  htons( 1200 );
    ServerAddr.sin_addr.s_addr 
=  inet_addr( " 192.168.1.100 " );
    
int  BlockFlag  =   0 ;
    
int  InvalFlag  =   0 ;
    
while  ( ! IsConnet)
    {
        
int  nResu  =  connect(ClientSock, (SOCKADDR  * ) & ServerAddr,  sizeof (SOCKADDR));
        
if (SOCKET_ERROR  ==  nResu)
        {
            
int  n  =  WSAGetLastError();
            
            
if (WSAEWOULDBLOCK  ==  n )         // 不能立即完成
            {
                cout
<< " 过程1! " << endl;
                BlockFlag
++ ;
                
continue ;
            }
            
else   if (WSAEINVAL  ==  n)         // 监听状态
            {
                cout
<< " 过程2! " << endl;
                InvalFlag
++ ;
                
continue ;
            }
            
else   if (WSAEISCONN  ==  n)         // 连接完成
            {
                cout
<< " 已连接服务器! " << endl;
                IsConnet 
=   true ;
                
break ;
            }
            
else  
            {
                cout
<< " 出现其他错误!\n " << endl;
                Sleep(
1000 );
            }
        }
    }

    
return   0 ;
}

    在发送数据线程中,send()返回的是发送数据的长度说明发送成功;返回SOCKET_ERROR时,若
错误代码为WSAEWOULDBLOCK就再重试,不是WSAEWOULDBLOCK就说明有错误。】
如:
// 发送数据线程
DWORD WINAPI SendDataPro(LPVOID LpP)
{
    
while ( ! IsConnet);     // 保证已连接服务器
     while ( 1 )
    {
        
int  len  =  send(ClientSock, ( char   * ) & DataPack, DataPack.Head.len,  0 );
        
if (SOCKET_ERROR  ==  len)
        {
            
int  Error  =  WSAGetLastError();
            
if (WSAEWOULDBLOCK  ==  Error)
                
continue ;
        }
        
else          // 发送成功
        {
            cout
<< " 发送成功! " << endl;
            
break ;
        }
    }

    
return   0 ;
}

你可能感兴趣的:(非阻塞套接字的模式)