2013-05-10 77 views
1

我在使用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功能似乎某些時間隨機量正常工作(和發送郵件的隨機數/接收)。我想知道幾件事情:

  1. 從第二個屏幕發送UDP消息有效嗎?我認爲是這樣,並且發送永遠不會失敗 - 即使接收失敗,它也會繼續工作。

  2. didReceiveData如何被調用,怎麼會被打破?如果我在Linux或RTOS中,我可能會創建一個等待數據包的顯式線程; GCD框架如何決定數據包應該放在哪裏?

  3. 爲什麼我的應用程序突然停止在端口上收聽?我如何檢測/調試?

  4. GCDAsyncUdpSocket對象是否由主屏幕擁有,與Delegate.m模塊相反,這有什麼關係?

  5. 是否適合使用主調度隊列,因爲我認爲我在做什麼?的確,我是否正確地做到了這一點?

我完全不知所措,所以當然,任何建議都會大受歡迎!無需回答所有問題 - 特別是如果您的答案是解決方案!

謝謝!

+0

使用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

+0

我在使用VVOSC庫的應用程序中遇到了類似的問題,在不一致的時間段(分鐘或小時)之後,它將停止在其「OSCInport」類(從udp套接字讀取)上接收數據,並且只能「重置「iPad。在傳入的UDP套接字停止工作之前,我看到了類似的ARP活動模式。 – 2014-03-02 15:17:40

回答

0

聽起來好像接收UDP套接字正在關閉或轉移到不同的地址/端口對。

  • 如果被關閉,傳出數據包仍然可以工作的唯一方法是,如果實際上有兩個套接字。也許接收端口綁定到端口12349,發送端口綁定到端口0.然後GCD在一段時間後關閉接收端口。鑑於您對ARP的後續評論,這似乎不大可能,但值得記住。

  • ARP活動表明iPad的IP地址可能正在改變。如果它改變了,或者如果它從WiFi接口切換到蜂窩接口,那麼它發送的數據包仍然會通過,但發送到它的數據包將完全按照所描述的失敗。

無論接收到那些心跳消息,請檢查recvfrom地址並確保它發回的任何消息都轉到該確切地址。當然,請確保注意endian(主機vs網絡字節順序)。

我假設兩臺設備之間沒有防火牆或NAT。如果有這樣的事情,那麼就會有一個完全不同的可能性世界。

3

這篇文章結束了我在POSIX/GCDAsyncSocket/NSStream/NSNetService衆神手中收到的大約八個小時的折磨。對於任何人遇到這種情況:我的GCDAsyncSocket連接通過對等/遠程對等方斷開連接錯誤重置的原因僅僅是我通過局域網上的IP連接,而不是使用主機名。 (即使用connectToAddress系列方法而不是connectToHost系列方法)。我啓動了WireShark,下載了X11,全部是爵士樂,並確認我面臨着Bob正在看到的同樣的問題 - 在斷開時間周圍的ARP活動。嘗試連接到主機而不是地址確認Seth的處理情況 - 這是路由器重新分配IP地址的問題。

這對於SO來說不是一個更無害的問題 - 0個upvotes的問題和答案,但是你們兩個人結合起來給予更多的信息來解決我認爲是棘手的問題。非常感謝!

相關問題