UDP 超時重傳機制

問題來源:
老式方法:UDP傳輸設定超時未N秒,發送一個請求後等待N秒鐘,若超時都沒有收到確認,則重發請求,重發一定次數後便丟棄。
老式方法不合理的原因:由於網絡上影響因素的不同,可能RTT差別較大,設定一個固定的超時時間使資源不能得到合理應用。
較好的方法:根據實測的RTT及其他因素考慮在內來估計超時時間。
術語
RTO:重傳超時
Srtt:平滑化的RTT估算因子
Reevar:平滑化平均偏差估算因子
G:施加在RTT估算因子上的增益1/4
H:施加在平均偏差估算因子上的增益1/8
計算規則:
下一次待用的RTO = srtt+4*rttvar; 初始化爲4*0.75=3S
Delta = RTT –srtt;
Srtt+=g*delta;
Rttvar+=h*(|delta| - rttvar);
RTO = srtt + 4*rttvar;

Jacobson算法:當重傳定時器到時,對下一個RTO實行指數回退。每次測得一個RTT就計算一次RTO。
Jacobson算法不足:第一次請求發出後,超時後爲收到,接着發出第二次請求,剛好又收到第一次請求的應答。 這樣第二次的請求會把第一次的應答當做他的應答來計算RTT,這明顯有二義性。當客戶收到一個應答時,他不知道這個應答時針對那一次請求的。


Karn算法:只根據沒有重傳的請求產生的應答來計算RTT,對於重傳而產生的應答不計算RTT。若收到重傳的應答,這個RTO還是用於下一次請求。

實際應用karn算法時,給每個請求冠一個服務器必須回射的序列號和服務器同樣必須回射的時間戳。
發送一個請求時,把這個時間戳保存一併發出,服務器端並不修改時間戳。服務器端回射這個請求時,再將這個回射的時間戳返回來。 客戶根據本地時間與時間戳之差就可以計算RTT。

#include "unp.h"
#include <setjmp.h>
#include <time.h>


static sigjmp_buf buf;
void func(int signo){
	return siglongjmp(buf, 0);
}
int main(){
	/*other process*/
	
	signal(SIGALRM, func);
	//清零重發計數器
	again:
	sendto();
	if(sigsetjmp(buf, 1) != 0){/*由信號處理函數返回時*/
		/*超時處理*/
		if(/*超過重試次數*/){
			errno = ETIMEOUT;
			return -1;
		}
		else{
			/*添加指數回退*/
			goto again;
		}
	}
	for( ; ; ){
		n = recvfrom();
		if(/*當前收到的應答的序列號和請求序列號相同*/)
			break;
	}
	alarm(0);/*在超時範圍內成功接收,關閉定時器*/
	//重新計算RTO用於下一次發送請求
	return n;
}



實現:
struct rtt_info{
	float rtt_rtt;		/*RTT 往返延時*/
	float rtt_srtt;		/*SRTT 平滑後的RTT估算因子*/
	float rtt_rttval;	/*rttval  平滑後的平均偏差估算因子*/
	float rtt_rto;		/*RTO 重傳超時*/
	int rtt_nrexmt;	/*重傳計數器,每個請求都初始化爲0*/
	uint32_t rtt_base;	/*時間戳基數*/
};
#define RTT_RXTMIN 2 /*最小重傳超時時間*/
#define RTT_RXTMAX 60   /*最大重傳超時時間*/
#define RTT_MAXNREXMT /*最大重傳次數*/
#define RTT_RTOCALC(ptr)  (ptr)->rtt_srtt + 4*(ptr)->rtt_rttval  /*計算RTO宏*/
int rtt_init(struct rtt_info *info){/*僅調用一次用於初始化*/
	struct timeval t;
	bzero(info, sizeof(struct rtt_info));
	info->rtt_rttval = 0.75;/*初始化RTO爲3S*/
	info->rtt_rto = info->rtt_rtt +(info->rtt_rttval)*4;/*3秒*/
	if(gettimofday(&t, NULL) < 0 )
		return -1;
	info->rtt_base = t.tv_sec; /*取得時間戳基數,用於計算RTT*/
	return 0;
}
uint32_t rtt_ts(struct rtt_info *info){/*取得當前的時間戳*/
	struct timeval *t;
	if(gettimeofday(&t, NULL) < 0)
		return -1;
	return  (t.tvsec - info->rtt_base)*1000 + t.tv_usec / 1000; /*毫秒爲單位*/
}
void rtt_newpack(struct rtt_info *info){/*在第一次發送請求時使用*/
	info->rtt_nrexmt = 0;
}
float rtt_minmax(float rto){ /*RTO的最大最小限制*/
	if(rto > RTT_RXTMAX)
		rto = RTT_RXTMAX;
	else if(rto < RTT_RXTMIN){
		rto = RTT_RXTMIN;
	}
	return rto;
}
void rtt_stop(struct rtt_info *info, uint32_t ms){/*收到應答後,計算RTT估算因子和RTO*/
	double delte;
	info->rtt_rtt = ms / 1000;
	delta = info->rtt_rtt - info->rtt_srtt;
	info->rtt_srtt += delta/8;
	info->rtt_rttval +=(abs(delta) - info->rtt_rttval)/4;
	info->rtt_rto = rtt_minmax(RTT_RTOCALC(info));
}

int rtt_timeout(struct rtt_info *info){
	info->rtt_rto *=2;/*指數回退*/
	if(info->rtt_nrexmt++ T_MAXNREXMT)
		return -1
	else
		return  0
}
	

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章