Unix网络编程 高级IO套接字设置超时
2014-11-18 13:20
337 查看
我们知道,对于一个套接字的读写(read/write)操作默认是阻塞的,如果当前套接字还不可读/写,那么这个操作会一直阻塞下去,这样对于一个需要高性能的服务器来说,是不能接受的。所以,我们可以在进行读写操作的时候可以指定超时值,这样就读写操作就不至于一直阻塞下去。
在涉及套接字的I/O操作上设置超时的方法有三种:
1:调用alarm,它在指定的超时期满时产生SIGALRM信号。这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm调用。
2:在select中阻塞等待I/O(select有内置的时间限制),依次代替直接阻塞在read或write调用上。(linux2.6以后的内核也可以使用epoll的epoll_wait)。
3:使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。这个方法的问题在于并非所有的实现都支持这两个套接字选项。
上述这三个技术都适用于输入和输出操作(read、write,及其变体recv/send, readv/writev, recvfrom, sendto)。不过我们也期待可以用于connect的技术,因为TCP内置的connect超时相当长(典型值为75秒),而我们在写服务器程序的时候,也不会希望一个连接的建立需要花费这么长时间。select可用来在connect上设置超时的先决条件是相应的套接字是非阻塞的,而那两个套接字选项对connect并不适用;同时也应当指出,前两个技术适用于任何描述符,而第三个技术仅仅适用于套接字描述符。
>>>>使用select对connect设置超时:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>
#define PORT 9900
#define MAXDATASIZE 5000
int main(int argc, char **argv)
{
int sockfd, nbytes;
char buf[1024];
struct hostent *he;
struct sockaddr_in servaddr;
if(argc != 2) {
perror("Usage:client hostname\n");
return 0;
}
if((he = gethostbyname(argv[1])) == NULL) {
perror("gethostbyname");
return 0;
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("create socket error");
return 0;
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr = *((struct in_addr *)he->h_addr);
fcntl(sockfd, F_SETFL, O_NONBLOCK);
timeval timeout = {3, 0};
if(connect(sockfd, (SA*)&servaddr, sizeof(struct sockaddr)) == -1) {
if(errno != EINPROGRESS) {
close(sockfd);
perror("connect error");
return 0;
}
}
fd_set readSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_SET(sockfd, &writeSet);
int ret = select(sockfd+1, &readSet, &writeSet, NULL, &timeout);
printf("%d", ret);
}
>>>>使用SIGALRM为connect设置超时:
#include <unp.h>
static void connect_alarm(int);
int
connect_timeo(int sockfd, const SA* saptr, socklen_t salen, int nsec)
{
Sigfunc* sigfunc;
int n;
sigfunc = Signal(SIGALRM, connect_alarm);
if(alarm(nsec) != 0)
err_msg("connect_timeo: alarm was already set");
if((n = connect(sockfd, saptr, salen)) < 0) {
close(sockfd);
if(errno == EINTR)
errno = ETIMEOUT;
}
alarm(0); /* turn off the alarm */
Signal(SIGALRM, sigfunc); /* restore previous signal handler */
return (n);
}
static void
connect_alarm(int signo)
{
return ; /* just interrupt the connect() */
}
>>>>使用SIGALRM为recvfrom设置超时:
#include <unp.h>
static void sig_alarm(int);
void
dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE+1];
Signal(SIGALRM, sig_alarm);
while(Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
alarm(5); /* set TIMEOUT 5 seconds */
if((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
if(errno == EINTR)
fprintf(stderr, "socket timeout\n");
else
err_sys("recvfrome error");
}else {
alarm(0); /* if success then turn off the alarm */
recvline
= 0; /* null terminate */
Fputs(recvline, stdout);
}
}
}
static void
sig_alarm(int signo)
{
return ; /* just interrupt the recvfrom() */
}
>>>>使用select为recvfrom设置超时:
#include <unp.h>
int
readable_timeo(int fd, int sec)
{
fd_set rset;
struct timeval tv;
FD_ZERO(&rset); /* reset the file discriptor set */
FD_SET(fd, &rset);
tv.tv_sec = sec; /* set struct timeval */
tv.tv_usec = 0;
return (select(fd+1, &rset, NULL, NULL, &tv));
}
所以上面dig_cli函数结合readable_timeo函数设置超时的程序就可变为:(阻塞在select上)
#include <unp.h>
void
dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE+1];
while(Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
if(readable_timeo(sockfd, 5) == 0) {
fprintf(stderr, "socket timeout\n");
} else {
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline
= 0; /* null terminate */
Fputs(recvline, stdout);
}
}
}
>>>>使用SO_RCVTIMEO套接字选项为recvfrom设置超时:
#include <unp.h>
void
dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE+1];
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
Setsockopt(sockfd, SOL_SOCKET, &tv, sizeof(tv));
while(Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
if(n < 0) {
if(errno == EWOULDBLOCK) {
fprintf(stderr, "socket timeout\n");
continue;
}else
err_sys("recvfrom error");
}
recvline
= 0; /* null terminate */
Fputs(recvline, stdout);
}
}
在涉及套接字的I/O操作上设置超时的方法有三种:
1:调用alarm,它在指定的超时期满时产生SIGALRM信号。这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm调用。
2:在select中阻塞等待I/O(select有内置的时间限制),依次代替直接阻塞在read或write调用上。(linux2.6以后的内核也可以使用epoll的epoll_wait)。
3:使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。这个方法的问题在于并非所有的实现都支持这两个套接字选项。
上述这三个技术都适用于输入和输出操作(read、write,及其变体recv/send, readv/writev, recvfrom, sendto)。不过我们也期待可以用于connect的技术,因为TCP内置的connect超时相当长(典型值为75秒),而我们在写服务器程序的时候,也不会希望一个连接的建立需要花费这么长时间。select可用来在connect上设置超时的先决条件是相应的套接字是非阻塞的,而那两个套接字选项对connect并不适用;同时也应当指出,前两个技术适用于任何描述符,而第三个技术仅仅适用于套接字描述符。
>>>>使用select对connect设置超时:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>
#define PORT 9900
#define MAXDATASIZE 5000
int main(int argc, char **argv)
{
int sockfd, nbytes;
char buf[1024];
struct hostent *he;
struct sockaddr_in servaddr;
if(argc != 2) {
perror("Usage:client hostname\n");
return 0;
}
if((he = gethostbyname(argv[1])) == NULL) {
perror("gethostbyname");
return 0;
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("create socket error");
return 0;
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr = *((struct in_addr *)he->h_addr);
fcntl(sockfd, F_SETFL, O_NONBLOCK);
timeval timeout = {3, 0};
if(connect(sockfd, (SA*)&servaddr, sizeof(struct sockaddr)) == -1) {
if(errno != EINPROGRESS) {
close(sockfd);
perror("connect error");
return 0;
}
}
fd_set readSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_SET(sockfd, &writeSet);
int ret = select(sockfd+1, &readSet, &writeSet, NULL, &timeout);
printf("%d", ret);
}
>>>>使用SIGALRM为connect设置超时:
#include <unp.h>
static void connect_alarm(int);
int
connect_timeo(int sockfd, const SA* saptr, socklen_t salen, int nsec)
{
Sigfunc* sigfunc;
int n;
sigfunc = Signal(SIGALRM, connect_alarm);
if(alarm(nsec) != 0)
err_msg("connect_timeo: alarm was already set");
if((n = connect(sockfd, saptr, salen)) < 0) {
close(sockfd);
if(errno == EINTR)
errno = ETIMEOUT;
}
alarm(0); /* turn off the alarm */
Signal(SIGALRM, sigfunc); /* restore previous signal handler */
return (n);
}
static void
connect_alarm(int signo)
{
return ; /* just interrupt the connect() */
}
>>>>使用SIGALRM为recvfrom设置超时:
#include <unp.h>
static void sig_alarm(int);
void
dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE+1];
Signal(SIGALRM, sig_alarm);
while(Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
alarm(5); /* set TIMEOUT 5 seconds */
if((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
if(errno == EINTR)
fprintf(stderr, "socket timeout\n");
else
err_sys("recvfrome error");
}else {
alarm(0); /* if success then turn off the alarm */
recvline
= 0; /* null terminate */
Fputs(recvline, stdout);
}
}
}
static void
sig_alarm(int signo)
{
return ; /* just interrupt the recvfrom() */
}
>>>>使用select为recvfrom设置超时:
#include <unp.h>
int
readable_timeo(int fd, int sec)
{
fd_set rset;
struct timeval tv;
FD_ZERO(&rset); /* reset the file discriptor set */
FD_SET(fd, &rset);
tv.tv_sec = sec; /* set struct timeval */
tv.tv_usec = 0;
return (select(fd+1, &rset, NULL, NULL, &tv));
}
所以上面dig_cli函数结合readable_timeo函数设置超时的程序就可变为:(阻塞在select上)
#include <unp.h>
void
dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE+1];
while(Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
if(readable_timeo(sockfd, 5) == 0) {
fprintf(stderr, "socket timeout\n");
} else {
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline
= 0; /* null terminate */
Fputs(recvline, stdout);
}
}
}
>>>>使用SO_RCVTIMEO套接字选项为recvfrom设置超时:
#include <unp.h>
void
dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE+1];
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
Setsockopt(sockfd, SOL_SOCKET, &tv, sizeof(tv));
while(Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
if(n < 0) {
if(errno == EWOULDBLOCK) {
fprintf(stderr, "socket timeout\n");
continue;
}else
err_sys("recvfrom error");
}
recvline
= 0; /* null terminate */
Fputs(recvline, stdout);
}
}
相关文章推荐
- Socket编程实践(9) --套接字IO超时设置方法
- Socket编程实践(9) --套接字IO超时设置方法
- UNIX网络编程——套接字I/O操作上的超时设置
- 如何在套接字IO操作上设置超时机制
- Socket编程实践(9) --套接字IO超时设置方法
- Socket编程实践(9) --套接字IO超时设置方法
- UNIX网络编程——设置套接字超时
- UNIX网络编程——设置套接字超时
- 套接字IO超时设置方法和用select实现超时
- 套接字IO超时设置和使用select实现超时管理
- UNIX网络编程——设置套接字超时
- UNIX网络编程——使用select 实现套接字I/O超时
- linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时
- boost::asio::io_service 超时设置
- python字节序转换+设置套接字超时时间
- linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时
- 服务器编程入门(13) Linux套接字设置超时的三种方法
- UNIX环境高级编程笔记——高级IO,进程间通信,套接字,高级进程间通信
- Linux下的socket编程实践(五)设置套接字I/O超时的方案
- linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时