套接字设置超时总结
来源:互联网 发布:js超链接跳转 编辑:程序博客网 时间:2024/05/17 02:44
涉及套接字I/O操作上设置超时的方法:
1.调用alarm
当一个低速调用阻塞期间捕捉到一个信号, 则该系统调用就被中断不再继续执行。 该系统调用返回出错,起errono设置为EINTR。 因为发生信号, 进程捕捉到它, 这将是一个很好的机会来唤醒阻塞的系统调用。但有一个问题就是如果该系统调为read(), 正在等待终端输入, 如果被信号中断的话, 难免会影响整个程序的正确性, 所以有些系统使这类系统调用自动重启动。就是一旦被某信号中断, 立即再启动。read是一个低速系统调用,按照posix.1标准,默认的方式是重启由信号中断的系统调用。
通过下面的实例测试发生信号时,read重启。接收到信号后,read被中断后重启,不会返回,所以"read return"不会被打印。
#include <stdio.h>#include <signal.h>#include <unistd.h>void handler(int s){printf("recieve signal\n");return;}main(){char buf[10];signal(SIGALRM, handler);alarm(5);read(0, buf, sizeof(buf));printf("read return\n");}
下面的实例通过设置SA_INTERRUPT可以让read被信号中断后不重启,立即返回。
接收到信号后,read返回,打印"read return"
#include <stdio.h>#include <signal.h>#include <unistd.h>#include <stdlib.h>void handler(int s){printf("recieve signal\n");return;}main(){int r;char buf[10];struct sigaction act; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_INTERRUPT; //由此中断的系统调用不会自动重启if(sigaction(SIGALRM, &act, NULL) == -1) {perror("");exit(-1);}alarm(5);read(0, buf, sizeof(buf));printf("read return\n");}
alarm在到达指定时间会产生SIGALRM信号,使I/O调用返回,但涉及信号处理,可能会干扰进程中现有的alarm调用。connect在内核中的超时时间通常位75s,下面的实例可以用于设置比75小的超时,如果大于75的超时,connect仍然是75s后超时。
void SIGALRM_handler(int s){return;}int Connect_timeout(int sockfd,const struct sockaddr *addr,socklen_t addrlen,unsigned int sec){int r;void (*func) (int);func = signal(SIGALRM, SIGALRM_handler);if (func == SIG_ERR)return -1;alarm(sec);if ((r = connect(sockfd, addr, addrlen)) == -1) {Close(sockfd);if (errno == EINTR)errno = ETIMEDOUT;}alarm(0);signal(SIGALRM, func);return r;}
2.使用select
select函数的最后一个参数是等待描述符可读写的超时时间,更多关于select的内容见http://blog.csdn.net/aspnet_lyc/article/details/24035071
下面是一个用select实现的到达超时时间返回的函数read_timeout
#include <sys/select.h>#include <stdlib.h>#include <stdio.h>char buf[1024];int read_timeout(int fd, unsigned int sec){fd_set rset;struct timeval tv;int r, i;FD_ZERO(&rset);FD_SET(fd, &rset);tv.tv_sec = sec;tv.tv_usec = 0;r = select(fd+1, &rset, NULL, NULL, &tv);//超时if (r == 0) return r;if (r == -1)exit(-1);//可读if (FD_ISSET(fd, &rset)) r = read(fd, buf, sizeof(buf)); return r;}main(){//read(0, buf, sizeof(buf));if (read_timeout(0, 10) <= 0)printf("read_timeout timeout return\n");elseprintf("%s", buf);}
3.设置SO_RCVTIMEO或SO_SNDTIMEO套接子选项
这两个套接子选项给套接子的接收和发送设置一个超时值,通过struct timeval结构来指定要设置的超时时间。
接收超时影响5个输入函数:read、readv、recv、recvfrom和recvmsg。
发送超时影响5个输出函数:write、writev、send、sendto和sendmsg。
这个方法的优点是一次性设置,而上面的两个方法设置超时需要每次在操作前做一些工作,例如使用信号中断要设置信号处理函数并调用alarm,使用select每次都要对select的参数进行初始化和设置。值得注意的是,这两个套接子选项不能为connect设置超时。
下面展示了一个简单的客户-服务器程序,客户连接到服务器后设置SO_RCVTIMEO套接字选项并调用read,而服务器accept到连接后不会向客户发送任何数据,这将导致客户程序在10s后read超时而返回。
net.h
#ifndef MY_NET_H #define MY_NET_H #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <arpa/inet.h> #include <unistd.h> #include <time.h> #include <string.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h>#include <signal.h> #define MAXLINE 4096 #define SA struct sockaddr #define LISTENEQ 10 /*err_quit*/ void err_quit(const char* err_string) { printf("%s\n", err_string); exit(-1); } /*err_sys*/ void err_sys(const char* err_string) { perror(err_string); exit(-1); } /*Socket*/ int Socket(int domain, int type, int protocol) { int sockfd = socket(domain, type, protocol); if (sockfd == -1) err_sys("socket error"); return sockfd; } /*Connect*/ int Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int r; if ((r = connect(sockfd, addr, addrlen)) == -1) err_sys("connect error"); return r; } /*Bind*/ int Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int r; r = bind(sockfd, addr, addrlen); if (r == -1) err_sys("bind error"); return r; } /*Listen*/ int Listen(int sockfd, int backlog) { int r; r = listen(sockfd, backlog); if (r == -1) err_sys("listen error"); return r; } /*Accept*/ int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int r; r = accept(sockfd, addr, addrlen); if (r == -1) err_sys("accept error"); return r; } /*Close*/ int Close(int fd) { int r = close(fd); if (r == -1) err_sys("close error"); return r; } /*Read*/ int Read(int fd, void *buf, size_t count) { int r; r = read(fd, buf, count); if (r == -1) err_sys("read error"); return r; } /*Write*/ int Write(int fd, const void *buf, size_t count) { int r; r = write(fd, buf, count); if (r == -1) err_sys("write error"); return r; } /*Writen *返回剩余的字符 */ int Writen(int fd, const void *buf, int len) { int r; const char* ptr; int nleft = len; if(len <= 0) return -1; ptr = (char*)buf; while (1) { r = Write(fd, ptr, len); nleft -= r; ptr += r; len -= r; if (nleft == 0) return 0; } } /*Readn*/ int Readn(int fd, void *buf, int len) { int r; char* ptr; ptr = (char*)buf; while (1) { r = Read(fd, ptr, len); if (r == 0) return 0; if (len-r > 0) { len -= r; ptr += r; } else if (len-r == 0) return len; } } /*getsockname*/int Getsockname(int sockfd, SA *addr, socklen_t* len){int r; r = getsockname(sockfd, addr, len); if (r == -1) err_sys("write error"); return r;} /*inet_pton*/int Inet_pton(int af, const char *src, void *dst){ int r; r = inet_pton(af, src, dst); if (r == -1) err_sys("inet_pton error"); if (r == 0)err_sys("inet_pton error:not contain a character string rep‐resenting a valid network address\n");return r;}/*Init_sockaddr*/int Init_sockaddr(struct sockaddr_in *stru, int protoc, char *addr, unsigned int port){if (stru == NULL || addr == NULL)return -1;if (inet_addr(addr) == -1)return -1;stru->sin_family = protoc;(stru->sin_addr).s_addr = inet_addr(addr);stru->sin_port = htons(port);return 0;}/*tcp_connect * 指定要连接到地址和端口 */int Tcp_connect(char *addr, unsigned int port){int sockfd;struct sockaddr_in saddr;int r;if (addr == NULL)return -1;sockfd = Socket(AF_INET, SOCK_STREAM, 0);Init_sockaddr(&saddr, AF_INET, addr, port);Connect(sockfd, (SA*)&saddr, sizeof(saddr));return sockfd;}/*tcp_listen * 指定要监听的地址和端口和监听排队数 */int Tcp_listen(char *addr, unsigned int port, int backlog){int sockfd;struct sockaddr_in saddr;int r;if (addr == NULL)return -1;sockfd = Socket(AF_INET, SOCK_STREAM, 0);Init_sockaddr(&saddr, AF_INET, addr, port);Bind(sockfd, (SA*)&saddr, sizeof(saddr));Listen(sockfd, backlog);return sockfd;}#endif
客户程序
#include "net.h"main(){int sockfd;int r;struct timeval tv;char buf[1024+1];sockfd = Tcp_connect("127.0.0.1", 9999);tv.tv_sec = 10;tv.tv_usec = 0;//设置SO_RCVTIMEO套接子选项r = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));if (r == -1)exit(-1);read(sockfd, buf, sizeof(buf));printf("read timeout\n");}
服务程序
#include "net.h"main(){int sockfd, clifd;sockfd = Tcp_listen("127.0.0.1", 9999, 10);clifd = Accept(sockfd, NULL, NULL);while(1);}
- 套接字设置超时总结
- 套接字超时设置
- 阻塞套接字巧妙设置连接超时
- 给套接字设置超时检测
- 给套接字设置超时检测
- 阻塞TCP套接字connect超时设置
- UNIX网络编程——设置套接字超时
- Unix网络编程 高级IO套接字设置超时
- Socket编程实践(9) --套接字IO超时设置方法
- python字节序转换+设置套接字超时时间
- Socket编程实践(8) --套接字IO超时设置方法
- Socket编程实践(9) --套接字IO超时设置方法
- linux网络编程之套接字:套接字I/O超时设置方法和用select实现超时
- 套接字IO超时设置方法和用select实现超时
- 套接字超时接接收
- linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时
- linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时
- linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时
- 庄伟雄:基于移动互联网的供应链管理
- UVALive 5907 —— Tichu(搜索)
- 弹出窗口,并适合屏幕大小
- 负载均衡的基本(常用)算法
- error: conflicting type qualifiers for 'xxxxx'
- 套接字设置超时总结
- Float类的floatToIntBit方法
- 深入理解 GCD(一)
- 4.使用Jackson将Json数据转换成实体数据
- assertion failed: Path for IClasspathEntry must be absolute解决方法
- win32 console application与win32 application的区别
- 科學計算 -- Octave 的函數列表
- 游戏开发工具
- 特殊字符编码