zoukankan      html  css  js  c++  java
  • 基于select模型的udp客户端实现超时机制

    参考:http://www.cnblogs.com/chenshuyi/p/3539949.html

    多路选择I/O — select模型

    其思想在于使用一个集合,该集合中包含需要进行读写的fd,通过轮询这个集合,直到有一个fd可读写,才返回。与阻塞I/O不同的是,阻塞I/O仅使用了一次系统调用,就是对fd的读写,如果没有fd处于就绪状态,则进程一直阻塞,而多路选择I/O使用了两次系统调用,第一次是轮询并返回可读写fd数,第二次是对fd进行读写,阻塞只发生在轮询fd的过程。

    select函数的原型(sys/select.h)

    1 int select (int __nfds, fd_set *__restrict __readfds,
    2          fd_set *__restrict __writefds,
    3          fd_set *__restrict __exceptfds,
    4          struct timeval *__restrict __timeout);

    (1)__nfds

        需轮询的最大文件描述符数。如__nfds = 10,则轮询值为0~9的fd,单个进程中,最多可打开1024个fd,该值在sys/select.h中的FD_SETSIZE定义。用户可通过“ulimit -n”查看该值,通过打印/proc/sys/fs/file-max中的值查看系统可打开的最大fd数。

    (2)__readfds,__writefds,__exceptfds

        分别代表用户关心的可读、可写、异常的fd,这三个参数的数据类型是fd_set *,这是一组文件描述符的集合,使用一个位来代表一个fd。

    fd_set位向量操作函数包括

    1 #define FD_SET(fd, fdsetp)   __FD_SET (fd, fdsetp)   //将指定的fd置1
    2 #define FD_CLR(fd, fdsetp)   __FD_CLR (fd, fdsetp)   //将指定的fd清0
    3 #define FD_ISSET(fd, fdsetp) __FD_ISSET (fd, fdsetp) //测试fd状态,如被置1,返回非0,否则返回0
    4 #define FD_ZERO(fdsetp)      __FD_ZERO (fdsetp)      //将所有的fd清0

    (3)__timeout

        timeout的数据类型是timeval结构体。通过填充该结构体,设置超时时间,精确到微妙级,如果该参数设置为NULL,则一直等待,直到有fd可读写。如果tv_sec和tv_usec都设置为0,则轮询完指定fd后,立即返回。

    1 struct timeval
    2 {
    3     __time_t tv_sec;          /* Seconds.  */
    4     __suseconds_t tv_usec;    /* Microseconds.  */
    5 };

    (4)select有三种返回值

    -1  :出错

    0   :如果设置了超时,在指定时间内没有fd可读写,则返回0,可在此指定相应的超时处理操作。

    >0 :返回可读写的fd数

    可屏蔽信号的select — pselect

    从原型上看,pselect函数与select函数的区别在于设置超时的结构体不同,以及多了个用于屏蔽信号的参数。如果__sigmask设置为NULL,则与select一样。

    1 int pselect (int __nfds, fd_set *__restrict __readfds,
    2             fd_set *__restrict __writefds,
    3             fd_set *__restrict __exceptfds,
    4             const struct timespec *__restrict __timeout,
    5             const __sigset_t *__restrict __sigmask);

    (1)timespec结构体的定义如下,它精确到纳秒级。

    1 struct timespec
    2 {
    3     __time_t tv_sec;         /* Seconds.  */
    4     long int tv_nsec;        /* Nanoseconds.  */
    5 };

    (2)__sigmask实际上是信号的位向量。数据类型是sigset_t,定义如下

    1 /* A `sigset_t' has a bit for each signal.  */
    2 
    3 # define _SIGSET_NWORDS    (1024 / (8 * sizeof (unsigned long int)))
    4 typedef struct
    5 {
    6     unsigned long int __val[_SIGSET_NWORDS];
    7 } __sigset_t;

    测试代码:

    //client
    #include <Winsock2.h>
    #include <stdio.h>
    #pragma comment (lib,"Ws2_32.lib")
    
    #define MAX_LINE 80
    #define PORT 8000
    typedef int socklen_t;
    
    int main(int argc, char *argv[])
    {
        struct sockaddr_in   sin, cin;
        socklen_t addr_len;
    
        int sockfd, maxfdp;
        char *msg = "client";
        char buf[MAX_LINE];
    
        fd_set fds;
        struct timeval timeout;
        int interval = 3;
    
        memset(&sin, 0,sizeof(sin));
    
        sin.sin_family = AF_INET;
        //inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr);
        sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
        sin.sin_port = htons(PORT);
    
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
            perror("fail to create socket.
    ");
            exit(1);
        }
    
        addr_len = sizeof(cin);
    
        while (1) {
    
            timeout.tv_usec = 0;
            timeout.tv_sec = interval;
    
            FD_ZERO(&fds);
            FD_SET(sockfd, &fds);
    
            maxfdp = sockfd + 1;
    
            sendto(sockfd, msg, strlen(msg) + 1, 0, (struct sockaddr*)&sin, sizeof(sin));
    
            switch (select(maxfdp, &fds, NULL, NULL, &timeout)) {
            case -1:
                perror("error");
                exit(-1);
                break;
    
            case 0:
                printf("timeout.
    ");
                break;
    
            default:
                if (FD_ISSET(sockfd, &fds)) {
    
                    if (recvfrom(sockfd, buf, MAX_LINE, 0, (struct sockaddr *)&cin, &addr_len) < 0) {
                        perror("fail to receive.
    ");
                        exit(1);
                    }
                    else {
                        printf("receive from server: %s.
    ", buf);
    
                        if (closesocket(sockfd) < 0) {
                            perror("fail to close.
    ");
                            exit(1);
                        }
                        return 0;
                    }
                }
                break;
            }
    
        }
        return 0;
    }
    //Server
    #pragma
    comment (lib,"ws2_32.lib") #include <Winsock2.h> #include <stdio.h> #define MAX_LINE 80 #define PORT 8000 int main(int argc, char *argv[]) { struct sockaddr_in sin, cin; int addr_len; int sockfd, n; char *msg = "server"; char buf[MAX_LINE]; memset(&sin,0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htons(INADDR_ANY); sin.sin_port = htons(PORT); if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("fail to create socket. "); exit(1); } n = bind(sockfd, (struct sockaddr*)&sin, sizeof(sin)); if (n < 0) { perror("fail to bind. "); exit(1); } while (1) { addr_len = sizeof(cin); recvfrom(sockfd, buf, MAX_LINE, 0, (struct sockaddr*)&cin, &addr_len); sendto(sockfd, msg, strlen(msg) + 1, 0, (struct sockaddr*)&cin, addr_len); } }

    测试:链接失败。。。

                             
  • 相关阅读:
    怎么把自己电脑上的文件传到服务器本地上
    查看hive中某个表中的数据、表结构及所在路径
    python2.7读汉字的时候出现乱码,如何解决
    如何连接服务器客户端
    java常用问题排查工具
    netty源码分析之一:server的启动
    java AQS 一:
    netty源码分析之二:accept请求
    java Resource
    二:基础概述netty
  • 原文地址:https://www.cnblogs.com/ranjiewen/p/5618651.html
Copyright © 2011-2022 走看看