我在使用GCDAsyncUdpSocket時遇到問題。我將iPad用作與其他應用程序交互的用戶界面應用程序 - 將其稱爲主機,後者在單獨的Windows計算機上運行。兩臺機器都在自己的專用網絡上,因此它們位於自己的子網上。在某些情況下,主機向iPad發送UDP數據包以指示它向用戶顯示哪個屏幕,並且iPad通過UDP數據包向用戶發送用戶響應。最後,iPad定期(以2赫茲)向主機發送簡單的「心跳」消息。使用GCDAsyncUdpSocket在iOS6上發生「對等連接重置」錯誤
這一切工作正常 - 一段時間。然後,顯然,iPad突然停止接受來自主機的UDP數據包 - 後者遇到「連接重置對等」錯誤,而它(iPad)仍在成功發送,而主機接收心跳消息。
我在想這個問題來自我對大中央調度(GCD)如何工作的困惑。我的iPad應用程序非常簡單;我將其基於iOS編程教程(我在這裏是初學者,但在Windows,Linux,嵌入式/實時和網絡方面非常有經驗)。它基本上由一個主屏幕組成,它不時創建第二個屏幕。因此,基本結構是這樣的:
- 的main.m
- Delegate.m
- MainViewController.m
- PopupViewController.m
的main.m文件和Delegate.m創建在教程中由Xcode自動完成,並且沒有什麼特別之處。 MainViewController.m是我的「主屏幕」,並擁有iPad應用程序使用的GCDAsyncUdpSocket。最終的文件,PopupViewController.m,是第二個屏幕,即使用這樣的:
# MainViewController.m
- (IBAction)sendResponseOne:(id)sender {
// Send a message to Host
[self sendUdpMessage:1];
// Switch to other view
PopupViewController *vc = [[PopupViewController alloc] init];
[vc setMainScreen:self]; // Used to access UDP from 2nd screen
[self presentViewController:vc animated:NO completion:nil];
}
# PopupViewController.m
- (IBAction)confirmAnswers:(id)sender
{
// Send a message to Host - calls same function as above main screen
[self->mainScr sendUdpMessage:2];
[self dismissViewControllerAnimated:NO completion:nil];
}
現在對於中小企業失敗的代碼。首先,這裏是MainViewController.m的@interface部分:
# From MainViewController.m
@interface MainViewController()
{
GCDAsyncUdpSocket *udpSocket;
}
@end
這裏是如何/在我創建UDP對象:
# From MainViewController.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
{
// Setup our socket, using the main dispatch queue
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
return self;
}
這裏就是我綁定端口:
# From MainViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Start UDP server
int port = 12349;
NSError *error = nil;
if (![udpSocket bindToPort:port error:&error])
{
NSLog(@"Error starting server (bind): %@", error);
return;
}
if (![udpSocket beginReceiving:&error])
{
[udpSocket close];
NSLog(@"Error starting server (recv): %@", error);
return;
}
[self startPingTimer];
isRunning = YES;
}
下面是接收數據包的代碼。顯然,這個函數一段時間工作正常,有時幾十次,然後意外失敗。
# From MainViewController.m
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
if (data.length == sizeof(MyMessage)) {
MyMessage msg;
[data getBytes:&msg length:sizeof(MyMessage)];
msg.magic = ntohl(msg.magic);
msg.msgId = ntohl(msg.msgId);
for (int i = 0; i < 4; ++i) {
msg.values[i] = ntohl(msg.values[i]);
}
if (msg.magic == 0xdeadcafe) {
switch (msg.msgId) {
case imiStateControl:
self->iceState = (IceState)msg.values[0];
break;
default:
break;
}
}
}
}
我很茫然,爲什麼didReceiveData功能似乎某些時間隨機量正常工作(和發送郵件的隨機數/接收)。我想知道幾件事情:
從第二個屏幕發送UDP消息有效嗎?我認爲是這樣,並且發送永遠不會失敗 - 即使接收失敗,它也會繼續工作。
didReceiveData如何被調用,怎麼會被打破?如果我在Linux或RTOS中,我可能會創建一個等待數據包的顯式線程; GCD框架如何決定數據包應該放在哪裏?
爲什麼我的應用程序突然停止在端口上收聽?我如何檢測/調試?
GCDAsyncUdpSocket對象是否由主屏幕擁有,與Delegate.m模塊相反,這有什麼關係?
是否適合使用主調度隊列,因爲我認爲我在做什麼?的確,我是否正確地做到了這一點?
我完全不知所措,所以當然,任何建議都會大受歡迎!無需回答所有問題 - 特別是如果您的答案是解決方案!
謝謝!
使用Wireshark我找到了失敗的原因。一段時間後,iPad發送了大量的ARP數據包,第一個是「誰擁有192.168.1.65?告訴0.0.0.0」,這是奇怪的,因爲iPad是192.168.1.65。然後它要求192.168.1.1(告訴192.168.1.65)和169.254.255.255;後者失敗5次,則iPad拒絕接受未來數據包 - 堆棧以「目標不可達」ICMP數據包響應外部主機。再一次,我無法訪問GCDAsyncUdpSocket可能遇到的任何錯誤,而且我完全停留在此。 – Bob 2013-07-06 21:53:39
我在使用VVOSC庫的應用程序中遇到了類似的問題,在不一致的時間段(分鐘或小時)之後,它將停止在其「OSCInport」類(從udp套接字讀取)上接收數據,並且只能「重置「iPad。在傳入的UDP套接字停止工作之前,我看到了類似的ARP活動模式。 – 2014-03-02 15:17:40