linux socket 設定連線超時的方法
阿新 • • 發佈:2019-01-04
1.alarm方法
http://hi.baidu.com/ppln/blog/item/0523d3090731689d0a7b8200.html
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<errno.h>
#include<signal.h>
#define PORT 1234
#define MAXDATA 100
#define CONNECT_TIMEOUT 6
static int sTimeout=0;
static void alarmhandler(int sig);
void process(FILE *fp,int sockfd);
char *getmessage(char * sendline,int len,FILE *fp);
int main(int argc,char *argv[])
{
int fd;
struct hostent *he;
struct sockaddr_in server;
if(argc!=2){
printf("usage:%s <ip address>/n",argv[0]);
exit(1);
}
if((he=gethostbyname(argv[1]))==NULL){
printf("gethostbyname() error/n");
exit(1);
}
if((fd=socket(AF_INET,SOCK_STREAM,0))==-1){
printf("socket() error/n");
exit(1);
}
signal(SIGALRM,alarmhandler);
sTimeout=0;
alarm(CONNECT_TIMEOUT);
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr=*((struct in_addr *)he->h_addr);
if(connect(fd,(struct sockaddr *)&server,sizeof(struct sockaddr))==-1){
if(sTimeout)
perror("timeout connecting stream socket");
else
perror("connecting failed");
close(fd);
//printf("connect() error/n");
exit(1);
}
sTimeout=0;
alarm(0);
process(stdin,fd);
close(fd);
}
該方法在多執行緒下有問題。
2.select方法
http://www.ycgczj.com.cn/34733.html
原理上是這樣的:
1.建立socket
2.將該socket設定為非阻塞模式
3.呼叫connect()
4.使用select()檢查該socket描述符是否可寫(注意,是可寫)
5.根據select()返回的結果判斷connect()結果
6.將socket設定為阻塞模式(如果你的程式不需要用阻塞模式的,這步就省了,不過一般情況下都是用阻塞模式的,這樣也容易管理)
如果你對網路程式設計很熟悉的話,其實我一說出這個過程你就知道怎麼寫你的程式了,下面給出我寫的一段程式,僅供參考。
/******************************
* Time out for connect()
* Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect超時時間20秒
int main(int argc , char **argv)
{
………………
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr;
………//以伺服器地址填充結構serv_addr
int error=-1, len;
len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
ioctl(sockfd, FIONBIO, &ul); //設定為非阻塞模式
bool ret = false;
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
tm.tv_set = TIME_OUT_TIME;
tm.tv_uset = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
{
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
if(error == 0) ret = true;
else ret = false;
} else ret = false;
}
else ret = true;
ul = 0;
ioctl(sockfd, FIONBIO, &ul); //設定為阻塞模式
if(!ret)
{
close( sockfd );
fprintf(stderr , "Cannot Connect the server!n");
return;
}
fprintf( stderr , "Connected!n");
//下面還可以進行發包收包操作
……………
}
3.SO_SNDTIMEO方法
http://tech.ddvip.com/2009-09/1252571498131945.html
http://hi.baidu.com/ganss/blog/item/1c69d3139036a8836538dba0.html
SO_RCVTIMEO和SO_SNDTIMEO套介面選項可以給套介面的讀和寫,來設定超時時間,在unix網路程式設計中,說是他們只能用於讀和寫,而像 accept和connect都不能用他們來設定.可是我在閱讀核心原始碼的過程中看到,在linux中,accept和connect可以分別用 SO_RCVTIMEO和SO_SNDTIMEO套介面來設定超時,這裡他們的超時時間也就是sock的sk_rcvtimeo和sk_sndtimeo 域.accept和connect的相關程式碼我前面都介紹過了,這裡再提一下.其中accept的相關部分在inet_csk_accept中,會呼叫 sock_rcvtimeo來取得超時時間(如果是非阻塞則忽略超時間).而connect的相關程式碼在inet_stream_connect中通過呼叫sock_sndtimeo來取得超時時間(如果非阻塞則忽略超時時間).
測試程式碼
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in addr;
struct timeval timeo = {3, 0};
socklen_t len = sizeof(timeo);
fd = socket(AF_INET, SOCK_STREAM, 0);
if (argc == 4)
timeo.tv_sec = atoi(argv[3]);
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
if (errno == EINPROGRESS) {
fprintf(stderr, "timeout/n");
return -1;
}
perror("connect");
return 0;
}
printf("connected/n");
return 0;
}
http://hi.baidu.com/ppln/blog/item/0523d3090731689d0a7b8200.html
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<errno.h>
#include<signal.h>
#define PORT 1234
#define MAXDATA 100
#define CONNECT_TIMEOUT 6
static int sTimeout=0;
static void alarmhandler(int sig);
void process(FILE *fp,int sockfd);
char *getmessage(char * sendline,int len,FILE *fp);
int main(int argc,char *argv[])
{
int fd;
struct hostent *he;
struct sockaddr_in server;
if(argc!=2){
printf("usage:%s <ip address>/n",argv[0]);
exit(1);
}
if((he=gethostbyname(argv[1]))==NULL){
printf("gethostbyname() error/n");
exit(1);
}
if((fd=socket(AF_INET,SOCK_STREAM,0))==-1){
printf("socket() error/n");
exit(1);
}
signal(SIGALRM,alarmhandler);
sTimeout=0;
alarm(CONNECT_TIMEOUT);
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr=*((struct in_addr *)he->h_addr);
if(connect(fd,(struct sockaddr *)&server,sizeof(struct sockaddr))==-1){
if(sTimeout)
perror("timeout connecting stream socket");
else
perror("connecting failed");
close(fd);
//printf("connect() error/n");
exit(1);
}
sTimeout=0;
alarm(0);
process(stdin,fd);
close(fd);
}
該方法在多執行緒下有問題。
2.select方法
http://www.ycgczj.com.cn/34733.html
原理上是這樣的:
1.建立socket
2.將該socket設定為非阻塞模式
3.呼叫connect()
4.使用select()檢查該socket描述符是否可寫(注意,是可寫)
5.根據select()返回的結果判斷connect()結果
6.將socket設定為阻塞模式(如果你的程式不需要用阻塞模式的,這步就省了,不過一般情況下都是用阻塞模式的,這樣也容易管理)
如果你對網路程式設計很熟悉的話,其實我一說出這個過程你就知道怎麼寫你的程式了,下面給出我寫的一段程式,僅供參考。
/******************************
* Time out for connect()
* Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect超時時間20秒
int main(int argc , char **argv)
{
………………
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr;
………//以伺服器地址填充結構serv_addr
int error=-1, len;
len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
ioctl(sockfd, FIONBIO, &ul); //設定為非阻塞模式
bool ret = false;
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
tm.tv_set = TIME_OUT_TIME;
tm.tv_uset = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
{
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
if(error == 0) ret = true;
else ret = false;
} else ret = false;
}
else ret = true;
ul = 0;
ioctl(sockfd, FIONBIO, &ul); //設定為阻塞模式
if(!ret)
{
close( sockfd );
fprintf(stderr , "Cannot Connect the server!n");
return;
}
fprintf( stderr , "Connected!n");
//下面還可以進行發包收包操作
……………
}
3.SO_SNDTIMEO方法
http://tech.ddvip.com/2009-09/1252571498131945.html
http://hi.baidu.com/ganss/blog/item/1c69d3139036a8836538dba0.html
SO_RCVTIMEO和SO_SNDTIMEO套介面選項可以給套介面的讀和寫,來設定超時時間,在unix網路程式設計中,說是他們只能用於讀和寫,而像 accept和connect都不能用他們來設定.可是我在閱讀核心原始碼的過程中看到,在linux中,accept和connect可以分別用 SO_RCVTIMEO和SO_SNDTIMEO套介面來設定超時,這裡他們的超時時間也就是sock的sk_rcvtimeo和sk_sndtimeo 域.accept和connect的相關程式碼我前面都介紹過了,這裡再提一下.其中accept的相關部分在inet_csk_accept中,會呼叫 sock_rcvtimeo來取得超時時間(如果是非阻塞則忽略超時間).而connect的相關程式碼在inet_stream_connect中通過呼叫sock_sndtimeo來取得超時時間(如果非阻塞則忽略超時時間).
測試程式碼
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in addr;
struct timeval timeo = {3, 0};
socklen_t len = sizeof(timeo);
fd = socket(AF_INET, SOCK_STREAM, 0);
if (argc == 4)
timeo.tv_sec = atoi(argv[3]);
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
if (errno == EINPROGRESS) {
fprintf(stderr, "timeout/n");
return -1;
}
perror("connect");
return 0;
}
printf("connected/n");
return 0;
}