We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
无论是从 Local DNS 解析域名,获取到 IP 列表,还是从第三方的 DNS 解析服务中,获取到域名对应的 IP 列表。我们获得多个 IP 后,总是想选取一个最优的 IP 使用,本文主要探讨如何在客户端探测 IP 的连接性以及连接速度,保证返回可用性最好的IP,以达到“IP优选”的目的。
新浪开源一个 HTTPDNSLib ,里面包含了测速逻辑,GitHub地址如下:
我们以该 sdk 里的测速逻辑为例进行原理解析。
使用 linux socket connect 和 select 函数实现的。 基于以下原理:
详细的测速实现如下,原理参考注释:
以 iOS 实现为例:
//#define CYL_SOCKET_CONNECT_TIMEOUT 10 //单位秒 //#define CYL_SOCKET_CONNECT_TIMEOUT_RTT 600000//10分钟 单位毫秒 - (int)testSpeedOf:(NSString *)ip port:(int16_t)port { NSString *oldIp = ip; //request time out float 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 = CYL_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 = CYL_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; }
注意:当出现错误的时候测试结果是速度是0,所以排序时不能简单地按照值大小排序,可以先删除速度为0的结果,或者将速度为零重置为超时时间,比如上面的 CYL_SOCKET_CONNECT_TIMEOUT_RTT 。避免错误IP为0,结果排序后排在前面。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
1. IP 测速排序方案的目的
无论是从 Local DNS 解析域名,获取到 IP 列表,还是从第三方的 DNS 解析服务中,获取到域名对应的 IP 列表。我们获得多个 IP 后,总是想选取一个最优的 IP 使用,本文主要探讨如何在客户端探测 IP 的连接性以及连接速度,保证返回可用性最好的IP,以达到“IP优选”的目的。
2. 新浪开源的 httpdns 的 sdk 里的测速逻辑
新浪开源一个 HTTPDNSLib ,里面包含了测速逻辑,GitHub地址如下:
我们以该 sdk 里的测速逻辑为例进行原理解析。
3. IP 测试实现原理
使用 linux socket connect 和 select 函数实现的。 基于以下原理:
A. 当连接建立成功时,套接口描述符变成可写;
B. 当连接出错时,套接口描述符变成既可读又可写。
详细的测速实现如下,原理参考注释:
以 iOS 实现为例:
注意:当出现错误的时候测试结果是速度是0,所以排序时不能简单地按照值大小排序,可以先删除速度为0的结果,或者将速度为零重置为超时时间,比如上面的 CYL_SOCKET_CONNECT_TIMEOUT_RTT 。避免错误IP为0,结果排序后排在前面。
The text was updated successfully, but these errors were encountered: