有一个更清洁的方式来可靠地使用write()函数吗?

我阅读man页,我的理解是,如果write()失败,并将errnoEAGAINEINTR ,我可以再次执行write() ,所以我想出了以下代码:

  ret = 0; while(ret != count) { write_count = write(connFD, (char *)buf + ret, count); while (write_count < 0) { switch(errno) { case EINTR: case EAGAIN: write_count = write(connFD, (char *)buf + ret, count -ret); break; default: printf("\n The value of ret is : %d\n", ret); printf("\n The error number is : %d\n", errno); ASSERT(0); } } ret += write_count; } 

我在socket上执行read()write()并像上面那样处理read() 。 我正在使用Linux,使用gcc编译器。

Solutions Collecting From Web of "有一个更清洁的方式来可靠地使用write()函数吗?"

你有一个“不要重复自己”的问题 – 不需要两个单独的调用来write ,也不需要两个嵌套的循环。

我的正常循环看起来像这样:

 for (int n = 0; n < count; ) { int ret = write(fd, (char *)buf + n, count - n); if (ret < 0) { if (errno == EINTR || errno == EAGAIN) continue; // try again perror("write"); break; } else { n += ret; } } // if (n < count) here some error occurred 

EINTREAGAIN处理通常应该略有不同。 EAGAIN总是一些表示套接字缓冲区状态的瞬态错误(或者更确切地说,您的操作可能会阻塞)。

一旦你点击一个EAGAIN你可能会想睡一下,或者把控制权交EAGAIN事件循环(假设你正在使用)。

EINTR的情况有点不同。 如果你的应用程序不停地接收信号,那么这可能是你的应用程序或环境中的一个问题,因此我倾向于拥有某种内部的eintr_max计数器,所以我不会停留在理论情况下,在EINTR循环。

Alnitak的答案(在大多数情况下是足够的)也应该在某处保存errno ,因为它可能被perror() (尽管为简洁起见可能已被省略)。

我宁愿在EAGAIN情况下poll描述符,而不是只是忙于循环和没有很好的理由烧毁CPU。 对于我使用的非阻塞式write这是一种“阻塞包装”

 ssize_t written = 0; while (written < to_write) { ssize_t result; if ((result = write(fd, buffer, to_write - written)) < 0) { if (errno == EAGAIN) { struct pollfd pfd = { .fd = fd, .events = POLLOUT }; if (poll(&pfd, 1, -1) <= 0 && errno != EAGAIN) { break; } continue; } return written ? written : result; } written += result; buffer += result; } return written; 

请注意,我实际上并没有检查除返回值以外的poll结果; 我认为如果描述符上存在永久性错误,则以下write将失败。

您可能希望将EINTR作为可重试错误加入,只需将其添加到EAGAIN的条件中,但我更喜欢它实际上中断I / O。

是的,有更清洁的方法来使用write()函数:将FILE*作为参数的写函数类。 也就是说,最重要的fprintf()fwrite() 。 在内部,这些库函数使用write()系统调用来完成他们的工作,并处理像EAGAINEINTR这样的东西。

如果你只有一个文件描述符,你总是可以通过fdopen()把它包装到FILE* ,所以你可以在上面的函数中使用它。

但是,有一个缺陷: FILE*流通常被缓冲。 如果您正在与其他程序进行通信并正在等待其响应,则可能会出现问题。 即使没有逻辑错误,这可能会使两个程序都死锁,因为fprintf()决定推迟相应的write() 。 只要您确实需要执行write()调用,就可以将缓冲关闭或fflush()输出流。