2013-03-18 24 views
0

在我的應用程序的iOS(DLNA媒體播放器),我看到一個掛不明白...我希望有人可以闡明它。ios多線程套接字libupnp掛在發送()

我的應用程序構建在Objective C中,它位於C++庫的頂部,其中一部分是libupnp。在查看下面的代碼時,編譯標誌SO_NOSIGPIPE被設置爲記錄。

從廣義上講,該應用程序運行良好,至少在運行iOS 6的iPod和iPad上運行得很好。它可以運行所有媒體播放器。

編輯:我錯了iPhone 4上的操作系統,我認爲它是6.x,但它是5.1.1,它的價值。

當我加強並開始在iPhone 4(iOS 5.1.1)和iPhone 5(iOS 6)上測試我的應用程序時,問題就發生了......我的代碼中有一個計時問題。

用戶選擇要在遠程數字媒體接收器(DMR)上播放/顯示的媒體項目。

我的代碼調用libupnp,創建soap命令來實現此目的。然後調用http_RequestAndResponse(),它創建套接字,connect()到主機,並調用http_SendMessage調用sock_read_write(稍後將在消息中包含此函數)以發送我構建的請求(POST命令在DMR上播放媒體)。然後,使用相同的套接字,調用http_RecvMessage(它再次調用sock_read_write()來接收字節)。此時,它被稱爲select(),等待DMR對播放命令作出響應。

在另一個線程上,libupnp的Web服務器獲取我們剛剛說過的媒體文件的位的請求。因此,在另一個線程中,我使用字節調用http_SendMessage來響應請求,該請求調用sock_read_write()將字節寫入客戶端。

sock_read_write中的send()掛起。它不僅會掛起libupnp,而且意味着在任何線程上的套接字上都沒有更多的通信。

這些掛起的套接字似乎不會超時,死亡或以其他方式終止。當然,這是一個我正在構建的DLNA媒體播放器,大部分有關世界狀態的命令和報告都是通過這些套接字進行的,所以我的應用程序有效地變成了殭屍:它響應了鼠標點擊以及什麼不是,但是你不能做任何有意義的事情。

我試過讓send()非阻塞。我試過調用fcntrl(sock,F_SETFL,O_NONBLOCK)來將它設置爲非阻塞,並在調用send()之前返回,如果因任何原因失敗。

我試過在send()上發送()像MSG_NOWAIT(它對iOS沒有任何影響)的標誌。

這似乎是一個計時問題。在iPad和iPod上,我可以播放音樂,直到母牛回家。在iPhone 4和iPhone 5上,我掛了。

有什麼建議嗎? (如果你告訴我哪些具體回答了這個問題,建議RTFM,閱讀手冊頁,閱讀書籍等都被高興地接受......)

哦,sock_read_write()(來自libupnp 1.6.18)的代碼:

/*! 
* \brief Receives or sends data. Also returns the time taken to receive or 
* send data. 
* 
* \return 
* \li \c numBytes - On Success, no of bytes received or sent or 
* \li \c UPNP_E_TIMEDOUT - Timeout 
* \li \c UPNP_E_SOCKET_ERROR - Error on socket calls 
*/ 
static int sock_read_write(
    /*! [in] Socket Information Object. */ 
    SOCKINFO *info, 
    /*! [out] Buffer to get data to or send data from. */ 
    char *buffer, 
    /*! [in] Size of the buffer. */ 
    size_t bufsize, 
    /*! [in] timeout value. */ 
    int *timeoutSecs, 
    /*! [in] Boolean value specifying read or write option. */ 
    int bRead) 
{ 
    int retCode; 
    fd_set readSet; 
    fd_set writeSet; 
    struct timeval timeout; 
    long numBytes; 
    time_t start_time = time(NULL); 
    SOCKET sockfd = info->socket; 
    long bytes_sent = 0; 
    size_t byte_left = (size_t)0; 
    ssize_t num_written; 

    if (*timeoutSecs < 0) 
     return UPNP_E_TIMEDOUT; 
    FD_ZERO(&readSet); 
    FD_ZERO(&writeSet); 
    if (bRead) 
     FD_SET(sockfd, &readSet); 
    else 
     FD_SET(sockfd, &writeSet); 
    timeout.tv_sec = *timeoutSecs; 
    timeout.tv_usec = 0; 
    while (TRUE) { 
     if (*timeoutSecs == 0) 
      retCode = select(sockfd + 1, &readSet, &writeSet, 
       NULL, NULL); 
     else 
      retCode = select(sockfd + 1, &readSet, &writeSet, 
       NULL, &timeout); 
     if (retCode == 0) 
      return UPNP_E_TIMEDOUT; 
     if (retCode == -1) { 
      if (errno == EINTR) 
       continue; 
      return UPNP_E_SOCKET_ERROR; 
     } else 
      /* read or write. */ 
      break; 
    } 
#ifdef SO_NOSIGPIPE 
    { 
     int old; 
     int set = 1; 
     socklen_t olen = sizeof(old); 
     getsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &old, &olen); 
     setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(set)); 
#endif 
     if (bRead) { 
      /* read data. */ 
      numBytes = (long)recv(sockfd, buffer, bufsize, MSG_NOSIGNAL); 
     } else { 
      byte_left = bufsize; 
      bytes_sent = 0; 
      while (byte_left != (size_t)0) { 
       /* write data. */ 
       num_written = send(sockfd, 
        buffer + bytes_sent, byte_left, 
        MSG_DONTROUTE | MSG_NOSIGNAL); 
       if (num_written == -1) { 
#ifdef SO_NOSIGPIPE 
        setsockopt(sockfd, SOL_SOCKET, 
         SO_NOSIGPIPE, &old, olen); 
#endif 
        return (int)num_written; 
       } 
       byte_left -= (size_t)num_written; 
       bytes_sent += num_written; 
      } 
      numBytes = bytes_sent; 
     } 
#ifdef SO_NOSIGPIPE 
     setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &old, olen); 
    } 
#endif 
    if (numBytes < 0) 
     return UPNP_E_SOCKET_ERROR; 
    /* subtract time used for reading/writing. */ 
    if (*timeoutSecs != 0) 
     *timeoutSecs -= (int)(time(NULL) - start_time); 

    return (int)numBytes; 
} 

謝謝!

-Ken

回答

1

那麼,現在是不是這個有趣......

有兩件事情錯我的代碼:

1)有人改變了配置文件,可方便地除去編譯中的-DSO_NOSIGPIPE。總是值得檢查細節。

2)似乎libupnp中的sock_read_write()存在一個錯誤。

如果定義了-DSO_NOSIGPIPE,則每次嘗試發送或接收時,都會進行選擇,只有/ then /是應用於套接字的SO_NOSIGPIPE選項。操作完成後,套接字的原始狀態將再次設置。

當我第一次測試-DSO_NOSIGPIPE時,我有時候還會得到SIGPIPE。我最終圍繞它通過了也做這樣的事情在我的main.m:

void sighandler(int signum) 
{ 
    NSLog(@"Caught signal %d",signum); 
} 

int main(int argc, char *argv[]) 
{ 
    signal(SIGPIPE,sighandler); 

    ... 

的尖銳機智高於我所想「白癡,你正在處理一些SIGPIPEs一個地方,還有一些別的地方!」

原來的select()語句也可以返回SIGPIPE。

我刪除了上面的sighandler,然後移動了「#ifdef SO_NOSIGPIPE」部分,其中SO_NOSIGPIPE屬性應用於select之上,問題完全消失。

如果select()由於EPIPE而失敗,則select()返回-1,它會在接下來的幾行中被捕獲,並且該函數將退出UPNP_E_SOCKET_ERROR,o它可以正確處理,而不是簡單地被忽略。

這是完全可能的,我完全誤解了這裏發生了什麼,在這種情況下,我絕對期待着接受教育。

但是,我確實再次享受可靠的網絡通信。

希望這可以幫助別人。

-Ken