zoukankan      html  css  js  c++  java
  • 套接字的非阻塞模式

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

        WSAData Data;
        WSAStartup(MAKEWORD(
    22), &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;
    }
    兴趣是学习的动力。
  • 相关阅读:
    Delphi从Internet下载文件
    datasnap 上传/下载大文件(本Demo以图传片文件为例)
    delphi 理解ParamStr
    delphi2010多线程编程教程
    QQ2008自动聊天精灵delphi源码
    Delphi使用Indy、ICS组件读取网页
    UniDac 使用日记(转)
    delphi xe5 安卓 配置sqlite
    Netty内存管理器ByteBufAllocator及内存分配
    初识内存分配ByteBuf
  • 原文地址:https://www.cnblogs.com/yunboy4/p/1539103.html
Copyright © 2011-2022 走看看