2013-03-18 21 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