zoukankan      html  css  js  c++  java
  • 《网络编程》先进 I/O

    这部分是高级插座 I/O 。

    设置套接字超时报警,使用更方便的数据传输功能。

    套接字 I/O 设置操作超时有三种方法:

    1. 转让 alarm 性能,制作时,它指定超时 SIGALRM 信号;
    2. 在 select 函数中设置超时堵塞等待 I/O,以替代直接堵塞在 read 或write 调用上;
    3. 使用 SO_RCVTIMEO 和 SO_SNDTIMEO 套接字选项(这两个选项仅仅是一部分实现支持)。

          以下使用 alarm 产生的 SIGALRM 信号为 connect 函数设置超时,当然系统会为 connect 函数设置超时限制,这里我们仅仅是表示 SIGALRM 信号在设置超时的作用。

    #include <signal.h>
    #include <errno.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/socket.h>
    
    typedef void Sigfunc(int);
    
    extern void err_sys(const char *,...);
    static Sigfunc *M_signal(int signo, Sigfunc *func);
    static Sigfunc *MySignal(int signo, Sigfunc *func);
    static void connect_alarm(int);
    
    int Myconnect_timo(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec)
    {
        Sigfunc *sigfunc;
        int n;
    
        /* SIGALRM 信号处理函数。并保存现有信号处理函数 */
        sigfunc = MySignal(SIGALRM, connect_alarm);
    
        /* 设置alarm超时 */
        if(alarm(nsec) != 0)/* 若已经设置了超时,则alarm返回的是当前剩余秒数。否则返回0 */
            printf("alarm was already set
    ");/* 提示:已经设置过alarm超时 */
    
        if( (n = connect(sockfd, saptr, salen)) < 0)
        {/* 由超时处理函数调用中断导致连接失败,则关闭套接字,并设置是由超时导致的失败 */
            close(sockfd);
            if(errno == EINTR)
                errno = ETIMEDOUT;
        }
        /* 关闭 alarm */
        alarm(0);
        /* 恢复原来的处理函数 */
        MySignal(SIGALRM, sigfunc);
    
        return(n);
    }
    
    static void connect_alarm(int signo)
    {
        printf("flag: %d
    ", signo);
        return;/* just interrupt the connect */
    }
    
    
    static Sigfunc *MySignal(int signo, Sigfunc *func)
    {
        Sigfunc *sigfunc;
        if( (sigfunc = M_signal(signo, func)) == SIG_ERR)
            err_sys("signal error");
        return (sigfunc);
    }
    
    static Sigfunc *M_signal(int signo, Sigfunc *func)
    {
        struct sigaction act, oact;
    
        /* 设置信号处理函数 */
        act.sa_handler = func;
        /* 初始化信号集 */
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        if(signo == SIGALRM)
        {/* 若是SIGALRM信号,则系统不会自己主动重新启动 */
    #ifdef SA_INTERRUPT
            act.sa_flags |= SA_INTERRUPT;
    #endif
        }
        else
        {/* 其余信号设置为系统会自己主动重新启动 */
    #ifdef SA_RESTART
            act.sa_flags |= SA_RESTART;
    #endif
        }
        /* 调用 sigaction 函数 */
        if(sigaction(signo, &act, &oact) < 0)
            return(SIG_ERR);
        return(oact.sa_handler);
    }
    

    使用 select 函数为 recvfrom 设置超时。有关select 函数的解说能够參考文章《I/O 多路复用》。

    #include <sys/select.h>
    #include <stdlib.h>
    extern void err_sys(const char *,...);
    
    /* 在指定的时间内等待描写叙述符变为可读 */
    int readable_timeo(int fd, int sec)
    {
    	fd_set			rset;
    	struct timeval	tv;
    
        /* 初始化fd_set 结构,并加入描写叙述符 */
    	FD_ZERO(&rset);
    	FD_SET(fd, &rset);
    
        /* 设置超时的时间 */
    	tv.tv_sec = sec;/* 秒数 */
    	tv.tv_usec = 0;/* 微秒 */
    
        /* 调用select函数,使进程堵塞于select的超时等待描写叙述符变为可读 */
    	return(select(fd+1, &rset, NULL, NULL, &tv));
    		/* 4> 0 if descriptor is readable */
    }
    /* end readable_timeo */
    
    int
    Read_timeo(int fd, int sec)
    {
    	int		n;
    
    	if ( (n = readable_timeo(fd, sec)) < 0)
    		err_sys("readable_timeo error");
    	return(n);
    }
    

            使用 使用 SO_RCVTIMEO 和 SO_SNDTIMEO 套接字选项,使用这两个套接字选项设置描写叙述符时,其超时设置将应用于该描写叙述符的全部读或写操作上。SO_RCVTIMEO 套接字选项仅仅能应用于读操作,而 SO_SNDTIMEO 套接字选项仅仅能应用于写操作。

    这两者仅支持一部分实现。比如在 connect 函数不能使用这两个套接字选项。


    recv 和 send 函数

    /* 传输数据 */
    /*
     * 函数功能:发送数据;
     * 返回值:若成功则返回发送的字节数。若出错则返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    
    ssize_t send(int sockfd, void *buff, size_t nbytes, int flags);
    /*
     * 说明
     * 该函数的功能相似与write函数,除了有标识符flags之外。其它的同样;
     * flags标识符的取值例如以下:
     * (1)MSG_DONTROUTE   勿将数据路由出本地网络
     * (2)MSG_DONTWAIT    同意非堵塞操作
     * (3)MSG_EOR         假设协议支持,此为记录结束
     * (4)MSG_OOB         假设协议支持。发送带外数据
     *
     * 若send成功返回,并不必定表示连接还有一端的进程接收数据,仅仅能说数据已经无错误地发送到网络。
     *
     * 对于支持为报文设限的协议,若报文超过协议所支持的最大尺寸,send失败并将errno设为EMSGSIZE;
     * 对于字节流协议。send会堵塞直到整个数据被传输;
     */
    
    /*
     * 函数功能:接收数据;
     * 返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0。若出错则返回-1;
     * 函数原型:
     */
    #include <sys/socket.h>
    
    ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
    /*
     * 说明
     * 该函数的功能相似与read函数,除了有标识符flags之外,其它的同样;
     * flags标识符的取值例如以下:
     * (1)MSG_PEEK        返回报文内容而不真正取走报文。即查看可读取数据
     * (2)MSG_TRUNC       即使报文被截断,要求返回的是报文的实际长度
     * (3)MSG_WAITALL     等待直到全部数据可用
     * (4)MSG_OOB         假设协议支持,发送带外数据
     * (5)MSG_DONTWAIT    同意非堵塞操作
     *
     */
    
    以下是 flags 标志的功能:

    1. MSG_DONTROUTE:本标志告知内核。目的主机在某个直接连接的本地网络上,因而无需运行路由表查找。
    2. MSG_DONTWAIT:本标志在无需打开对应套接字的非堵塞标志的前提下,把单个 I/O 操作暂时指定为非堵塞,接着运行 I/O 操作,然后关闭非堵塞标志;
    3. MSG_OOB:若协议支持,则可发送带外数据;
    4. MSG_PEEK :本标志适用于 recv 和 recvfrom 函数,它同意查看已可读取的数据,并且系统不在 recv 或 recvfrom 返回后丢弃这些数据。
    5. MSG_WAITALL:等待全部数据可用。

    readv 和 writev 函数

    这两个函数类似于 read 和 write 函数,可是它们支持多个缓冲区操作。其定义例如以下:

    /* 读、写多个非连续的缓冲区 */
    
    /*
     * 函数功能:读取数据到多个非连续的缓冲区,或从多个非连续缓冲区写数据到文件。
     * 返回值:若成功则返回已读、写的字节数,若出错则返回-1。
     * 函数原型:
     */
    #include <sys/uio.h>
    ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
    ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
    /*
     * 说明:
     * iovec的指针结构例如以下:
     */
    struct iovec
    {
        void *iov_base;     /* starting address of buffer */
        size_t iov_len;     /* size of buffer */
    };
    


    recvmsg 和 sendmsg 函数

    这两个函数是通用的 I/O 函数,前面介绍的 I/O 函数都能够使用这两个函数替换。其定义例如以下:

    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    
    ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
    /*
     * 说明
     * 该函数能够使用不止一个的选择来通过套接字发送数据,能够指定多重缓冲区数据传输,相似与readv函数;
     * msghdr结构至少包括下面成员:
     */
    struct msghdr
    {
        void        *msg_name;      /* optional address */
        socklen_t   msg_namelen;    /* address size in bytes */
        struct iovec *msg_iov;      /* array of IO buffers */
        int         msg_iovlen;     /* number of elements in array */
        void        *msg_control;   /* ancillary data */
        socklen_t   msg_controllen; /* number of ancillary bytes */
        int         msg_flags;      /* flags for recevied message */
    };
    


    参考资料:

    《Unix 网络编程》

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    课堂测试-文本操作
    异常的总结
    动手动脑实验-异常
    从小工到专家-读后感3
    从小工到专家-读后感2
    从小工到专家-读后感1
    构建之法读书笔记(一)
    2.12日总结
    BaseAdapter的使用
    Activity之Bundle的使用
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4875773.html
Copyright © 2011-2022 走看看