1、前言

  最近在写一个测试工具,要求快速的高效率的扫描出各个服务器开放了哪些端口。当时想了一下,ping只能检测ip,判断服务器的网络是连通的,而不能判断是否开放了端口。我们知道端口属于网络的传输层,因此需要用ip和端口来探测,这个时候就可以用connect来探测一下,针对TCP协议,connect函数要进行TCP三次握手,如果connect成功,则说明服务器开放了某个端口,如果connect失败,则说明服务器没有开放某个端口。而connect失败是通过超时来控制的,在规定的时间内,connect会发起多次连接,一直执行到超时,才返回错误。默认情况下,connect是阻塞的,而且默认的超时时间为75s,正常情况下,检测网络的连通性都是毫秒级,如果要判断10万台服务器的,用阻塞的默认的connect去做,效率非常低下。因此采用非阻塞的connect,而且需要自定义超时间(我自定义超时时间为5s)。

2、非阻塞connect

  对于阻塞式套接字,调用connect函数将激发TCP的三次握手过程,而且仅在连接建立成功或者出错时才返回;对于非阻塞式套接字,如果调用connect函数会之间返回-1(表示出错),且错误为EINPROGRESS,表示连接建立,建立启动但是尚未完成;如果返回0,则表示连接已经建立,这通常是在服务器和客户在同一台主机上时发生。

  select是一种IO多路复用机制,它允许进程指示内核等待多个事件的任何一个发生,并且在有一个或者多个事件发生或者经历一段指定的时间后才唤醒它。connect本身并不具有设置超时功能,如果想对套接字的IO操作设置超时,可使用select函数。

  对于select和非阻塞connect,注意两点:[1] 当连接成功建立时,描述符变成可写; [2] 当连接建立遇到错误时,描述符变为即可读,也可写,遇到这种情况,可调用getsockopt函数。

3、实现步骤

(1) 创建socket,并利用fcntl将其设置为非阻塞

(2) 调用connect函数,如果返回0,则连接建立;如果返回-1,检查errno ,如果值为 EINPROGRESS,则连接正在建立。

(3) 为了控制连接建立时间,将该socket描述符加入到select的可读可写集合中,采用select函数设定超时。

(4) 如果规定时间内成功建立,则描述符变为可写;否则,采用getsockopt函数捕获错误信息

(5) 恢复套接字的文件状态并返回。

测试代码如下所示:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h> int main(int argc, char **argv)
{
if (argc < ) {
printf("please input ip and port, for example ./main 120.12.34.56 80.\n");
return -;
} char *ipaddr = argv[];
unsigned int port = atoi(argv[]); int fd = ;
struct sockaddr_in addr;
fd_set fdr, fdw;
struct timeval timeout;
int err = ;
int errlen = sizeof(err); fd = socket(AF_INET,SOCK_STREAM,);
if (fd < ) {
fprintf(stderr, "create socket failed,error:%s.\n", strerror(errno));
return -;
} bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, ipaddr, &addr.sin_addr); /*设置套接字为非阻塞*/
int flags = fcntl(fd, F_GETFL, );
if (flags < ) {
fprintf(stderr, "Get flags error:%s\n", strerror(errno));
close(fd);
return -;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < ) {
fprintf(stderr, "Set flags error:%s\n", strerror(errno));
close(fd);
return -;
} /*阻塞情况下linux系统默认超时时间为75s*/
int rc = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
if (rc != ) {
if (errno == EINPROGRESS) {
printf("Doing connection.\n");
/*正在处理连接*/
FD_ZERO(&fdr);
FD_ZERO(&fdw);
FD_SET(fd, &fdr);
FD_SET(fd, &fdw);
timeout.tv_sec = ;
timeout.tv_usec = ;
rc = select(fd + , &fdr, &fdw, NULL, &timeout);
printf("rc is: %d\n", rc);
/*select调用失败*/
if (rc < ) {
fprintf(stderr, "connect error:%s\n", strerror(errno));
close(fd);
return -;
} /*连接超时*/
if (rc == ) {
fprintf(stderr, "Connect timeout.\n");
close(fd);
return -;
}
/*[1] 当连接成功建立时,描述符变成可写,rc=1*/
if (rc == && FD_ISSET(fd, &fdw)) {
printf("Connect success\n");
close(fd);
return ;
}
/*[2] 当连接建立遇到错误时,描述符变为即可读,也可写,rc=2 遇到这种情况,可调用getsockopt函数*/
if (rc == ) {
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -) {
fprintf(stderr, "getsockopt(SO_ERROR): %s", strerror(errno));
close(fd);
return -; } if (err) {
errno = err;
fprintf(stderr, "connect error:%s\n", strerror(errno));
close(fd);
return -; }
} }
fprintf(stderr, "connect failed, error:%s.\n", strerror(errno));
return -;
}
return ;
}

4、参考资料

http://dongxicheng.org/network/non-block-connect-implemention/

http://www.cnblogs.com/flyxiang2010/archive/2010/12/17/1909051.html

Linux下connect超时处理【总结】的更多相关文章

  1. Linux下connect超时处理

    1.前言 最近在写一个测试工具,要求快速的高效率的扫描出各个服务器开放了哪些端口.当时想了一下,ping只能检测ip,判断服务器的网络是连通的,而不能判断是否开放了端口.我们知道端口属于网络的传输层, ...

  2. linux下connect超时时间探究

    最近在linux做服务器开发的时候,发现了一个现象:服务器在启动的时候调用了 connect 函数,因为连接了一个不可用的端口,导致connect最后报出了 “Connection timed out ...

  3. 解决Linux下SSH超时自动断开

    title: 解决Linux下SSH超时自动断开 comments: false date: 2019-08-19 19:22:55 description: Linux 下 SSH 超时自动断开?? ...

  4. linux 设置connect 超时代码[select/epoll]

    转载请注明来源:https://www.cnblogs.com/hookjc/ linux下socket编程有常见的几个系统调用: 对于服务器来说, 有socket(), bind(),listen( ...

  5. linux 设置connect 超时

    转载请注明来源:https://www.cnblogs.com/hookjc/ 将一个socket 设置成阻塞模式和非阻塞模式,使用fcntl方法,即: 设置成非阻塞模式: 先用fcntl的F_GET ...

  6. [转] - linux下使用write\send发送数据报 EAGAIN : Resource temporarily unavailable 错

    linux下使用write\send发送数据报 EAGAIN : Resource temporarily unavailable 错 首先是我把套接字设置为异步的了,然后在使用write发送数据时采 ...

  7. Windows 和 Linux下使用socket下载网页页面内容(可设置接收/发送超时)的代码

    主要难点在于设置recv()与send()的超时时间,具体要注意的事项,请看代码注释部分,下面是代码: #include <stdio.h> #include <sys/types. ...

  8. linux下socket connect 阻塞方式 阻塞时间控制

    同事今天问我,如何在linux下的c代码里面控制connect的阻塞时间.应用的背景是:linux下的c程序有两个目标IP需要connect,如果用阻塞方式,当其中一个IP不能连接的情况下,程序将阻塞 ...

  9. linux下的tcp连接超时

    最近需要写一个linux下的通信程序, 通信模块用的是Qt的QTcpSocket. 最后程序需要增加一个断网检测, 在windows下调试没问题, 拔网线, 断网口都能马上检测到, 但到了部署到lin ...

随机推荐

  1. Unity3D ——强大的跨平台3D游戏开发工具(二)

    第二章 Unity3D的简单预览 每个Unity3D版本都会自带一个Demo源文件.在3.0的正式版中,自带的Demo就是网上展示的那款强大的射击游戏.在一般情况下,您只要第一次 打开Unity3D ...

  2. Java虚拟机——进度1

    Java 虚拟机       一.Java虚拟机的基本结构 ①类加载子系统:从文件系统或者网络中加载Class信息,存放在方法区中. ②方法区中存放放进来的Class信息,也包括一些运行时常量池信息包 ...

  3. DNS分析

    一.DNS服务器 区域传送: 区域(zone)和 域(domain) 区域:物理概念 域:逻辑概念 区域数据库文件: 资源记录:RR 有类型:A AAAA PTR SOA NS CNAME MX SO ...

  4. zepto callback

    // Zepto.js // (c) 2010-2013 Thomas Fuchs // Zepto.js may be freely distributed under the MIT licens ...

  5. HTML5学习笔记四:html5结构

    一.大纲:大纲即文档中各内容区块的结构编排 1. 显示编排内容区块:使用section等元素创建文档结构,每个内容区块使用标题(h1~h6,hgroup); 2. 隐式编排内容区块:根据页面所书写的各 ...

  6. Leetcode 175. Combine Two Tables

    Table: Person +-------------+---------+ | Column Name | Type | +-------------+---------+ | PersonId ...

  7. iOS 之 #import与#include的区别及@class

    #import 相比#include不会引起交叉编译. @class一般用于头文件中需要声明该类的变量时用到

  8. java 二维码

    在http://www.ostools.net/qr看到了一个生成二维码的工具,于是就产生了一个想法: 为什么自己不做一个二维码的生成和解析工具呢?花了一个多钟的时间,嘿嘿,就做出来啦... 先来看看 ...

  9. Java 读写Properties配置文件

    Java 读写Properties配置文件 JAVA操作properties文件 1.Properties类与Properties配置文件 Properties类继承自Hashtable类并且实现了M ...

  10. 【翻译】使用Visual Studio在Azure上部署Asp.Net Core Web应用

    配置运行环境 Install the latest Azure SDK for Visual Studio. The SDK installs Visual Studio if you don't a ...