接続タイムアウト設定
4703 ワード
socketプログラミングでは、クライアントがconnect()でサービス側に接続している場合、サーバやネットワークが忙しい場合、または電源が入っていないホストに接続している場合、クライアントが送信したSYNパケットが応答しない場合、connect関数は長い間返されません(ブロックモード).具体的なタイムアウト時間はシステムに関連し、75秒、120秒などに設定される場合があります.あるアプリケーションでは、ブロックが長すぎるのはプログラムが望んでいる行為ではありませんか?
接続タイムアウト時間と/proc/sys/net/ipv 4/tcp_syn_retriesの値に関係し,この値はSYNパケットの再送回数を決定する一方,SYNパケットの再送間隔時間系列は,1,2,4,8,16,32,64,120,120…この時間系列は固定されているのでtcp_syn_retriesの値が大きいほどconnectタイムアウト時間が長くなります.これはシステム設定であり、この値を変更することでconnectのタイムアウト時間を短縮するのは明らかに合理的ではありません.(sysctlを検索して変更方法を表示できます)
次にconnectタイムアウト時間を他の方法で変更する方法を見てみましょう
方法1:
ステップ socket を確立する socketを非ブロックモード に設定 connect を呼び出す selectを使用して、socket記述子が書き込み可能かどうかを確認する . select戻り結果からconnect結果 を判断する socketをブロックモード に復元する
コードの例
方法2(回転):
linuxカーネルではconnectの実装に使用されるタイムアウトパラメータはsndtimeoである
net/ipv4/af_inet.c
つまりLinuxプラットフォームではconnectの前にSO_を設定できるSNDTIMOは、接続のタイムアウトを制御する目的を達成します.
サンプルコードは次のとおりです.
接続タイムアウト時間と/proc/sys/net/ipv 4/tcp_syn_retriesの値に関係し,この値はSYNパケットの再送回数を決定する一方,SYNパケットの再送間隔時間系列は,1,2,4,8,16,32,64,120,120…この時間系列は固定されているのでtcp_syn_retriesの値が大きいほどconnectタイムアウト時間が長くなります.これはシステム設定であり、この値を変更することでconnectのタイムアウト時間を短縮するのは明らかに合理的ではありません.(sysctlを検索して変更方法を表示できます)
次にconnectタイムアウト時間を他の方法で変更する方法を見てみましょう
方法1:
ステップ
コードの例
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int connect_timeout(int sockfd, struct sockaddr *serv_addr, int addrlen, int timeout);
int open_clientfd(const char *server, int port)
{
struct sockaddr_in serverAddr;
struct hostent *hostp;
int clientfd = socket(AF_INET, SOCK_STREAM, 0);
if (clientfd < 0)
{
perror("socket: ");
return -1;
}
if ( (hostp = gethostbyname(server)) == NULL)
{
perror("gethostbyname: ");
close(clientfd);
return -1;
}
bzero((char *)&serverAddr, sizeof(serverAddr));
memcpy((void *)&(serverAddr.sin_addr), (void *)hostp->h_addr_list[0],hostp->h_length);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = ntohs(port);
int n = connect_timeout(clientfd, (sockaddr *)&serverAddr, sizeof(serverAddr), 10000);
if (n<0)
{
printf("error
");
close(clientfd);
return -1;
}
else if (n==0)
{
printf("time out
");
close(clientfd);
return -1;
}
else
printf("connect sucessed
");
return clientfd;
}
/*
connect,timeout
*/
int connect_timeout(int sockfd, struct sockaddr *serv_addr, int addrlen, int timeout)
{
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
int n = connect(sockfd, serv_addr, sizeof(*serv_addr));
if(n < 0)
{
/*
EINPROGRESS connect
#define EWOULDBLOCK EAGAIN Operation would block
*/
if(errno != EINPROGRESS && errno != EWOULDBLOCK)
return -1;
struct timeval tv;
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout - tv.tv_sec*1000)*1000;
fd_set wset;
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
n = select(sockfd+1, NULL, &wset, NULL, &tv);
if(n < 0)
{
// select
return -1;
}
else if (0 == n)
{
//
return 0;
}
}
fcntl(fd,F_SETFL,flags & ~O_NONBLOCK); //
return 1;
}
int main(int argc, char **argv)
{
int sk = open_clientfd(argv[1], atoi(argv[2]));
printf("socket: %d
", sk);
if (sk>0)
close(sk);
return 0;
}
方法2(回転):
linuxカーネルではconnectの実装に使用されるタイムアウトパラメータはsndtimeoである
net/ipv4/af_inet.c
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
int writebias = (sk->sk_protocol == IPPROTO_TCP) &&
tcp_sk(sk)->fastopen_req &&
tcp_sk(sk)->fastopen_req->data ? 1 : 0;
/* Error code is set above */
if (!timeo || !inet_wait_for_connect(sk, timeo, writebias))
goto out;
err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out;
}
つまりLinuxプラットフォームではconnectの前にSO_を設定できるSNDTIMOは、接続のタイムアウトを制御する目的を達成します.
サンプルコードは次のとおりです.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.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
");
return -1;
}
perror("connect");
return 0;
}
printf("connected
");
return 0;
}