zoukankan      html  css  js  c++  java
  • 【转】【win网络编程】socket中的recv阻塞和select的用法

    在编写ftp客户端程序时,在联通后使用recv函数进行接收欢迎信息时,需要申请内存进行接收数据保存,一次读取成功,但是由于一个随机的ftp服务端在说,欢迎信息的大小是不知道的,所以在尝试使用死循环,在阅读recv的说明时讲到返回值即是接收到的字节数,那么返回0的时候就代表结束了,实践发现recv是个阻塞函数,在连接不断开的情况下,会一直处于阻塞状态,也不会返回0.也就是说程序不能这么一直读,如果对端连接没有关闭,则在没有数据的情况下,调用recv会阻塞,如果对端关闭连接,则立即返回0.

    所以就需要使用到select函数来操作。

    MSDN中对select的介绍连接为:ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/winsock/winsock/select_2.htm

    select的功能为检测一个或者多个socket是否可读或者可写,或者有错误产生。根据设置可以处于阻塞、非阻塞、等待固定时间返回。

    原型:

    select Function

    The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

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

    Parameters

    nfds

    Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.

    忽略。nfds参数在这里只是为了和伯克利套接字相兼容。(这个参数在linux中有意义)

    readfds

    Optional pointer to a set of sockets to be checked for readability.

    指向一组等待判断可读性的socket的指针,可不设置。

    writefds

    Optional pointer to a set of sockets to be checked for writability.

    指向一组等待判断可写性的socket的指针,可不设置。

    exceptfds

    Optional pointer to a set of sockets to be checked for errors.

    和上面两个一样,指向待检测是否发生错误的一组socket的指针,可不设置。

    timeout

    Maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.

    select函数最大等待时间,使用TIMEVAL结构体。设置为NULL时阻塞。

    TIMEVAL结构体定义如下:

    typedef struct timeval {
      long tv_sec;    //秒
      long tv_usec;   //毫秒
    } timeval;

    Return Value

            返回fd_set结构中准备好的(可读、可写或者发生错误)socket句柄的总个数。等待时间到则返回0,发生错误返回SOCKET_ERROR。
     
     
    操作fs_set结构

            windows提供几个宏对fs_set结构进行操作:

    FD_CLR(s, *set)

    Removes the descriptor s from set.

    从fd_set集合中移除一个描述符。

    FD_ISSET(s, *set)

    Nonzero if s is a member of the set. Otherwise, zero.

    检测一个描述符是否是fd_set集合的可读或者可写成员,不在返回0,是返回非0.

    FD_SET(s, *set)

    Adds descriptor s to set.

    向fs_set集合中添加一个描述符。

    FD_ZERO(*set)

    Initializes the set to the null set.

    初始化fd_set集合为NULL。

    例子:

    以读取FTP服务器的欢迎信息为例。

    注意:在使用过程中如果只是想检测可读,千万不要在写检测的参数里同时赋值。我在写例子的过程中不小心将同一个rfds同时赋在了读写参数里,结果是虽然不可读了,但是select仍然返回非0值,因为同一个socket可写。找了半天才发现错误。

    这样就不用担心申请的内存空间不能一次读完缓冲区了。也不用担心recv一直阻塞在那里了。

    #include<stdio.h>
    #include <winsock2.h>
    #include <string.h>
    
    int main(void)
    {
        SOCKET fp;
        FILE * ffp;
        struct fd_set rfds;
        struct sockaddr_in ipadd;
        struct timeval timeout = {3,0};
        char * readbuff[10] = {0};
        WSADATA wData;
    
        WSAStartup(0x0202,&wData);
        fp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        memset(&ipadd, 0, sizeof(struct sockaddr_in));
        ipadd.sin_family = AF_INET;
        ipadd.sin_addr.s_addr = inet_addr("192.168.1.101");
        ipadd.sin_port = htons(21);
    
        if(0 != connect(fp, &ipadd, sizeof(struct sockaddr_in)))
        {
            printf("
    error");
        }
        ffp = fopen("test.txt", "rw+");
        int ret;
        while(1)
        {
            FD_ZERO(&rfds);  /* 清空集合 */
            FD_SET(fp, &rfds);  /* 将fp添加到集合,后面的FD_ISSET和FD_SET没有必然关系,这里是添加检测 */
    
            ret=select(0, &rfds, NULL, NULL, &timeout);
            printf("
    select ret = %d",ret);
            if(0 > ret)
            {
                    closesocket(fp);
                    fclose(ffp);
                    return -1;
            }
            else if(0 == ret)
            {
                break;
            }
            else
            {
                if(FD_ISSET(fp,&rfds))  /* 这里检测的是fp在集合中是否状态变化,即可以操作。 */
                {
                    ret = recv(fp, readbuff, 9, 0);
    <span style="white-space:pre">        </span>if(0 == ret) return 0;    /* 此处需要检测!否则ftp发送数据时,后面会循环接收到0字节数据 */
                    // printf("
    %s",readbuff);
                    fputs(readbuff, ffp);
                    memset (readbuff,0,10);
                }
            }
    
    
    
        }
        printf("
    read successful!");
        fclose(ffp);
        closesocket(fp);
    }

     见  http://blog.csdn.net/kikilizhm/article/details/8201512

  • 相关阅读:
    js中的原生Ajax和JQuery中的Ajax
    this的用法
    static的特性
    时政20180807
    java compiler没有1.8怎么办
    Description Resource Path Location Type Java compiler level does not match the version of the installed Java project facet Unknown Faceted Project Problem (Java Version Mismatch)
    分词器
    [数算]有一个工程甲、乙、丙单独做,分别要48天、72天、96天完成
    一点感想
    解析Excel文件 Apache POI框架使用
  • 原文地址:https://www.cnblogs.com/zhanjxcom/p/5252077.html
Copyright © 2011-2022 走看看