从主机获取ping延迟

时间:2014-03-18 03:28:47

标签: ios objective-c ping bandwidth latency

我正试图让主机延迟很长一段时间而且我陷入困境。已经尝试Simple Ping ,但似乎它没有返回延迟。我最接近的是当我使用TKC-PingTest用于MAC OS时。这是完美的,但仅限于iPhone模拟器,因为当使用iPhone时,我得到一个错误,因为TKC使用了补丁“/ sbin / ping”。除了这两个,我已经尝试了很多其他的东西,什么都没有。

4 个答案:

答案 0 :(得分:16)

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

<强>目标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使用主机,它将阻止您的主线程,因此在单独的线程中调用它可能很有用。

答案 1 :(得分:8)

您可以轻松扩展简单的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
}

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

答案 2 :(得分:3)

我从@ 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 SimplePing中的SimplePing.hSimplePing.m文件添加到您的Xcode项目中
  • 让Xcode创建桥接标头并添加行#include "SimplePing.h"

答案 3 :(得分:2)

Swift 3实现了hris.to的答案:

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)
    }

}