【问题标题】:Get ping latency from host从主机获取 ping 延迟
【发布时间】:2014-04-23 13:40:39
【问题描述】:

我正在尝试从主机获得延迟一段时间,但我陷入了困境。已经尝试过Simple Ping ,但似乎它没有返回延迟。我所做的最接近的是当我将TKC-PingTest 用于MAC OS 时。这很完美,但只能在 iPhone 模拟器中使用,因为当使用 iPhone 时,由于 TKC 使用的补丁“/sbin/ping”而出现错误。除了这两个,我已经尝试了很多其他的,但一无所获。

【问题讨论】:

  • 为什么你不能只修改你链接到的简单 Ping 示例代码?这肯定就像添加 NSTimer 一样简单,以计算从发送 ping 到收到响应需要多长时间?
  • 我不认为我理解你。我没有使用 NSTimer。简单 Ping 不会返回延迟。

标签: ios objective-c ping bandwidth latency


【解决方案1】:

以下是完整的工作示例,它只 ping 一次给定地址,然后以毫秒为单位返回 ping 时间:

Objective-C

@interface SimplePingClient : NSObject<SimplePingDelegate>

+(void)pingHostname:(NSString*)hostName andResultCallback:(void(^)(NSString* latency))result;

@end

@interface SimplePingClient()
{
    SimplePing* _pingClient;
    NSDate* _dateReference;
}

@property(nonatomic, strong) void(^resultCallback)(NSString* latency);

@end

@implementation SimplePingClient

+(void)pingHostname:(NSString*)hostName andResultCallback:(void(^)(NSString* latency))result
{
    static SimplePingClient* singletonPC = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singletonPC = [[SimplePingClient alloc] init];
    });

    //ping hostname
    [singletonPC pingHostname:hostName andResultCallBlock:result];
}

-(void)pingHostname:(NSString*)hostName andResultCallBlock:(void(^)(NSString* latency))result
{
    _resultCallback = result;
    _pingClient = [SimplePing simplePingWithHostName:hostName];
    _pingClient.delegate = self;
    [_pingClient start];
}

#pragma mark - SimplePingDelegate methods
- (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address
{
    [pinger sendPingWithData:nil];
}

- (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error
{
    _resultCallback(nil);
}

- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet
{
    _dateReference = [NSDate date];
}

- (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet error:(NSError *)error
{
    [pinger stop];
    _resultCallback(nil);
}

- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet
{
    [pinger stop];
    NSDate *end=[NSDate date];
    double latency = [end timeIntervalSinceDate:_dateReference] * 1000;//get in miliseconds
    _resultCallback([NSString stringWithFormat:@"%.f", latency]);
}

- (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet
{
    [pinger stop];
    _resultCallback(nil);
}

@end

示例用法如下:

[SimplePingClient pingHostname:@"www.apple.com"
             andResultCallback:^(NSString *latency) {

                 NSLog(@"your latency is: %@", latency ? latency : @"unknown");

             }];

斯威夫特

import Foundation

public typealias SimplePingClientCallback = (String?)->()

public class SimplePingClient: NSObject {
    static let singletonPC = SimplePingClient()

    private var resultCallback: SimplePingClientCallback?
    private var pingClinet: SimplePing?
    private var dateReference: NSDate?

    public static func pingHostname(hostname: String, andResultCallback callback: SimplePingClientCallback?) {
        singletonPC.pingHostname(hostname, andResultCallback: callback)
    }

    public func pingHostname(hostname: String, andResultCallback callback: SimplePingClientCallback?) {
        resultCallback = callback
        pingClinet = SimplePing(hostName: hostname)
        pingClinet?.delegate = self
        pingClinet?.start()
    }
}

extension SimplePingClient: SimplePingDelegate {
    public func simplePing(pinger: SimplePing!, didStartWithAddress address: NSData!) {
        pinger.sendPingWithData(nil)
    }

    public func simplePing(pinger: SimplePing!, didFailWithError error: NSError!) {
        resultCallback?(nil)
    }

    public func simplePing(pinger: SimplePing!, didSendPacket packet: NSData!) {
        dateReference = NSDate()
    }

    public func simplePing(pinger: SimplePing!, didFailToSendPacket packet: NSData!, error: NSError!) {
        pinger.stop()
        resultCallback?(nil)
    }

    public func simplePing(pinger: SimplePing!, didReceiveUnexpectedPacket packet: NSData!) {
        pinger.stop()
        resultCallback?(nil)
    }

    public func simplePing(pinger: SimplePing!, didReceivePingResponsePacket packet: NSData!) {
        pinger.stop()

        guard let dateReference = dateReference else { return }

        //timeIntervalSinceDate returns seconds, so we convert to milis
        let latency = NSDate().timeIntervalSinceDate(dateReference) * 1000

        resultCallback?(String(format: "%.f", latency))
    }
}

用法:

SimplePingClient.pingHostname("www.apple.com") { latency in

            print("Your latency is \(latency ?? "unknown")")
        }

为方便起见,我使用SimplePing,如文档中所述,它与 iOS 完全兼容:

SimplePing 在 Mac OS X 10.7 及更高版本上运行,尽管核心代码在所有版本的 iOS 上都可以正常工作,而底层方法也可以在早期版本的 Mac OS X(回到 10.2)上运行。

请注意,我使用的是单例,因为我反复检查延迟,但是如果您只需要一次,您可以在没有单例实例的情况下采用它。 SimplePing 还使用主机,这将阻塞您的主线程,因此在单独的线程中调用它可能很有用。

【讨论】:

  • 我在找什么! :)
  • 很棒的实现!
  • 感谢您提供非常有用的示例。
【解决方案2】:

您可以轻松扩展简单的 ping 以计算延迟。 Simpleping.h 定义了 SimplePingDelegate 协议。有两种有趣的方法 - didSendPacketdidReceivePingResponsePacket。用于计时延迟的幼稚实现将是

@property (strong,nonatomic) NSDate *start;

- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet
{
    self.start=[NSDate date];
}

- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet
{
    NSDate *end=[NSDate date];
    double latency = [end timeIntervalSinceDate:self.start]*1000.0;

    //TODO - Do something with latency
}

我说这是一个幼稚的实现,因为它不处理在收到响应之前发送另一个数据包或丢弃数据包的情况。为了解决这个问题,您需要检查数据包数据以确定发送和接收事件之间的序列号是否一致。

【讨论】:

  • 我将 NetworkTestViewController.h 设置为 @interface NetworkTestViewController : UITableViewController 并实现这两个方法。当我启动 pinger 时,它不会调用我班级中的方法。
【解决方案3】:

hris.to 的 Swift 3 实现回答:

import Foundation
public typealias SimplePingClientCallback = (String?)->()

public class SimplePingClient: NSObject {
    fileprivate static let singletonPC = SimplePingClient()

    fileprivate var resultCallback: SimplePingClientCallback?
    fileprivate var pingClinet: SimplePing?
    fileprivate var dateReference: Date?

    public static func pingHostname(hostname: String, andResultCallback callback: SimplePingClientCallback?) {
        singletonPC.pingHostname(hostname: hostname, andResultCallback: callback)
    }

    public func pingHostname(hostname: String, andResultCallback callback:  SimplePingClientCallback?) {
        resultCallback = callback
        pingClinet = SimplePing(hostName: hostname)
        pingClinet?.delegate = self
        pingClinet?.start()
    }
}

extension SimplePingClient: SimplePingDelegate {

    public func simplePing(_ pinger: SimplePing, didSendPacket packet: Data, sequenceNumber: UInt16){
        dateReference = Date()
    }

    public func simplePing(_ pinger: SimplePing, didStartWithAddress address: Data) {
        pinger.send(with: nil)
    }

    public func simplePing(_ pinger: SimplePing, didFailWithError error: Error) {
        resultCallback?(nil)
    }

    public func simplePing(_ pinger: SimplePing, didReceiveUnexpectedPacket packet: Data) {
        pinger.stop()
        resultCallback?(nil)
    }

    public func simplePing(_ pinger: SimplePing, didReceivePingResponsePacket packet: Data, sequenceNumber: UInt16) {
        pinger.stop()
        guard let dateReference = dateReference else { return }

      //timeIntervalSinceDate returns seconds, so we convert to milis
        let latency = Date().timeIntervalSince(dateReference) * 1000
        resultCallback?(String(format: "%.f", latency))
    }

    public func simplePing(_ pinger: SimplePing, didFailToSendPacket packet: Data, sequenceNumber: UInt16, error: Error) {
        pinger.stop()
        resultCallback?(nil)
    }

}

【讨论】:

    【解决方案4】:

    我从@hris.to 获取了漂亮的代码(谢谢!)并将其更新到最新的 Swift 版本。想我会分享:

    Swift 5.2

    public class SimplePingClient: NSObject {
        public typealias PingResultCompletion = (Result<Double, Error>) -> Void
    
        static let singletonPC = SimplePingClient()
    
        private var completion: PingResultCompletion?
        private var pingClient: SimplePing?
        private var dateReference: Date?
    
        public static func ping(hostname: String, completion: PingResultCompletion?) {
            singletonPC.ping(hostname: hostname, completion: completion)
        }
    
        public func ping(hostname: String, completion: PingResultCompletion?) {
            self.completion = completion
            pingClient = SimplePing(hostName: hostname)
            pingClient?.delegate = self
            pingClient?.start()
        }
    }
    
    extension SimplePingClient: SimplePingDelegate {
        public func simplePing(_ pinger: SimplePing, didStartWithAddress address: Data) {
            pinger.send(with: nil)
        }
    
        public func simplePing(_ pinger: SimplePing, didFailWithError error: Error) {
            completion?(.failure(error))
        }
    
        public func simplePing(_ pinger: SimplePing, didSendPacket packet: Data, sequenceNumber: UInt16) {
            dateReference = Date()
        }
    
        public func simplePing(_ pinger: SimplePing, didFailToSendPacket packet: Data, sequenceNumber: UInt16, error: Error) {
            pinger.stop()
            completion?(.failure(error))
        }
    
        public func simplePing(_ pinger: SimplePing, didReceiveUnexpectedPacket packet: Data) {
            pinger.stop()
            completion?(.failure(PingError.receivedUnexpectedPacket))
        }
    
        public func simplePing(_ pinger: SimplePing, didReceivePingResponsePacket packet: Data, sequenceNumber: UInt16) {
            pinger.stop()
            guard let dateReference = dateReference else { return }
    
            //timeIntervalSinceDate returns seconds, so we convert to milis
            let latency = Date().timeIntervalSince(dateReference) * 1000
            completion?(.success(latency))
        }
    
        enum PingError: Error {
            case receivedUnexpectedPacket
        }
    }
    

    用法:

    func pingApple() {
        SimplePingClient.ping(hostname: "www.apple.com") { result in
            switch result {
            case .success(let latency):
                print("Latency: \(latency)")
            case .failure(let error):
                print("Ping got error: \(error.localizedDescription)")
            }
        }
    }
    

    注意事项:

    • 将来自developer.apple.com SimplePingSimplePing.hSimplePing.m 文件添加到您的Xcode 项目中
    • 让 Xcode 创建一个桥接头并添加行 #include "SimplePing.h"

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-03
      • 2012-03-27
      • 2022-08-06
      • 2010-10-20
      • 1970-01-01
      • 2011-12-28
      • 2018-09-19
      • 1970-01-01
      相关资源
      最近更新 更多