1. 程式人生 > >如何解決Connect超時導致的阻塞問題

如何解決Connect超時導致的阻塞問題

        這幾天發現一個現象,客戶端正常連線伺服器connect顯然不會出現問題。

        在異常情況下,如果是伺服器出現異常,connect能夠立即返回失敗;但是當客戶端出現異常的情況下,分為兩種情況:

        一種是不插網線,客戶端沒有獲得ip地址,在這種情況下,connect也可以立即返回錯誤;

        二是但是當客戶端插上網線,但是連線網路失敗,也就是說能夠獲取到ip地址,但是和伺服器是ping不通的。這種情況下connect就可能會發生阻塞,因為按照《UNIX 網路程式設計》中講解,connect的在進行三次握手,如果失敗情況,需要等待75s的超市時間的。

        我們主要討論第二種情況如何解決,可以讓connect快速返回結果,不至於阻塞等待超長的時間。

        如下是我的程式碼

/******************************
* Time out for connect()
******************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>


#define TIME_OUT_TIME 20 //connect超時時間20秒

bool setBlockOpt(int m_fd,bool blocked)
{
			
#ifndef WIN32
	int  flags;
	flags = fcntl(m_fd, F_GETFL, 0);
	if(flags < 0)
	{
		return false;
	}
	if(blocked)
	{	
		printf("Set BLOCK !!!\n");
		flags &= ~O_NONBLOCK;
	}
	else
	{
		printf("Set NONBLOCK !!!\n");
		flags |= O_NONBLOCK;
	}
	if(fcntl(m_fd, F_SETFL, flags) < 0)
	{
		return false;
	}
	
#else
	u_long ulValue;
	if(blocked)
	{
		ulValue = 1;
	}
	else
	{
		ulValue = 0;
	}	
	int n = ioctlsocket(m_fd, FIONBIO, &ulValue);
	if (n != 0)
	{
		return false;
	}
#endif
	return true;
}

int connectWithTimeout(int m_fd,int timeout)
{
	int selectFlag = -1;
	int error=-1, len;
	len = sizeof(int);
	bool ret = false;
	int connectFlag = -1;
	
	const char* m_ip = "115.239.210.27";
	int m_port = 80;
	
	if("" == m_ip || 0 > m_port)
	{
		return -1;
	}
	
	if(m_fd < 0 && "" != m_ip && m_port >=0)
	{
		m_fd =  socket(AF_INET, SOCK_STREAM, 0);
		if(m_fd < 0)
		{
			return -1;
		}
	}
	
	if(m_fd < 0)
	{
		return -1;
	}

	struct sockaddr_in servAddr;
	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons((unsigned short)m_port);
	servAddr.sin_addr.s_addr = inet_addr(m_ip);

	setBlockOpt(m_fd,false);	//設定為非阻塞模式
	if( (connectFlag= connect(m_fd, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0))
	{
		if(errno != EINPROGRESS)
		{
			goto done;
		}
	}
	else
	{
		ret = true;
		goto done;
	}
	timeval tm;
	tm.tv_sec = timeout/1000;
	tm.tv_usec = timeout%1000;
	fd_set rest, west;
	FD_ZERO(&rest);
	FD_ZERO(&west);
	FD_SET(m_fd, &rest);
	FD_SET(m_fd, &west);

	if( (selectFlag = select(m_fd+1, &rest, &west, NULL, &tm)) > 0)
	{
		//如果套介面及可寫也可讀,需要進一步判斷
		if(FD_ISSET(m_fd, &rest) && FD_ISSET(m_fd, &west)) 
		{
			if(getsockopt(m_fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0)
			{
				printf("getsockopt error!!!\n");
			}
			else
			{
				if(error == 0)
				{
					ret = true;
				}
				else 
				{
					printf("connect getsockopt error!!! %d\n",error);
				}
			}
		}
		//如果套介面可寫不可讀,則連結完成
		else if(FD_ISSET(m_fd, &west) && !FD_ISSET(m_fd, &rest)) 
		{ 
			ret = true;
		}
	}
	else if(selectFlag == 0)
	{
		printf("connect select timeout!!!\n");
	}
	else 
	{
		printf("connect select error!!!\n");
	}
	
done:
	setBlockOpt(m_fd,true);// 設定為阻塞模式
	if(!ret)
	{
		return -1;
	}
	return 0;
}


int main(int argc,char* argv[])
{
	if(argc <= 1)
	{
		printf("input error!!!\n");
		exit(1);
	}
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0) 
	{
		exit(1);
	}
	if(connectWithTimeout(sockfd,atoi(argv[1])) == 0)
	{
		printf("connect sucess!!!\n");
	}
	else
	{
		printf("connect filed!!!\n");
	}
	close(sockfd);
	
	
	return 0;
}


       原理很簡單,就是先把套接字設定為非阻塞,因為在非阻塞情況下,connect的結果是立即返回的,然後我們再使用select或者poll等機制來檢測套接字一定的時間,如果在超時時間內不可寫,則認為connect失敗,然後需要把套接字重新設定為阻塞,當然如果你不需要在阻塞模式下工作,可以不用設定。

      如上,我們就可以對connect的超時進行可控。

相關推薦

如何解決Connect超時導致阻塞問題

        這幾天發現一個現象,客戶端正常連線伺服器connect顯然不會出現問題。         在異常情況下,如果是伺服器出現異常,connect能夠立即返回失敗;但是當客戶端出現異常的情況下,分為兩種情況:         一種是不插網線,客戶端沒有獲得ip地址

TCP解決connect函式的超時問題

在一個TCP套介面被設定為非阻塞之後呼叫connect,connect會立即返回EINPROGRESS錯誤,表示連線操作正在進行中,但是仍未完成;同時TCP的三路握手操作繼續進行;在這之後,我們可以呼叫select來檢查這個連結是否建立成功;非阻塞connect有三種用途:

MongoDB3.4安裝配置以及與Robomongo1.1的連接——解決Authentication Failed導致的不能連接問題

class .exe oca with www 解決方法 comm ror 命令 本文環境:win10(64)+MongoDB(3.4.5)+Robomongo(1.1) 目錄: MongoDB的安裝 MongoDB的配置 Robomongo的安裝以及與MongoDB的連

如何監控和解決SQL Server的阻塞(1) (當前阻塞)

host tab tool alt 現象 dmv fonts 毫秒 .text 1. 什麽是"阻塞"? 阻塞是SQL數據庫應用"鎖"機制的一個副作用。當一個應用請求針對某個數據庫對象(例如全表,某行數據, 或者是某個數據頁)加鎖後,那麽這個鎖會阻塞其它的應用請求。這就好像你

解決高亮導致的列表元素整體上移

有一個列表,基本結構如下 <ul> <li>你好</li> <li>你好</li> <li>你好</li> </ul> 樣式基本如下 ul >li { marg

如何解決ssh超時無輸入自動斷開連線的問題

本文轉自百度知道“不理你的帥哥”的回答,可以說很全了。 方法有以下三種:1.修改server端的etc/ssh/sshd_config ClientAliveInterval 60 #server每隔60秒傳送一次請求給client,然後client響應,從而保持連線 ClientAliveCo

Spark專案實戰-troubleshooting之解決JVM GC導致的shuffle檔案拉取失敗

一、shuffle檔案拉取失敗的背景介紹 我們知道Executor是一個JVM程序,在其內部有一個BlockManager用於管理該executor的一些資料。 Map端的task在往磁盤裡寫檔案的時候,會通過BlockManager來維護底層的資料,同時也會將資料的元資訊

超時導致的Galera節點加入叢集失敗

需求:為galera叢集新增新的節點。   初始化新的節點,加入的時候一直報錯,加入失敗,報錯日誌如下 1 WSREP_SST: [ERROR] Removing /var/lib/mysql//.sst/xtrabackup_galera_info file due to signal

解決異常斷電導致資料被損壞的問題

原因:拿到問題機器,把data/system/users/0/settings_global.xml匯出來,provisioned值確實變成0了,如下: 另外,如果在正常的機器上,手動把這個值改成0,同樣能復現這個現象。 所以,我們暫時把\frameworks\base\packages\

大資料下載防止系統崩潰解決方案一:阻塞集中式下載

/**  * 下載控制器  * 阻塞集中式下載 防止系統崩潰  * 使用定時過期的快取鎖實現  * 每10分鐘只能下載一次  **/ import com.google.common.cache.Cache; import com.google.common

linux下connect超時時間探究

最近在linux做伺服器開發的時候,發現了一個現象:伺服器在啟動的時候呼叫了 connect 函式,因為連線了一個不可用的埠,導致connect最後報出了 “Connection timed out” 的錯誤。但是這中間過了六十多秒的時間。 為何會等待這麼長的時間才超時呢?這個時間又在哪裡設定? 《UNI

Spark troubleshooting shuffle定址 以及 解決JVM GC導致拉取檔案失敗

shuffle定址圖 shuffle檔案定址基礎知識 MapOutputTracker spark架構中的一個主從模組 Driver端主物件MapOutputTrackerMaster Executor端從物件MapOutputTrackerWorker BlockMa

快速解決因googleapis導致wordpress後臺頁面開啟慢的問題

由於google在國內無法訪問,wordpress的預設字型則是從googleapis介面獲取的,這就導致wordpress的預設主題以及後臺頁面載入非常慢,在瀏覽器左下角處提示“正在等待fonts.googleapis.com的響應”,直到超時。 我在忍受了一段時間後,終於決定整改,在網上找到一

VC socket Connect 超時時間設定

設定connect超時很簡單,CSDN上也有人提到過使用select,但卻沒有一個令人滿意與完整的答案。偶所講的也正是select函式,此函式整合在winsock1.1中,簡單點講,"作用使那些想避免在套接字呼叫過程中被鎖定的應用程式,採取一種有序的方式,同時對多個套接字進

使用阿里DNS解決DNS劫持導致的上網問題,Fuck DX!!!

你上網慢未必是你的頻寬不夠,也可能是黑心DNS服務商搞的鬼! 年後換了工作,搬到新租的房子裡。入住前問房東關於寬頻的事情,說是10M的無線路由。感覺可以開心的上網了。 當我開開心心地連上路由開始上網的時候,煩惱的事情就開始了。白天的時候還好,開個網頁、看個電影啥的倒還可以,

Linux下connect超時處理【總結】

1、前言   最近在寫一個測試工具,要求快速的高效率的掃描出各個伺服器開放了哪些埠。當時想了一下,ping只能檢測ip,判斷伺服器的網路是連通的,而不能判斷是否開放了埠。我們知道埠屬於網路的傳輸層,因此需要用ip和埠來探測,這個時候就可以用connect來探測一下,針對TCP協議,connect函式要進行T

Android學習:AsyncTask方案解決UI執行緒阻塞

post方式能解決UI執行緒阻塞問題,但是程式碼的可讀性較差。 一:看程式 package com.example.testuithread; import android.app.Activit

設定TCP connect超時時間的2種方法

1.常用方法設定socket非阻塞,之後使用select等設定超時時間2.使用alarm訊號量需要注意:執行緒訊號量掩碼是執行緒私有的,當指定程序遞交訊號量時,作業系統會將訊號量遞交至該程序中未遮蔽該訊號量的所有執行緒中的隨機之一。見 man 7 signal:Asignal

Jedis “Socket讀取超時導致“返回值型別錯誤”

從異常資訊來看,首先是在'zadd'操作時出現"Socket讀取超時異常",具體異常資訊"JedisConnectionException: java.net.SocketTimeoutExce

android中用AsyncTask解決UI執行緒阻塞

package com.example.mm.helloworld; import android.os.AsyncTask; import android.support.v7.app.AppCom