移动端 IP 优选方案
摘要: 無論是從 Local DNS 解析域名,獲取到 IP 列表,還是從第三方的 DNS 解析服務中,獲取到域名對應的 IP 列表。我們獲得多個 IP 后,總是想選取一個最優的 IP 使用,本文主要探討如何在客戶端探測 IP 的連接性以及連接速度,保證返回可用性最好的IP,以達到“IP優選”的目的。
移動端 IP 優選方案
1. IP 優選目的
無論是從 Local DNS 解析域名,獲取到 IP 列表,還是從第三方的 DNS 解析服務中,獲取到域名對應的 IP 列表。我們獲得多個 IP 后,總是想選取一個最優的 IP 使用,本文主要探討如何在客戶端探測 IP 的連接性以及連接速度,保證返回可用性最好的IP,以達到“IP優選”的目的。
2. 新浪開源的 httpdns 的 sdk 里的測速邏輯
新浪開源一個 HTTPDNSLib ,里面包含了測速邏輯,GitHub地址如下:
《HTTPDNSLib-for-iOS》
《HTTPDNSLib》
我們以該 sdk 里的測速邏輯為例進行原理解析。
3. IP 測試實現原理
使用 linux socket connect 和 select 函數實現的。 基于以下原理:
即使套接口是非阻塞的。如果連接的服務器在同一臺主機上,那么在調用connect 建立連接時,連接通常會立即建立成功,我們必須處理這種情況。
源自Berkeley的實現(和Posix.1g)有兩條與select 和非阻塞IO相關的規則:
A. 當連接建立成功時,套接口描述符變成可寫;
B. 當連接出錯時,套接口描述符變成既可讀又可寫。
詳細的測速實現如下,原理參考注釋:
以 iOS 實現為例:
- (int)testSpeedOf:(NSString *)ip port:(int16_t)port {NSString *oldIp = ip;//request time outfloat rtt = 0.0;//sock:將要被設置或者獲取選項的套接字。int s = 0;struct sockaddr_in saddr;saddr.sin_family = AF_INET;// MARK: - 設置端口,這里需要根據需要自定義,默認是80端口。saddr.sin_port = htons(port);saddr.sin_addr.s_addr = inet_addr([ip UTF8String]);//saddr.sin_addr.s_addr = inet_addr("1.1.1.123");if( (s=socket(AF_INET, SOCK_STREAM, 0)) < 0) {NSLog(@"ERROR:%s:%d, create socket failed.",__FUNCTION__,__LINE__);return 0;}NSDate *startTime = [NSDate date];NSDate *endTime;//為了設置connect超時 把socket設置稱為非阻塞int flags = fcntl(s, F_GETFL,0);fcntl(s,F_SETFL, flags | O_NONBLOCK);//對于阻塞式套接字,調用connect函數將激發TCP的三次握手過程,而且僅在連接建立成功或者出錯時才返回;//對于非阻塞式套接字,如果調用connect函數會之間返回-1(表示出錯),且錯誤為EINPROGRESS,表示連接建立,建立啟動但是尚未完成;//如果返回0,則表示連接已經建立,這通常是在服務器和客戶在同一臺主機上時發生。int i = connect(s,(struct sockaddr*)&saddr, sizeof(saddr));if(i == 0) {//建立連接成功,返回rtt時間。 因為connect是非阻塞,所以這個時間就是一個函數執行的時間,毫秒級,沒必要再測速了。close(s);return 1;}struct timeval tv;int valopt;socklen_t lon;tv.tv_sec = HTTPDNS_SOCKET_CONNECT_TIMEOUT;tv.tv_usec = 0;fd_set myset;FD_ZERO(&myset);FD_SET(s, &myset);// MARK: - 使用select函數,對套接字的IO操作設置超時。/**select函數select是一種IO多路復用機制,它允許進程指示內核等待多個事件的任何一個發生,并且在有一個或者多個事件發生或者經歷一段指定的時間后才喚醒它。connect本身并不具有設置超時功能,如果想對套接字的IO操作設置超時,可使用select函數。**/int maxfdp = s+1;int j = select(maxfdp, NULL, &myset, NULL, &tv);if (j == 0) {NSLog(@"INFO:%s:%d, test rtt of (%@) timeout.",__FUNCTION__,__LINE__, oldIp);rtt = HTTPDNS_SOCKET_CONNECT_TIMEOUT_RTT;close(s);return rtt;}if (j < 0) {NSLog(@"ERROR:%s:%d, select function error.",__FUNCTION__,__LINE__);rtt = 0;close(s);return rtt;}/**對于select和非阻塞connect,注意兩點:[1] 當連接成功建立時,描述符變成可寫; [2] 當連接建立遇到錯誤時,描述符變為即可讀,也可寫,遇到這種情況,可調用getsockopt函數。**/lon = sizeof(int);//valopt 表示錯誤信息。// MARK: - 測試核心邏輯,連接后,獲取錯誤信息,如果沒有錯誤信息就是訪問成功/*!* //getsockopt函數可獲取影響套接字的選項,比如SOCKET的出錯信息* (get socket option)*/getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);//如果有錯誤信息:if (valopt) {NSLog(@"ERROR:%s:%d, select function error.",__FUNCTION__,__LINE__);rtt = 0;} else {endTime = [NSDate date];rtt = [endTime timeIntervalSinceDate:startTime] * 1000;}close(s);return rtt; }總結
以上是生活随笔為你收集整理的移动端 IP 优选方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSDN企业数字化之路 ——「低代码」发
- 下一篇: 深度剖析:Redis 分布式锁到底安全吗