Linux下connect超時(shí)處理(總結(jié))
1、前言
最近在寫一個(gè)測(cè)試工具,要求快速的高效率的掃描出各個(gè)服務(wù)器開放了哪些端口。當(dāng)時(shí)想了一下,ping只能檢測(cè)ip,判斷服務(wù)器的網(wǎng)絡(luò)是連通的,而不能判斷是否開放了端口。我們知道端口屬于網(wǎng)絡(luò)的傳輸層,因此需要用ip和端口來探測(cè),這個(gè)時(shí)候就可以用connect來探測(cè)一下,針對(duì)TCP協(xié)議,connect函數(shù)要進(jìn)行TCP三次握手,如果connect成功,則說明服務(wù)器開放了某個(gè)端口,如果connect失敗,則說明服務(wù)器沒有開放某個(gè)端口。而connect失敗是通過超時(shí)來控制的,在規(guī)定的時(shí)間內(nèi),connect會(huì)發(fā)起多次連接,一直執(zhí)行到超時(shí),才返回錯(cuò)誤。默認(rèn)情況下,connect是阻塞的,而且默認(rèn)的超時(shí)時(shí)間為75s,正常情況下,檢測(cè)網(wǎng)絡(luò)的連通性都是毫秒級(jí),如果要判斷10萬臺(tái)服務(wù)器的,用阻塞的默認(rèn)的connect去做,效率非常低下。因此采用非阻塞的connect,而且需要自定義超時(shí)間(我自定義超時(shí)時(shí)間為5s)。
2、非阻塞connect
對(duì)于阻塞式套接字,調(diào)用connect函數(shù)將激發(fā)TCP的三次握手過程,而且僅在連接建立成功或者出錯(cuò)時(shí)才返回;對(duì)于非阻塞式套接字,如果調(diào)用connect函數(shù)會(huì)之間返回-1(表示出錯(cuò)),且錯(cuò)誤為EINPROGRESS,表示連接建立,建立啟動(dòng)但是尚未完成;如果返回0,則表示連接已經(jīng)建立,這通常是在服務(wù)器和客戶在同一臺(tái)主機(jī)上時(shí)發(fā)生。
select是一種IO多路復(fù)用機(jī)制,它允許進(jìn)程指示內(nèi)核等待多個(gè)事件的任何一個(gè)發(fā)生,并且在有一個(gè)或者多個(gè)事件發(fā)生或者經(jīng)歷一段指定的時(shí)間后才喚醒它。connect本身并不具有設(shè)置超時(shí)功能,如果想對(duì)套接字的IO操作設(shè)置超時(shí),可使用select函數(shù)。
對(duì)于select和非阻塞connect,注意兩點(diǎn):[1] 當(dāng)連接成功建立時(shí),描述符變成可寫; [2] 當(dāng)連接建立遇到錯(cuò)誤時(shí),描述符變?yōu)榧纯勺x,也可寫,遇到這種情況,可調(diào)用getsockopt函數(shù)。
3、實(shí)現(xiàn)步驟
(1) 創(chuàng)建socket,并利用fcntl將其設(shè)置為非阻塞
(2) 調(diào)用connect函數(shù),如果返回0,則連接建立;如果返回-1,檢查errno ,如果值為 EINPROGRESS,則連接正在建立。
(3) 為了控制連接建立時(shí)間,將該socket描述符加入到select的可讀可寫集合中,采用select函數(shù)設(shè)定超時(shí)。
(4) 如果規(guī)定時(shí)間內(nèi)成功建立,則描述符變?yōu)榭蓪?;否則,采用getsockopt函數(shù)捕獲錯(cuò)誤信息
(5) 恢復(fù)套接字的文件狀態(tài)并返回。
測(cè)試代碼如下所示:
#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 < 3) { printf("please input ip and port, for example ./main 120.12.34.56 80.\n"); return -1; } char *ipaddr = argv[1]; unsigned int port = atoi(argv[2]); int fd = 0; struct sockaddr_in addr; fd_set fdr, fdw; struct timeval timeout; int err = 0; int errlen = sizeof(err); fd = socket(AF_INET,SOCK_STREAM,0); if (fd < 0) { fprintf(stderr, "create socket failed,error:%s.\n", strerror(errno)); return -1; } bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); inet_pton(AF_INET, ipaddr, &addr.sin_addr); /*設(shè)置套接字為非阻塞*/ int flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { fprintf(stderr, "Get flags error:%s\n", strerror(errno)); close(fd); return -1; } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) { fprintf(stderr, "Set flags error:%s\n", strerror(errno)); close(fd); return -1; } /*阻塞情況下linux系統(tǒng)默認(rèn)超時(shí)時(shí)間為75s*/ int rc = connect(fd, (struct sockaddr*)&addr, sizeof(addr)); if (rc != 0) { if (errno == EINPROGRESS) { printf("Doing connection.\n"); /*正在處理連接*/ FD_ZERO(&fdr); FD_ZERO(&fdw); FD_SET(fd, &fdr); FD_SET(fd, &fdw); timeout.tv_sec = 10; timeout.tv_usec = 0; rc = select(fd + 1, &fdr, &fdw, NULL, &timeout); printf("rc is: %d\n", rc); /*select調(diào)用失敗*/ if (rc < 0) { fprintf(stderr, "connect error:%s\n", strerror(errno)); close(fd); return -1; } /*連接超時(shí)*/ if (rc == 0) { fprintf(stderr, "Connect timeout.\n"); close(fd); return -1; } /*[1] 當(dāng)連接成功建立時(shí),描述符變成可寫,rc=1*/ if (rc == 1 && FD_ISSET(fd, &fdw)) { printf("Connect success\n"); close(fd); return 0; } /*[2] 當(dāng)連接建立遇到錯(cuò)誤時(shí),描述符變?yōu)榧纯勺x,也可寫,rc=2 遇到這種情況,可調(diào)用getsockopt函數(shù)*/ if (rc == 2) { if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { fprintf(stderr, "getsockopt(SO_ERROR): %s", strerror(errno)); close(fd); return -1; } if (err) { errno = err; fprintf(stderr, "connect error:%s\n", strerror(errno)); close(fd); return -1; } } } fprintf(stderr, "connect failed, error:%s.\n", strerror(errno)); return -1; } return 0; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Apache 二級(jí)域名實(shí)現(xiàn)方法介紹
首先,你的擁有一個(gè)有泛域名解析的頂級(jí)域名,例如:domain.com2009-05-05Linux程序運(yùn)行找不到動(dòng)態(tài)庫.so文件的解決
這篇文章主要介紹了Linux程序運(yùn)行找不到動(dòng)態(tài)庫.so文件的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Linux內(nèi)核設(shè)備驅(qū)動(dòng)之內(nèi)核的調(diào)試技術(shù)筆記整理
今天小編就為大家分享一篇關(guān)于Linux內(nèi)核設(shè)備驅(qū)動(dòng)之內(nèi)核的調(diào)試技術(shù)筆記整理,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12Centos 7.4中的遠(yuǎn)程訪問控制的實(shí)現(xiàn)方法
這篇文章主要介紹了Centos 7.4中的遠(yuǎn)程訪問控制的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11Linux下模擬http的get/post請(qǐng)求(curl or wget)詳解
這篇文章主要介紹了Linux下模擬http的get/post請(qǐng)求(curl or wget)詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05Wampserver2.5配置虛擬主機(jī)出現(xiàn)403 Forbidden的處理方案
WampServer是一款由法國(guó)人開發(fā)的Apache Web服務(wù)器、PHP解釋器以 及MySQL數(shù)據(jù)庫的整合軟件包。免去了開發(fā)人員將時(shí)間花費(fèi)在繁瑣的配置環(huán)境過程,從而騰出更多精力去做開發(fā)。在windows下將Apache+PHP+Mysql 集成環(huán)境,擁有簡(jiǎn)單的圖形和菜單安裝和配置環(huán)境。2014-09-09Linux之使用split將一個(gè)大文件拆分成多個(gè)小文件
這篇文章主要介紹了Linux之使用split將一個(gè)大文件拆分成多個(gè)小文件實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04