当我打开O_NONBLOCK时,我得到“0”和“I / O错误”

时间:2013-02-11 21:22:43

标签: c linux tcp redhat

当我打开O_NONBLOCK时,我的连接len返回0而我的errno返回EIO。我期望len为-1而errno为EAGAIN。根据我得到的结果,我可以假设初始连接存在问题。我不明白的是为什么我得到它。如果我评论我打开O_NONBLOCK的位置,我根本没有这个问题。我是否遗漏了关于启用O_NONBLCOK的内容?

我的代码(主要())

long len;

//Var for O_NONBLOCKING
int flags;

printf("R thread - starting\n");

MainTcpipIndex = TcpipIndex = 0;
while ( fMainShutdown == FALSE )
  {

  s_MuxWait(5000, "", &hevTcpipBuffAvail[MainTcpipIndex], 0);
  if ( hevTcpipBuffAvail[MainTcpipIndex] == 0)
     continue;

  len = s_recv( lSocket, TcpipRecord[MainTcpipIndex].TcpipBuffer,
              sizeof(TcpipRecord[MainTcpipIndex].TcpipBuffer));

    //initialize flags var      
  flags = fcntl(lSocket, F_GETFL, 0);

  //turns on O_NONBLOCKING
  fcntl(lSocket, F_SETFL, flags | O_NONBLOCK);

  if (len == -1  ||  len == 0 && errno != EAGAIN)  //0 = connection broken by host
     {
         ReceiveError = errno;
         hevReceiveError = 1;
         printf("R_main - set hevReceiveError ON\n");
         pthread_exit(NULL);
     }
 //catches O_NONBLOCK
 if (len == -1 && errno == EAGAIN)
     {
         LogMessage(&LogStruct, INFO, LOGQUEUE + LOGSCREEN, "Caught the
                     O_NONBLOCKING\n");
         len = 0;
         continue;
     }

  // Record Length
  TcpipRecord[MainTcpipIndex].ulTcpipBufLen = len;

  // Tell T Thread we have a message
  hevTcpipBuffUsed[MainTcpipIndex] = 1;
  hevTcpipBuffAvail[MainTcpipIndex] = 0;

  // Maintain Index
  if ( ++MainTcpipIndex >= MAX_TCPIP_BUFFS )
     MainTcpipIndex = 0;

  }//end while

修改

也许我第一次没有说清楚,我明白errno和len与s_recv有关 我的问题是,当我打开O_NONBLOCK时,我得到了不希望的结果; s_recv的错误是EIO及其 len是0.如果我关闭O_NONBLOCK那么我的所有问题都会消失; len是1或更多而errno不是 需要检查。

以下是我期望的场景示例:

在第一个错误情况s_recv中,Len为0或-1,errno不是EAGAIN,连接已重置。

在第二个不好的情况s_recv中,Len是-1而errno是EAGAIN。这是什么时候的预期场景 O_NONBLOCK基于手册页打开。

在好的sceanrio s_recv中,len超过1并且没有必要检查errno。

2 个答案:

答案 0 :(得分:1)

下一次阅读之前,对O_NONBLOCK的更改无关紧要。

因此,在致电len之前,您必须先检查errnofcntl 。当然,您必须检查fcntl的返回值,因为它也可能会失败。

总结一下,len == 0errno == EIOO_NONBLOCK的更改无关,而是与s_recv之前的更改无关。

此外,请注意您的

if (len == -1 || len == 0 && errno != EAGAIN)

相同
if (len == -1 || (len == 0 && errno != EAGAIN) )

这没有意义,因为如果返回值 -1,则必须忽略errno。如果返回值为0,则对等方已关闭连接。

<强>更新

我已经构建了一个简单的echo客户端

fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
    error("socket");

he = gethostbyname("localhost");
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ((struct in_addr*)he->h_addr)->s_addr;
addr.sin_port = htons(7); /* echo */
if (connect(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1)
    error("connect");

while (fgets(buf, sizeof(buf), stdin)) {
    n = strlen(buf);
    printf("fgets=%d, %s", n, buf);
    if (send(fd, buf, n, 0) == -1)
        error("send");

    n = recv(fd, buf, sizeof(buf), 0);
    if (n == -1)
        error("recv");

    printf("recv=%d, %s", n, buf);

    flags = fcntl(fd, F_GETFL, 0);
    printf("flags=%d\n", flags);
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
        error("fcntl");
}

在设置O_NONBLOCK后,我收到了

  

错误号= 11
  recv:资源暂时不可用

如果我在循环之前移动fcntl(O_NONBLOCK),一切正常。

因此,在读取或写入套接字后,似乎不建议使用非阻塞。

答案 1 :(得分:0)

问题在于:

if (len == -1  ||  len == 0 && errno != EAGAIN)

这相当于

if (len == -1  ||  (len == 0 && errno != EAGAIN))

因此,如果没有可用的数据(len == -1),您将无法检查errno,并且无条件地标记错误并退出。此外,当有一个EOF(len == 0)时,errno不会被设置,所以检查它会给出之前通话中发生的任何事情。

你可能想要:

if ((len < 0 && errnor != EGAIN) || len == 0) {
    ReceiveError = (len < 0) ? errno : 0;

此外,如果fcntl出现错误,那将会破坏错误(虽然我预计不会发生这种情况 - 您可能会看到GETFL / SETFL唯一的失败是EBADF。)您可能想要在recv调用之前执行fcntl调用。