0

我的网络中有很多交换机,我尝试检查它们是否在线。我用一个小的 UDP 数据包来做这个,他们用他们自己的 UDP 数据包来响应,告诉我他们在那里。实际上它只是一个开关模拟20000进行测试,但这并不重要。

因为我不喜欢在不需要的情况下工作低级别,所以我使用https://github.com/robbiehanson/CocoaAsyncSocket来处理 UDP 的事情。

它工作......几乎。当我重复 ping 例如 10 个开关时(我同时 ping 它们,然后等待所有响应),它似乎在第一个周期工作,但在几个周期后它失控了,几乎所有似乎都没有响应. 哪个是哪个不是完全随机的。当我添加更多开关时它会变得更糟(这意味着它从第一个周期开始不起作用)。使用循环我:我为所有开关发送 ping,然后等待所有答案(或超时),然后再次发送 ping。

当我使用数据包嗅探器检查我的网络流量时,我看到对于“错误”的交换机(意味着它们显示为离线 == 超时,但实际上是在线的),每个交换机都有三种可能的情况:

  1. 来自 iPhone 的数据包永远不会发送,因此没有收到任何答复。
  2. iPhone 发送数据包,但交换机没有响应。
  3. 收到了应答包,但 iPhone 没有读取它。

这是完全随机的,它们在一个循环中混合。

当所有交换机实际上都在线时,问题被最小化(需要更多周期才能出现)但仍然存在。

由于代码是异步的,因此很有可能出现错误,我认为肯定存在某种竞争条件或其他东西。

我将在这里发布我的 ping 课程。这不是很复杂。我盯着这个看了几个小时,但找不到错误。

用法如下:您创建一个实例:

- (id)initWithTimeout:(NSTimeInterval)timeout delegate:(id<NMASwitchPingerDelegate>)delegate;

比您只需为要 ping 的每个 ipAddress 调用以下方法。该方法立即返回。这意味着 ping 是同时处理的:

- (void)sendPingToAddress:(NSString*)address;

为方便起见,我将其添加到粘贴箱中:http: //pastebin.com/0LuiXsXY

#import "NMASwitchPinger.h"

/**
 *  Private interface.
 */
@interface NMASwitchPinger () {
    /**
     *  The delegate of this class.
     */
    __weak id<NMASwitchPingerDelegate> delegate;
    /**
     *  The timeout after which the pinger stops waiting for a response.
     */
    NSTimeInterval timeout;
    /**
     *  The socket which is used to send the ping.
     */
    GCDAsyncUdpSocket *socket;
    /**
     *  List of pings which are awaiting a response.
     */
    NSMutableDictionary *pendingPings;
    /**
     *  Dispatch queue which serializes access to the pendingPings dictionary.
     */
    dispatch_queue_t pendingPingsAccessQueue;
    /**
     *  The queue on which the delegate methods of the socket are executed.
     */
    dispatch_queue_t socketDelegateQueue;
    /**
     *  Is set to true when the SwitchPinger started receiving responses (after first send)
     */
    bool receiving;
}
@end


@implementation NMASwitchPinger

#pragma mark - Initialization

- (id)initWithTimeout:(NSTimeInterval)newTimeout delegate:(id<NMASwitchPingerDelegate>)newDelegate {
    self = [super init];
    if (self) {
        // setting passed values
        timeout = newTimeout;
        delegate = newDelegate;

        // init data structures
        pendingPings = [[NSMutableDictionary alloc] init];
        pendingPingsAccessQueue = dispatch_queue_create("de.nexans-ans.pingerPendingAccess", DISPATCH_QUEUE_SERIAL);

        // create the socket for udp sending
        socketDelegateQueue = dispatch_queue_create("de.nexans-ans.pingerDelegate", DISPATCH_QUEUE_CONCURRENT);
        socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:socketDelegateQueue];
    }
    return self;
}

- (id)init {
    NSAssert(NO, @"Use the designated initializer");
    return nil;
}

#pragma mark - Sending a ping

- (void)sendPingToAddress:(NSString *)address {
    // we allow only one ping at a time to the same ip
    __block BOOL alreadyInList = NO;
    dispatch_sync(pendingPingsAccessQueue, ^{
        if (pendingPings[address]) {
            alreadyInList = YES;
        } else {
            pendingPings[address] = [[NSDate alloc] init];
        }
    });

    // don't send a second ping to the same address
    if (alreadyInList) {
        NSLog(@"SimplePinger: did not send ping because already a ping pending to this addres: %@", address);
        return;
    }

    // create a minimal packet (3 bytes)
    NSMutableData *packet = [[NSMutableData alloc] initWithCapacity:3];
    uint16_t vendor_value = CFSwapInt16HostToBig(266);
    uint8_t request_type = 1;
    [packet appendBytes:&vendor_value length:sizeof(vendor_value)];
    [packet appendBytes:&request_type length:sizeof(request_type)];

    // send over the wire
    [socket sendData:packet toHost:address port:50266 withTimeout:timeout tag:0];

    // schedule timeout handler
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC));
    dispatch_after(popTime, pendingPingsAccessQueue, ^(void){
        [self removeTimedOutPingWithAddress:address];
    });

    // start receiving when not already receiving
    if (!receiving) {
        bool recvGood = [socket beginReceiving:nil];
        NSAssert(recvGood, @"SimplePinger: could not start receiving");
        receiving = YES;
    }
}


#pragma mark - GCDAsyncSocket delegate

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext {
    NSString *ipAddress = [GCDAsyncUdpSocket hostFromAddress:address];

    __block BOOL pingStillPending = NO;
    dispatch_sync(pendingPingsAccessQueue, ^{
        NSDate *sendDate = pendingPings[ipAddress];
        if (sendDate) {
            [pendingPings removeObjectForKey:ipAddress];
            pingStillPending = YES;
        }
    });

    if (pingStillPending) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [delegate switchPinger:self didReceiveResponse:data fromAddress:ipAddress];
        });
    }
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error {
    NSLog(@"didnt send data");
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
    NSLog(@"did send");
}

#pragma mark - Private methods

/**
 *  Removes a timed out ping. A call of this function gets scheduled when a ping is send.
 *
 *  @param address The address of the ping which should be removed.
 */
- (void)removeTimedOutPingWithAddress:(NSString*)address {
    NSDate *sendDate = pendingPings[address];

    if (sendDate) {
        NSLog(@"timeout: %@", address);
        NSAssert(fabs([sendDate timeIntervalSinceNow]) >= timeout, @"SimplePing: removed ping before timout");
        [pendingPings removeObjectForKey:address];
        dispatch_async(dispatch_get_main_queue(), ^{
            [delegate switchPinger:self didReceiveTimeoutFromAddress:address];
        });
    }
}

@end
4

0 回答 0