2013-02-27 80 views
1

客户端报告了一个我无法理解的错误。基于TCP的客户端连接到从其接收数据的服务器,很少发送任何内容。通常一切正常,但一旦在一个蓝色的月亮像这样的情况发生:tcp套接字读取调用如何不能返回

  • 服务器发送一些数据
  • 客户端接收数据
  • 客户端处理数据
  • ...和同时服务器发送更多的数据
  • 客户端处理完
  • 客户端尝试读取插槽
  • 客户永远挂着数据在处理
  • 服务器之后的第一个read()语句关闭连接
  • 客户仍然挂起

这里是如何的TCP连接建立(删除所有日志,返回支票等)

ret = inet_pton(AF_INET, conn->address, &addr.sin_addr); 
addr.sin_port  = htons(conn->port); /* Server port */ 
addr.sin_family  = AF_INET; 
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
connect(sock, (struct sockaddr *) &addr, sizeof(addr)); 

这里是读包装:

int32_t _readn (int fd, uint8_t *vptr, int32_t n) 
{ 
    int32_t nleft; 
    int32_t nread; 
    uint8_t*  ptr; 

    ptr = vptr; 
    nleft = n; 
    while (nleft > 0) { 
    if ((nread = read (fd, ptr, nleft)) < 0) { 
     if (errno == EINTR) { 
     nread = 0; 
     } else { 
     return E_NETWORK_ERROR; 
     } 
    } else if (nread == 0) { 
     break; 
    } 
    nleft -= nread; 
    ptr += nread; 
    } 
    return (n-nleft); 
} 

是否有可能即使在连接关闭之后,读取调用永远都会被阻止?

在我的包装中有没有一些棘手的错误,我没有注意到可能会导致这种情况?我应该为连接上的套接字设置一些标志吗?

+1

有,为什么不使用它无阻塞,把一个理由像fd上的'select'而不是? – Jite 2013-02-27 13:05:47

+0

不,没有。这样做可能是一个解决方案。还有其他一些方法我可以想到这将是一个很好的解决方法。但我想知道问题的根源。 – Dariusz 2013-02-27 13:20:50

回答

4

问题的根源在于是否没有数据读取将会阻止读取。例如。如果少于预期的n个字节写入。这被称为阻止读取。

要发现是否有数据,请使用select,因为Jite说。

最后,您可能会在防火墙上删除实时连接。一些防火墙被配置为切断已经打开超过给定时间的连接,例如, 30分钟。然而,这可能不是你所拥有的。

+0

没有数据=块不是源 - 这是结果。来源仍未被发现。 – Dariusz 2013-02-27 14:41:09

+0

@DariuszWawer你从哪里得到'n'?你怎么知道预计有多少字节? – Ben 2013-02-27 14:51:18

+0

应用层协议提供该信息。我首先阅读了3个常用的所有消息头字节,并据此决定要读取多少数据。 – Dariusz 2013-02-27 14:55:02

2

我结束了使用基于select的函数来检查数据是否可用。

而神秘的数据丢失背后的原因仍然是未知的(无服务器错误convirmed),这似乎这样的伎俩:

int32_t isReadDataAvailableOnSocket (int sock, uint32_t waitTimeUs) 
{ 
    fd_set fds; 
    int16_t ret = 0; 
    struct timeval timeout; 
    struct timeval* timeoutPtr = NULL; 

    if (waitTimeUs>0) { 
    timeout.tv_sec = waitTimeUs/1000000; 
    timeout.tv_usec = waitTimeUs % 1000000; 
    timeoutPtr = &timeout; 
    } 

    FD_ZERO (&fds); 
    FD_SET (sock, &fds); 

    ret = select (sock+1, &fds, NULL, NULL, timeoutPtr); 
    if (ret == -1) { 
    WARN("select failed for udp socket=[%d]", sock); 
    return E_NETWORK_ERROR; 
    } 
    if (! FD_ISSET(sock, &fds)) 
    { 
    return E_NO_DATA; 
    } 
    else 
    { 
    return 0; 
    } 
} 
+1

网络丢失数据。 TCP使用“一遍又一遍地喊它直到它到来”的方式来对此进行反击,但是如果网络非常不幸,那可能需要无限的时间才能成功... – 2013-03-19 14:32:01

+0

@DonalFellows它不是无限的。尝试将最终超时并失败。 – EJP 2017-02-02 18:16:36

+0

发送端将失败。接收方将继续等待。 – 2018-01-18 19:10:29