前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过select 和状态EINPROGRESS 实现socket 连接超时判断

通过select 和状态EINPROGRESS 实现socket 连接超时判断

作者头像
全栈程序员站长
发布2022-09-09 10:58:24
1.1K0
发布2022-09-09 10:58:24
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

调用connect连接一般的超时时间是75s, 但是在程序中我们一般不希望等这么长时间采取采取动作。 可以在调用connect之前设置套接字非阻塞,然后调用connect,此时connect会立刻返回, 如果连接成功则直接返回0(成功), 如果没有连接成功,也会立即返回并且会设置errno为EINPROCESS,这并不是一个致命错误,仅仅是告知你已经在连接了,你只要判断是它就继续执行后面的逻辑就行了,比如select.通过select设置超时来达到为connect设定超时的目的. 下面的代码显示这个过程。

bool timeout_connect(const string& _host, uint16_t _port, uint32t _timeout, int32_t& _sockfd)

{ #define CLOSE_SOCK_AND_RETURN_FALSE(so) close(so); return false;

if (_sockfd != -1) return true; int sockfd; struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(_port); serv_addr.sin_addr.s_addr = inet_addr(_host.c_str());

if ((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1) { return false; } // get origin sockfd flag int flags = fcntl(sockfd, F_GETFL); if (flags == -1) { CLOSE_SOCK_AND_RETURN_FALSE(sockfd); } // set sockfd to non-block mode int retcode = fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); if (retcode == -1) { CLOSE_SOCK_AND_RETURN_FALSE(sockfd); }

if (::connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {

if (errno == EINPROGRESS) // EINPROGRESS means connection is in progress, normally the socket connecting timeout is 75s. after the socket fd is ready to read.

// means the connecting of the socket fd is established.

{ int err; int len = sizeof(int); fd_set wds; struct timeval tm; tm.tv_sec = _timeout; tm.tv_usec = 0; FD_ZERO(&wds); FD_SET(sockfd, &wds); if (select(sockfd + 1, NULL, &wds, NULL, &tm) > 0) // “>0” means sockfd ready to read, “=0” means timeout cause retrun, “<0” means error. { retcode = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, (socklen_t *)&len); if (retcode == -1 || err != 0) { CLOSE_SOCK_AND_RETURN_FALSE(sockfd); } } else { CLOSE_SOCK_AND_RETURN_FALSE(sockfd); } } else { CLOSE_SOCK_AND_RETURN_FALSE(sockfd); } } retcode = fcntl(sockfd, F_SETFL, flags); // trun back the mode of sockfd to block. if (retcode == -1) { CLOSE_SOCK_AND_RETURN_FALSE(sockfd); }

struct timeval readtimeout; readtimeout.tv_sec = _timeout; readtimeout.tv_usec = 0; retcode = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (void *)&readtimeout, sizeof(readtimeout)); // set socket read timeout. if (retcode == -1) { CLOSE_SOCK_AND_RETURN_FALSE(sockfd); }

#undef CLOSE_SOCK_AND_RETURN_FALSE _sockfd = sockfd; return true;

}

通过SO_RCVTIMEO 设置连接超时

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来取得超时时间(如果非阻塞则忽略超时时间).

SO_RCVTIMEO和SO_SNDTIMEO ,它们分别用来设置socket接收数据超时时间和发送数据超时时间。 因此,这两个选项仅对与数据收发相关的系统调用有效,这些系统调用包括:send, sendmsg, recv, recvmsg, accept, connect 。 这两个选项设置后,若超时, 返回-1,并设置errno为EAGAIN或EWOULDBLOCK. 其中connect超时的话,也是返回-1, 但errno设置为EINPROGRESS

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <assert.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> //超时连接 int timeout_connect(const char *ip, int port, int time); int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, “Usage: %s ip port\n”, argv[0]); return 1; } const char *ip = argv[1]; int port = atoi(argv[2]); int sockfd = timeout_connect(ip, port, 10); if (sockfd < 0) return 1; return 0; } int timeout_connect(const char *ip, int port, int time) { int ret = 0; int error; struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; address.sin_port = htons(port); inet_pton(AF_INET, ip, &address.sin_addr); int sockfd = socket(PF_INET, SOCK_STREAM, 0); if (sockfd == -1) return -1; //超时时间 struct timeval timeout; timeout.tv_sec = time; timeout.tv_usec = 0; socklen_t len = sizeof(timeout); ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len); if (ret == -1) { error = errno; while ((close(sockfd) == -1) && (errno == EINTR)); errno = error; return -1; } ret = connect(sockfd, (struct sockaddr*)&address, sizeof(address)); if (ret == -1) { if (errno == EINPROGRESS) { printf(“connecting timeout\n”); return -1; } printf(“error occur when connecting to server\n”); return -1; } char buffer[1024]; memset(buffer, ‘\0’, 1024); ret = recv(sockfd, buffer, 1024, 0); printf(“recv %d bytes, buf: %s\n”, ret, buffer); return sockfd; }

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/160515.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档