66// Copyright © 2015年 Qiniu Cloud Storage. All rights reserved.
77//
88
9+ #import < sys/socket.h>
10+ #import < netinet/in.h>
11+ #import < arpa/inet.h>
12+ #import < unistd.h>
13+ #import < netdb.h>
14+
15+ #import < netinet/tcp.h>
16+ #import < netinet/in.h>
17+
18+ #include < AssertMacros.h>
19+
920#import " QNNPing.h"
1021
11- typedef struct PingPacket{
22+ @interface QNNPingResult ()
23+
24+ -(instancetype )init : (NSInteger )code
25+ max : (NSTimeInterval )maxRtt
26+ min : (NSTimeInterval )minRtt
27+ avg : (NSTimeInterval )avgRtt
28+ loss : (double )lossRate
29+ interval : (NSInteger )interval
30+ count : (NSInteger )count ;
31+ @end
32+
33+ @implementation QNNPingResult
34+
35+ -(NSString *) description {
36+ if (_code == 0 ) {
37+ return [NSString stringWithFormat: @" ping %d times, min/avg/max = %f /%f /%f ms loss %f " , _count, _minRtt, _avgRtt, _maxRtt, _lossRate];
38+ }
39+ return [NSString stringWithFormat: @" ping failed %d " , _code];
40+ }
41+
42+ -(instancetype )init : (NSInteger )code
43+ max : (NSTimeInterval )maxRtt
44+ min : (NSTimeInterval )minRtt
45+ avg : (NSTimeInterval )avgRtt
46+ loss : (double )lossRate
47+ interval : (NSInteger )interval
48+ count : (NSInteger )count {
49+ if (self = [super init ]) {
50+ _code = code;
51+ _minRtt = minRtt;
52+ _avgRtt = avgRtt;
53+ _maxRtt = maxRtt;
54+ _lossRate = lossRate;
55+ _interval = interval;
56+ _count = count;
57+ }
58+ return self;
59+ }
60+
61+ @end
62+
63+ // IP header structure:
64+
65+ struct IPHeader {
66+ uint8_t versionAndHeaderLength;
67+ uint8_t differentiatedServices;
68+ uint16_t totalLength;
69+ uint16_t identification;
70+ uint16_t flagsAndFragmentOffset;
71+ uint8_t timeToLive;
72+ uint8_t protocol;
73+ uint16_t headerChecksum;
74+ uint8_t sourceAddress[4 ];
75+ uint8_t destinationAddress[4 ];
76+ // options...
77+ // data...
78+ };
79+ typedef struct IPHeader IPHeader;
80+
81+ check_compile_time (sizeof (IPHeader) == 20);
82+ check_compile_time (offsetof(IPHeader, versionAndHeaderLength) == 0);
83+ check_compile_time (offsetof(IPHeader, differentiatedServices) == 1);
84+ check_compile_time (offsetof(IPHeader, totalLength) == 2);
85+ check_compile_time (offsetof(IPHeader, identification) == 4);
86+ check_compile_time (offsetof(IPHeader, flagsAndFragmentOffset) == 6);
87+ check_compile_time (offsetof(IPHeader, timeToLive) == 8);
88+ check_compile_time (offsetof(IPHeader, protocol) == 9);
89+ check_compile_time (offsetof(IPHeader, headerChecksum) == 10);
90+ check_compile_time (offsetof(IPHeader, sourceAddress) == 12);
91+ check_compile_time (offsetof(IPHeader, destinationAddress) == 16);
92+
93+ typedef struct ICMPPacket{
1294 uint8_t type;
1395 uint8_t code;
1496 uint16_t checksum;
1597 uint16_t identifier;
1698 uint16_t sequenceNumber;
17- uint8_t payload[1 ]; // data, variable length
18- }PingPacket;
99+ uint8_t payload[0 ]; // data, variable length
100+ }ICMPPacket;
101+
102+ enum {
103+ kQNNICMPTypeEchoReply = 0 , // code is always 0
104+ kQNNICMPTypeEchoRequest = 8 // code is always 0
105+ };
106+
107+ check_compile_time (sizeof (ICMPPacket) == 8);
108+ check_compile_time (offsetof(ICMPPacket, type) == 0);
109+ check_compile_time (offsetof(ICMPPacket, code) == 1);
110+ check_compile_time (offsetof(ICMPPacket, checksum) == 2);
111+ check_compile_time (offsetof(ICMPPacket, identifier) == 4);
112+ check_compile_time (offsetof(ICMPPacket, sequenceNumber) == 6);
113+
114+ const int kQNNPacketSize = sizeof (ICMPPacket) + 100 ;
19115
20116static uint16_t in_cksum (const void *buffer, size_t bufferLen)
21117// This is the standard BSD checksum code, modified to use modern types.
@@ -59,17 +155,95 @@ static uint16_t in_cksum(const void *buffer, size_t bufferLen)
59155 return answer;
60156}
61157
62- static PingPacket* build_packet (int seq){
63- return NULL ;
158+ static ICMPPacket* build_packet (uint16_t seq, uint16_t identifier){
159+ ICMPPacket* packet = (ICMPPacket*)calloc (kQNNPacketSize , 1 );
160+
161+ packet->type = kQNNICMPTypeEchoRequest ;
162+ packet->code = 0 ;
163+ packet->checksum = 0 ;
164+ packet->identifier = OSSwapHostToBigInt16 (identifier);
165+ packet->sequenceNumber = OSSwapHostToBigInt16 (seq);
166+ snprintf ((char *)packet->payload , kQNNPacketSize - sizeof (ICMPPacket), " qiniu ping test %d " , (int )seq);
167+ packet->checksum = in_cksum (packet, kQNNPacketSize );
168+ return packet;
64169}
65170
66171@interface QNNPing ()
172+ @property (readonly ) NSString * host;
67173@property (nonatomic , strong ) id <QNNOutputDelegate> output;
68- @property NSInteger sequenceNumber;
174+ @property (readonly ) QNNPingCompleteHandler complete;
175+
176+ @property (readonly ) NSInteger interval;
177+ @property (readonly ) NSInteger count;
178+ @property (atomic ) BOOL stopped;
69179@end
70180
71181@implementation QNNPing
72182
183+ -(NSInteger )sendPacket : (ICMPPacket*)packet
184+ sock : (int )sock
185+ target : (struct sockaddr *)addr {
186+ int sent = sendto (sock, packet, (size_t )kQNNPacketSize , 0 , addr, (socklen_t )sizeof (struct sockaddr));
187+ if (sent < 0 ) {
188+ return errno;
189+ }
190+ return 0 ;
191+ }
192+
193+ -(void )run {
194+ struct sockaddr_in addr;
195+ memset (&addr, 0 , sizeof (addr));
196+ addr.sin_len = sizeof (addr);
197+ addr.sin_family = AF_INET;
198+ addr.sin_addr .s_addr = inet_addr ([_host UTF8String ]);
199+ if (addr.sin_addr .s_addr == INADDR_NONE) {
200+ struct hostent *host = gethostbyname ([_host UTF8String ]);
201+ if (host == NULL || host->h_addr == NULL ) {
202+ [self .output write: @" Problem accessing the DNS" ];
203+ if (_complete != nil ) {
204+ dispatch_async (dispatch_get_main_queue (), ^(void ) {
205+ QNNPingResult* result = [[QNNPingResult alloc ] init: -1006 max: 0 min: 0 avg: 0 loss: 0 interval: 0 count: 0 ];
206+ _complete (result);
207+ });
208+ }
209+ return ;
210+ }
211+ addr.sin_addr = *(struct in_addr *)host->h_addr ;
212+ [self .output write: [NSString stringWithFormat: @" ping to ip %s ...\n " , inet_ntoa (addr.sin_addr)]];
213+ }
214+
215+ NSTimeInterval * durations = (NSTimeInterval *)malloc (sizeof (NSTimeInterval )*_count);
216+ NSInteger index = 0 ;
217+ int r = 0 ;
218+ do {
219+ NSDate * t1 = [NSDate date ];
220+ r = [self connect: &addr];
221+ NSTimeInterval duration = [[NSDate date ] timeIntervalSinceDate: t1];
222+ intervals[index] = duration;
223+ if (r == 0 ) {
224+ [self .output write: [NSString stringWithFormat: @" connected to %s :%d , %f ms\n " , inet_ntoa (addr.sin_addr), _port, duration*1000 ]];
225+ }else {
226+ [self .output write: [NSString stringWithFormat: @" connect failed to %s :%d , %f ms, error %d \n " , inet_ntoa (addr.sin_addr), _port, duration*1000 , r]];
227+ }
228+
229+ if (index < _count && !_stopped && r == 0 ) {
230+ [NSThread sleepForTimeInterval: 0.1 ];
231+ }
232+ } while (++index < _count && !_stopped && r == 0 );
233+
234+ if (_complete) {
235+ NSInteger code = r;
236+ if (_stopped){
237+ code = kQNNRequestStoped ;
238+ }
239+ dispatch_async (dispatch_get_main_queue (), ^(void ) {
240+ QNNPingResult result =
241+ _complete ([self buildResult: code durations: intervals count: index]);
242+ });
243+ }
244+ free (durations);
245+ }
246+
73247+(instancetype ) start : (NSString *)host
74248 output : (id <QNNOutputDelegate>)output
75249 complete : (QNNPingCompleteHandler)complete {
@@ -85,6 +259,7 @@ +(instancetype) start:(NSString*)host
85259}
86260
87261-(void )stop {
262+ _stopped = YES ;
88263 return ;
89264}
90265
0 commit comments