• 设置非阻塞的套接字Socket


    当使用socket()函数和WSASocket()函数创建套接字时,默认都是阻塞的。在创建套接字之后,通过调用ioctlsocket()函数,将该套接字设置为非阻塞模式。函数的第一个参数是套接字,第二个参数设置为FIONBIO,第三个参数设置为unsigned long类型的非零值。下面代码清单演示了如何用ioctlsocket()函数,设置套接字为非阻塞模式。

    SOCKET            s;                                                                       //套接字

    unsigned long ul = 1;                                                                          //设置套接字选项

    int                        nRet;                                                                 //返回值

    s = socket(AF_INET, SOCK_STREAM, 0);                            //创建套接字

    nRet = ioctlsocket(s, FIONBIO, (unsigned long*)&ul);                 //设置套接字非阻塞模式

    if (nRet == SOCKET_ERROR)

    {

             //设置套接字非阻塞模式,失败处理

    }

    套接字设置为非阻塞模式后,在调用Windows Sockets API函数时,调用函数会立即返回。大多数情况下,这些函数调用都会调用“失败”,并返回WSAEWOULDBLOCK错误代码。说明请求的操作在调用期间内没有时间完成。通常,应用程序需要重复调用该函数,直到获得成功返回代码。下面程序清单示例了一个在非阻塞套接字上反复调用recv()函数,直到收到1024个字节的数据。

    #define                        NUM_REQUIRED              1024         //需要读入数据的大小

    #define                        MAX_SIZE                            2048         //输入缓冲区的大小

    TCHAR                        buff[MAX_SIZE];                            //输入缓冲区

    bool                    close;                                                      //对方关闭了连接

    SOCKET                     sock;                                              //Windows sockets

    void ReadData(void)

    {

             int nTotal = 0;                                                            //已经读入缓冲区字节数

             int nRead = 0;                                                           //在调用recv时实际读入字节数

             int nLeft = 0;                                                              //剩下数据的字节数

             int nBytes = 0;                                                           //当前已读数据在缓冲区的位置

            

             nLeft = NUM_REQUIRED;

             while (nTotal != NUM_REQUIRED)//已经读入缓冲区的字节数不等于需要读入的大小时

             {

                       nRead = recv(sock, &buff[MAX_SIZE - nBytes], nLeft, 0);   //接收数据

                      

                       if(SOCKET_ERROR == nRead)                                              //读操作失败

                       {

                                int err = WSAGetLastError();

                                if(WSAEWOULDBLOCK == err)                                     //接收数据缓冲区不可用

                                {

                                         continue;                                                                   //接着读取数据

                                }else if(WSAETIMEDOUT == err || WSAENETDOWN == err)      //连接已经断开

                                {

                                         close = TRUE;                                                                            //函数退出

                                         break;

                                }

                       }

                      

                       if( 0 == nRead)                                      //客户端关闭了连接

                       {

                                close = TRUE;                              //函数退出

                                break;

                       }

                      

                       nTotal += nRead;

                       nLeft -= nRead;

                       nBytes += nRead;

             }

             return;

    }

    在该程序中,通过调用WSAGetLastError()函数获得recv()函数返回的错误代码。当返回WSAEWOULDBLOCK错误时,说明此时套接字的缓冲区还没有准备好的数据。需要继续调用该函数。

    在该程序中,还对recv()函数返回的其他错误代码进行处理。WSAETIMEDOUT和WSAENETDOWN错误说明,此时由于网络系统的原因与对方的连接已经断开了。当函数返回0时,说明对方关闭了连接。在程序中通过设置close布尔变量值为TRUE,表明与对方的连接已经断开。调用break语句跳出while循环体,函数退出。在开发中,应该根据具体情况对函数返回的错误值进行具体处理。

    不同的Windows Sockets API函数,在调用失败时返回的WSAEWOULDBLOCK错误代码具有不同的含义。表对几个Windows Sockets API函数返回WSAEWOULDBLOCK错误的含义进行了总结。

    表  WSAEWOULDBLOCK的含义

    函数名

    说明

    accept()和WSAAcept()

    应用程序没有收到连接请求

    recv()、WSARecv()、recvfrom()和WSARecvfrom()

    接收缓冲区没有收到数据

    send()、WSASend()、sendfrom()和WSASendfrom()

    发送缓冲区此时不可用

    connect()和WSAConnect()

    连接未能立即完成

    closescoket()

    通常情况下意味着应用程序使用SO_LINGER选项并且设置了一个非零的超时值,调用了setsocketopt()函数

    需要说明的是并非所有的Windows Sockets API在非阻塞模式下调用,都会返回WSAEWOULDBLOCK错误。例如,以非阻塞模式的套接字为参数调用bind()函数时,就不会返回该错误代码。当然,在调用WSAStartup()函数时更不会返回该错误代码,因为该函数是应用程序第一调用的函数,当然不会返回这样的错误代码。

    要将套接字设置为非阻塞模式,除了使用ioctlsocket()函数之外,还可以使用WSAAsyncselect()和WSAEventselect()函数。当调用该函数时,套接字会自动地设置为非阻塞方式。在后续章节中,讲解该函数的使用方法。

  • 相关阅读:
    shell的执行顺序问题
    七层负载均衡——HAProxy
    不要自以为是码农
    SSL协议运行机制
    Linux启动流程
    MIM协议与Base64编码
    Adele的生活
    你值得拥有:25个Linux性能监控工具
    [Zabbix] 如何实现邮件报警通知以及免费短信报警通知
    php.ini中date.timezone设置分析
  • 原文地址:https://www.cnblogs.com/oneway1990/p/7843562.html
Copyright © 2020-2023  润新知