获取本地ip及域名对应ip
参考:
前言
最近部分用户socket一直连不上服务器,将日志给运维看时,运维说只能看到域名信息,检查了该域名在用户所在地区没什么问题,如果我们能提供该用户调用域名时解析的ip更方便排查,于是特研究记录了连接socket时域名解析成ip的过程并记录日志
完整获取域名解析的ip代码 TestDomainIP.h
1
2
3
4
#import <Foundation/Foundation.h>
@interface TestDomainIP: NSObject
+ (void)getDomainToIp:(NSString *)domain port:(int)port;
@end
TestDomainIP.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#import <netdb.h>
#import <arpa/inet.h>
@interface TestDomainIP()
@end
@implementation TestDomainIP
+ (void)getDomainToIp:(NSString *)domain port:(int)port {
NSString *portStr = [NSString stringWithFormat:@"%d", port];
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// 使用系统方法将域名解析成ip
int gai_error = getaddrinfo([domain UTF8String], [portStr UTF8String], &hints, &res);
if (!gai_error) {
if (res->ai_family == AF_INET){
// 获取ipv4
// 强转类型sockaddr * => sockaddr_in *
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
char ipAddress[INET_ADDRSTRLEN];
// 将解析到的ip字节流信息转为字符串
inet_ntop(AF_INET, &(ipv4->sin_addr), ipAddress, INET_ADDRSTRLEN);
printf("The IP address is: %s\n", ipAddress);
// 将C语言字符串转换为OC字符串
// NSString *ipHost = [NSString stringWithFormat:@"%s",ipAddress];
NSString *ipHost = [NSString stringWithUTF8String: ipAddress];
NSLog(@"The domain is: %@ IP address is: %@\n", domain, ipHost);
}
}
}
@end
测试:
1
2
3
TestDomainIP.getDomainToIp("www.baidu.com", port: 443)
The IP address is: 183.2.172.42
The domain is: www.baidu.com IP address is: 183.2.172.42
CocoaAsyncSocket
流程及修改
使用的socket连接库CocoaAsyncSocket
,该库的链接过程: try socket.connect(toHost: xx, onPort: xx)
-> …
-> - (BOOL)connectToHost:(NSString *)inHost onPort:(uint16_t)port viaInterface:(NSString *)inInterface withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
-> + (NSMutableArray *)lookupHost:(NSString *)host port:(uint16_t)port error:(NSError **)errPtr
getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
: 使用系统获取域名解析成ip addrinfo
:系统提供的解析域名的信息 ai_family
:域名类型(AF_INET
=ipv4、AF_INET6
=ipv6) ai_addr
:域名内容(类型为:sockaddr *
) ai_addrlen
:域名内容数据长度
CocoaAsyncSocket
里核心将域名转换为ip的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#import <netinet/in.h>
#import <ifaddrs.h>
#import <sys/socket.h>
// 将端口数字转换为字符串
NSString *portStr = [NSString stringWithFormat:@"%hu", port];
struct addrinfo hints, *res, *res0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// 使用系统方法将域名解析成ip
int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
if (gai_error)
{
error = [self gaiError:gai_error];
}
else
{
// 解析正确
NSUInteger capacity = 0;
for (res = res0; res; res = res->ai_next)
{
if (res->ai_family == AF_INET || res->ai_family == AF_INET6) {
// 如果是IPV4或IPV6的地址
capacity++;
}
}
addresses = [NSMutableArray arrayWithCapacity:capacity];
for (res = res0; res; res = res->ai_next)
{
if (res->ai_family == AF_INET)
{
// Found IPv4 address. IPV4的地址
// Wrap the native address structure, and add to results.
NSData *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
[addresses addObject:address4];
}
else if (res->ai_family == AF_INET6)
{
// Fixes connection issues with IPv6
// https://github.com/robbiehanson/CocoaAsyncSocket/issues/429#issuecomment-222477158
// Found IPv6 address.
// Wrap the native address structure, and add to results.
struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)(void *)res->ai_addr;
in_port_t *portPtr = &sockaddr->sin6_port;
if ((portPtr != NULL) && (*portPtr == 0)) {
*portPtr = htons(port);
}
NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
[addresses addObject:address6];
}
}
freeaddrinfo(res0);
if ([addresses count] == 0)
{
error = [self gaiError:EAI_FAIL];
}
}
修改源码部分一、(GCDAsyncSocket.h
):在socket正式开始连接时增加delegate方法
1
2
3
4
5
6
7
8
@protocol GCDAsyncSocketDelegate <NSObject>
@optional
// ...
- (void)socketDidGetIP:(NSString *)ipHost;
// ...
修改源码部分二、(GCDAsyncSocket.m
):在socket正式开始连接时增加下回调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@implementation GCDAsyncSocket
// ....
- (BOOL)connectWithAddress4:(NSData *)address4 address6:(NSData *)address6 error:(NSError **)errPtr
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
LogVerbose(@"IPv4: %@:%hu", [[self class] hostFromAddress:address4], [[self class] portFromAddress:address4]);
LogVerbose(@"IPv6: %@:%hu", [[self class] hostFromAddress:address6], [[self class] portFromAddress:address6]);
// Determine socket type
BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO;
// Create and bind the sockets
if (address4)
{
LogVerbose(@"Creating IPv4 socket");
socket4FD = [self createSocket:AF_INET connectInterface:connectInterface4 errPtr:errPtr];
}
if (address6)
{
LogVerbose(@"Creating IPv6 socket");
socket6FD = [self createSocket:AF_INET6 connectInterface:connectInterface6 errPtr:errPtr];
}
if (socket4FD == SOCKET_NULL && socket6FD == SOCKET_NULL)
{
return NO;
}
int socketFD, alternateSocketFD;
NSData *address, *alternateAddress;
if ((preferIPv6 && socket6FD != SOCKET_NULL) || socket4FD == SOCKET_NULL)
{
socketFD = socket6FD;
alternateSocketFD = socket4FD;
address = address6;
alternateAddress = address4;
}
else
{
socketFD = socket4FD;
alternateSocketFD = socket6FD;
address = address4;
alternateAddress = address6;
__strong id<GCDAsyncSocketDelegate> theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector: @selector(socketDidGetIP:inPort:ipHost:)])
{
// 强转类型sockaddr * => sockaddr_in *
struct sockaddr_in *ipv4 = (struct sockaddr_in *)[address4 bytes];;
char ipAddress[INET_ADDRSTRLEN];
// 将解析到的ip字节流信息转为字符串
inet_ntop(AF_INET, &(ipv4->sin_addr), ipAddress, INET_ADDRSTRLEN);
// printf("The IP address is: %s\n", ipAddress);
// 将C语言字符串转换为OC字符串
NSString *ipHost = [NSString stringWithFormat:@"%s",ipAddress];
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate socketDidGetIP:ipHost];
}});
}
}
int aStateIndex = stateIndex;
[self connectSocket:socketFD address:address stateIndex:aStateIndex];
if (alternateAddress)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(alternateAddressDelay * NSEC_PER_SEC)), socketQueue, ^{
[self connectSocket:alternateSocketFD address:alternateAddress stateIndex:aStateIndex];
});
}
return YES;
}
// ...