我的网络中有很多交换机,我尝试检查它们是否在线。我用一个小的UDP数据包做到这一点,他们用自己的UDP数据包回复它,告诉我它们在那里。事实上,只有一个开关模拟200个用于测试,但这并不重要。
因为我不喜欢在低级别工作,所以我不必使用https://github.com/robbiehanson/CocoaAsyncSocket作为UDP的东西。
它几乎可以工作。当我重复ping例如10个开关(我同时ping它们然后等待所有响应)时,它似乎适用于第一个循环,但之后几个失控,几乎所有似乎都没有响应。它是完全随机的,哪些不是。当我添加更多开关时它会变得更糟(意味着它从第一个循环起不起作用)。有了循环,我会说:我发送所有开关的ping然后等待所有的答案(或超时),然后再发送ping。
当我使用数据包嗅探器检查网络流量时,我发现对于“错误”的交换机(意味着它们显示为脱机= =超时,但实际上是在线),每个交换机有三种可能的情况:
它是完全随机的,它们在一个循环中混合。
当所有开关实际上都在线时,问题被最小化(需要更多周期出现),但仍然存在。
由于代码是异步的,因此很有可能存在错误,我认为必定存在某种竞争条件。
我会在这里发布我的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