C/S 超时控制的几种写法

第一种:调用alarm(second)超时时,发送SIGALRM信号,产生中断.
实例:回显C/S

服务端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFSIZE 4096
#define LISTENQ 100
void sig_child(int signo) {
    int ppid;
    while ((ppid = waitpid(-1,NULL,WNOHANG)) > 0) {
        printf("child: %d terminate\n",ppid);
    }
}
void sig_alarm(int signo) {
    return;
}
int tcp_send(int sockfd,char* buf,int len,int second) {
    struct sigaction oact,act;
    act.sa_handler = sig_alarm;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGALRM,&act,&oact) < 0) {
        printf("tcp_send: sigaction error: %s\n",strerror(errno));
        exit(1);
    }   
    if (alarm(second) != 0) {
        printf("tcp_send: already set alarm\n");
        exit(1);
    }

    if (write(sockfd,buf,len) != len) {
        if (errno == EINTR) {
            errno = ETIMEDOUT;
            printf("tcp_send: send message to client timeout\n");
            exit(1);
        }
        printf("write error: %s\n",strerror(errno));
        exit(1);
    }
    alarm(0);
    if (sigaction(SIGALRM,&oact,NULL) < 0) {
        printf("tcp_send: sigaction error: %s\n",strerror(errno));
        exit(1);
    }
}

void server_echo(int sockfd) {
    char recv[BUFSIZE];
    int n;
    while ((n = read(sockfd,recv,BUFSIZE)) > 0) {
        //sleep(8);
        tcp_send(sockfd,recv,n,2);
    }
}

int main(int argc,char** argv) {
    if (argc != 2) {
        printf("please add \n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }

    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }
        if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
            break;
        }
        close(sockfd);
    }
    if (dummy == NULL) {
        freeaddrinfo(results);
        printf("all socket failed\n");
        exit(1);
    }
    freeaddrinfo(results);
    if (listen(sockfd,LISTENQ) < 0) {
        printf("listen error: %s\n",strerror(errno));
        exit(1);
    }
    if (signal(SIGCHLD,sig_child) == SIG_ERR) {
        printf("signal error: %s\n",strerror(errno));
        exit(1);
    }


    int connfd;
    int pid;
    for (; ;) {
        if ((connfd = accept(sockfd,NULL,NULL)) < 0) {
            if (errno == EINTR) {
                continue;
            }
            printf("accept error: %s\n",strerror(errno));
            exit(1);
        }
        if ((pid = fork()) < 0) {
            printf("fork error: %s\n",strerror(errno));
            exit(1);
        }else if (pid == 0) {
            close(sockfd);
            server_echo(connfd);
            close(connfd);
        }
        close(connfd);
    } 
    return 0;
}

客户端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define BUFSIZE 4096

static void sig_alarm(int signo) {
    return;
}

int tcp_connect(int sockfd,struct sockaddr* peer,socklen_t len,int second) {
    //成功返回 1 失败返回-1 超时返回0
    int ret = 1;
    struct sigaction oact,act;
    act.sa_handler = sig_alarm;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGALRM,&act,&oact) < 0) {
        printf("tcp_connect: sigaction error: %s\n",strerror(errno));
        exit(1);
    }

    if (alarm(second) != 0) {
        printf("tcp_connect: alarm error: alarm already set\n");
        exit(1);
    }

    if (connect(sockfd,peer,len) < 0) {
        if (errno == EINTR) {
            errno = ETIMEDOUT;
            ret = 0;
            printf("tcp_connect: one connect timeout\n");
        }else {
            ret = -1;
        }
    }

    alarm(0);
    if (sigaction(SIGALRM,&oact,NULL) < 0) {
        printf("tcp_connect: sigaction error: %s\n",strerror(errno));
        exit(1);
    }
    return ret;
}

int tcp_recv(int sockfd,char* buf,int second) {
    struct sigaction oact,act;
    act.sa_handler = sig_alarm;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGALRM,&act,&oact) < 0) {
        printf("tcp_recv: sigaction error: %s\n",strerror(errno));
        exit(1);
    }
    if (alarm(second) != 0) {
        printf("tcp_recv: alarm error: alarm already set\n");
        exit(1);
    }
    int cread = -1;
    if ((cread = read(sockfd,buf,BUFSIZE)) < 0) {
        if (errno == EINTR) {
            errno = ETIMEDOUT;
            printf("tcp_recv: read error: recvive from server timeout\n");
        }else {
            printf("tcp_recv: read error: %s\n",strerror(errno));
            exit(1);
        }
    }

    alarm(0);
    if (sigaction(SIGALRM,&oact,NULL) < 0) {
        printf("tcp_recv: sigaction error: %s\n",strerror(errno));
        exit(1);
    }
    return cread;
}
int main(int argc,char** argv) {
    if (argc != 3) {
        printf("please add  \n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_ALL;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(argv[1],argv[2],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }

    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;       
        }
        if (tcp_connect(sockfd,dummy->ai_addr,dummy->ai_addrlen,5) > 0) {
            break;
        }
        close(sockfd); 
    }
    if (dummy == NULL) {
        freeaddrinfo(results);
        printf("all connect failed\n");
        exit(1);
    }
    freeaddrinfo(results);
    char send[BUFSIZE];
    int n;
    while ((n = read(STDIN_FILENO,send,BUFSIZE)) > 0) {
        if (write(sockfd,send,n) != n) {
            printf("write error: %s\n",strerror(errno));
            exit(1);
        }
        if ((n = tcp_recv(sockfd,send,20)) == 0) {
            printf("server is restart or closed\n");
            exit(1);
        }
        if (write(STDIN_FILENO,send,n) != n) {
            printf("write error: %s\n",strerror(errno));
            exit(1);
        }   
    }
    return 0;
}

第二种:采用轮询技术:
select + udp echo c/s:
服务端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFSIZE 4096

int main(int argc,char** argv) {
    if (argc != 2) {
        printf("please check \n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(errno));
        exit(1);
    }

    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }
        if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
            break;
        }
        close(sockfd);      
    }

    if (dummy == NULL) {
        freeaddrinfo(results);
        printf("all failed\n");
        exit(1);
    }

    freeaddrinfo(results);
    struct sockaddr_storage peer;
    char recv[BUFSIZE];
    socklen_t len = sizeof(struct sockaddr_storage);
    int n;
    for (; ;) {
        if ((n = recvfrom(sockfd,recv,BUFSIZE,0,(struct sockaddr*)&peer,&len)) < 0) {
            printf("recvfrom error: %s\n",strerror(errno));
            exit(1);
        }
        sleep(9);   
        if (sendto(sockfd,recv,n,0,(struct sockaddr*)&peer,len) != n) {
            printf("sendto error: %s\n",strerror(errno));
            exit(1);
        }
    }   
    return 0;
}

客户端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define BUFSIZE 4096

void sig_alarm(int signo) {
    return;
}

int udp_readable(int fd,int second,int usec) {
    fd_set fs;
    FD_ZERO(&fs);
    FD_SET(fd,&fs);
    struct timeval tv;
    tv.tv_sec = second;
    tv.tv_usec = usec;
    return select(fd + 1,&fs,NULL,NULL,&tv);
}

void client(int fd) {
    char send[BUFSIZE];
    int n;
    int err;
    while ((n = read(STDIN_FILENO,send,BUFSIZE)) > 0) {
        if (write(fd,send,n) != n) {
            printf("write error: %s\n",strerror(errno));
            exit(1);
        }
        if ((err = udp_readable(fd,5,20)) == 0) {
            printf("udp_read: timeout\n");
            exit(1);
        }else if (err < 0) {
            printf("udp_read: select error\n");
            exit(1);
        }else if (err > 0) {
            if ((n = read(fd,send,BUFSIZE)) < 0) {
                printf("read error: %s\n",strerror(errno));
                exit(1);
            }
        }
        if (write(STDOUT_FILENO,send,n) != n) {
            printf("write error: %s\n",strerror(errno));
            exit(1);
        }
    }
}
int udp_connect(int sockfd,struct addrinfo* to,int second) {
    struct sigaction oact,act;
    act.sa_handler = sig_alarm;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGALRM,&act,&oact) < 0) {
        printf("udp_connect: sigaction error: %s\n",strerror(errno));
        exit(1);
    }
    if (alarm(second) != 0) {
        printf("udp_connect: alarm alreay set\n");
        exit(1);
    }
    int ret = 0;
    if (connect(sockfd,to->ai_addr,to->ai_addrlen) < 0) {
        if (errno == EINTR) {
            errno = ETIMEDOUT;
            printf("udp_connect: connect timeout\n");
            ret = 1;
        }else {
            printf("udp_connect: connect error: %s\n",strerror(errno));
            ret = -1;
        }
    }

    alarm(0);
    if (sigaction(SIGALRM,&oact,NULL) < 0) {
        printf("udp_connect:sigaction error: %s\n",strerror(errno));
        exit(1);
    }
    return ret;
}

int main(int argc,char** argv) {
    if (argc != 3) {
        printf("please check  \n");
        exit(1); 
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_ALL;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(argv[1],argv[2],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }
    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }
        if (udp_connect(sockfd,dummy,5) == 0) {
            break;
        }
        close(sockfd);
    }
    if (dummy == NULL) {
        printf("all socket failed\n");
        freeaddrinfo(results);
        exit(1);
    }
    client(sockfd);
    close(sockfd);
    return 0;
}

第三种:为套接字设置SO_RCVTIMEO或SO_SNDTIMEO选项
服务端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SCTP_MAX_STREAM 20
#define LISTENQ 100
#define BUFSIZE 4096

int main(int argc,char** argv) {
    if (argc != 2) {
        printf("please add \n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_SEQPACKET;
    hints.ai_protocol = IPPROTO_SCTP;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }
    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }
        if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
            break;
        }
        close(sockfd);
    }
    if (listen(sockfd,LISTENQ) < 0) {
        printf("listen error: %s\n",strerror(errno));
        exit(1);
    }
    struct sctp_initmsg sim;
    bzero(&sim,sizeof(struct sctp_initmsg));
    sim.sinit_num_ostreams = SCTP_MAX_STREAM;
    if (setsockopt(sockfd,IPPROTO_SCTP,SCTP_INITMSG,&sim,sizeof(struct sctp_initmsg)) < 0) {
        printf("setsockopt error: %s\n",strerror(errno));
        exit(1);
    }
    struct sctp_event_subscribe events;
    bzero(&events,sizeof(struct sctp_event_subscribe));
    events.sctp_data_io_event = 1;
    if (setsockopt(sockfd,IPPROTO_SCTP,SCTP_EVENTS,&events,sizeof(struct sctp_event_subscribe)) < 0) {
        printf("setsockopt error: %s\n",strerror(errno));
        exit(1);
    }
    char recv[BUFSIZE];
    int n;
    struct sctp_sndrcvinfo sri;
    struct sockaddr_storage peer;
    socklen_t len = sizeof(struct sockaddr_storage);
    int msg_flags;
    int stream_no = 0;
    for (; ;) {
        if ((n = sctp_recvmsg(sockfd,recv,BUFSIZE,(struct sockaddr*)&peer,&len,&sri,&msg_flags)) < 0) {
            printf("sctp_recvmsg error: %s\n",strerror(errno));
            exit(1);
        }

        if (stream_no >= SCTP_MAX_STREAM) {
            stream_no = 0;
        }
        sleep(7);
        if (sctp_sendmsg(sockfd,recv,n,(struct sockaddr*)&peer,len,0,0,stream_no++,0,0) != n) {
            printf("sctp_sendmsg error: %s\n",strerror(errno));
            exit(1);
        }
    }
    return 0;
}

客户端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFSIZE 4096
#define SCTP_MAX_STREAM 15

void client(int fd,struct sockaddr* to,socklen_t tolen) {
    char send[BUFSIZE];
    int n;
    struct sctp_sndrcvinfo sri;
    int stream_num = 0;
    int msg_flags;
    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 20;
    if (setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(struct timeval)) < 0) {
        printf("setsockopt error: %s\n",strerror(errno));
        exit(1);
    }   
    while ((n = read(STDIN_FILENO,send,BUFSIZE)) > 0) {
        if (stream_num >= SCTP_MAX_STREAM) {
            stream_num = 0;
        }
        if (sctp_sendmsg(fd,send,n,to,tolen,0,0,stream_num++,0,0) != n) {
            printf("sctp_sendmsg error: %s\n",strerror(errno));
            exit(1);
        }
        if ((n = sctp_recvmsg(fd,send,BUFSIZE,NULL,NULL,&sri,&msg_flags)) < 0) {
            if (errno == EWOULDBLOCK) {
                printf("sctp_recvmsg: timeout\n");
                exit(1);
            }else {
                printf("sctp_recvmsg error: %s\n",strerror(errno));
                exit(1);
            }
        }
        printf("server peer stream_num: %d sctp_assoc_id %d\n",sri.sinfo_stream,sri.sinfo_assoc_id);
        if (write(STDOUT_FILENO,send,n) != n) {
            printf("write error: %s\n",strerror(errno));
            exit(1);
        }
    }
}
int main(int argc,char** argv) {
    if (argc != 3) {
        printf("please check \n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_ALL;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_SEQPACKET;
    hints.ai_protocol = IPPROTO_SCTP;

    struct addrinfo* results;
    int err;
    if (getaddrinfo(argv[1],argv[2],&hints,&results) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }
    int sockfd;
    struct addrinfo* dummy = results;
    sctp_assoc_t id;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }
        struct sctp_initmsg sim;
        bzero(&sim,sizeof(struct sctp_initmsg));
        sim.sinit_num_ostreams = SCTP_MAX_STREAM;
        if (setsockopt(sockfd,IPPROTO_SCTP,SCTP_INITMSG,&sim,sizeof(struct sctp_initmsg)) < 0) {
            close(sockfd);
            continue;
        }
        struct sctp_event_subscribe events;
        bzero(&events,sizeof(struct sctp_event_subscribe));
        events.sctp_data_io_event = 1;
        if (setsockopt(sockfd,IPPROTO_SCTP,SCTP_EVENTS,&events,sizeof(struct sctp_event_subscribe)) < 0) {
            close(sockfd);
            continue;
        }
        if (sctp_connectx(sockfd,dummy->ai_addr,1,&id) == 0) {
            break;
        }
        close(sockfd);
    }
    if (dummy == NULL) {
        freeaddrinfo(results);
        printf("all failed\n");
        exit(1);
    }
    client(sockfd,dummy->ai_addr,dummy->ai_addrlen);
    freeaddrinfo(results);
    close(sockfd);
    return 0;
}

你可能感兴趣的:(网络编程)