• 【转】【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

  • 相关阅读:
    MVC提交时验证
    远程计划任务管理
    教你一步一步部署.net免费空间OpenShift系列之四------绑定域名、使用CDN加速
    启用IIS7报错功能
    教你一步一步部署.net免费空间OpenShift系列之三------上传ASP.net程序
    教你一步一步部署.net免费空间OpenShift系列之二------创建应用
    Spring SimpleJdbcOperations 批量更新
    c#获取已安装的所有NET版本
    (转载)数据库效率提高的方案
    linux两台服务器之间文件/文件夹拷贝
  • 原文地址:https://www.cnblogs.com/zhanjxcom/p/5252077.html
Copyright © 2020-2023  润新知