C 是否有更干净的方法可靠地使用write()函数?

C 是否有更干净的方法可靠地使用write()函数?,c,linux,sockets,gcc,C,Linux,Sockets,Gcc,我阅读了man页面,我的理解是,如果write()失败并将errno设置为EAGAIN或EINTR,我可能会再次执行write(),因此我产生了以下代码: ret = 0; while(ret != count) { write_count = write(connFD, (char *)buf + ret, count); while (write_count < 0) { switch(errno) { case EINTR:

我阅读了
man
页面,我的理解是,如果
write()
失败并将
errno
设置为
EAGAIN
EINTR
,我可能会再次执行
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;
  }
ret=0;
while(ret!=计数){
写入计数=写入(connFD,(char*)buf+ret,计数);
while(写入计数<0){
开关(错误号){
案例中心:
案例伊根:
写入计数=写入(connFD,(char*)buf+ret,count-ret);
打破
违约:
printf(“\n ret的值为:%d\n”,ret);
printf(“\n错误号为:%d\n”,errno);
断言(0);
}
}
ret+=写入计数;
}
我正在套接字上执行
read()
write()
,并像上面一样处理
read()
。我使用的是带有
gcc
编译器的Linux。

这里有一个“不要重复自己”的问题-不需要两个单独的
编写调用,也不需要两个嵌套循环

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

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
for(int n=0;n
EINTR
EAGAIN
处理通常应该略有不同
EAGAIN
始终是表示套接字缓冲区状态的某种暂时性错误(或者更准确地说,可能是您的操作可能会阻塞的)

一旦你点击了一个
EAGAIN
,你可能会想睡一会儿或者将控制返回到一个事件循环(假设你正在使用一个)

使用
EINTR
时,情况有所不同。如果您的应用程序不停地接收信号,那么这可能是您的应用程序或环境中的一个问题,因此我倾向于使用某种内部
eintr\u max
计数器,因此我不会陷入理论状态,继续无限循环
eintr


Alnitak的答案(对于大多数情况来说足够了)也应该是将
errno
保存在某个地方,因为它可能会被
perror()
(尽管为了简洁起见可能会被省略)。

是的,有更干净的方法来使用
write()
:将
文件*
作为参数的写函数类。最重要的是,
fprintf()
fwrite()
。在内部,这些库函数使用
write()

如果您只有一个文件描述符,则始终可以通过
fdopen()
将其包装成
文件*
,以便将其与上述函数一起使用


然而,有一个陷阱:
文件*
流通常是缓冲的。如果您正在与其他程序通信并等待其响应,则可能会出现问题。这可能会使两个程序都死锁,即使没有逻辑错误,这仅仅是因为
fprintf()
决定将相应的
write()
延迟一点。您可以在实际需要执行
write()
调用时关闭缓冲或
fflush()
输出流。

EAGAIN
的情况下,我更喜欢
poll
描述符,而不是无缘无故地忙着循环和烧掉CPU。这是一种非阻塞
写入的“阻塞包装器”,我使用:

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;
ssize\u t write=0;
while(写入如果(轮询和pfd,1,-1)检查EAGAIN表示您使用的是非阻塞套接字。我可能会将其抽象为一个单独的函数,该函数提供写入量以及是否失败的统计信息。@我使用的是阻塞套接字。我不知道只有在使用非阻塞套接字时才需要检查EAGAIN。这就是我提到的原因为了以防万一,我只检查了非阻塞套接字上的EAGAIN,因为这就是我理解它工作的方式。如果有人知道它发生在阻塞套接字上,请大声说出来。@Ioan您是正确的。它不会发生在阻塞套接字上。您可以使用select()调用,当套接字准备好写入时会立即通知您。好吧,我看这更优雅。什么是“不要重复自己的问题”?@AnkurVj它不必要地重复相同的代码-DRY(不要重复自己)这是本书中采用的一个原则。它意味着避免两次编写相同的代码。在这里,只要一次就足够了,您就可以编写两次
write
语句。在
EAGAIN/ewoldblock的情况下,只烧掉CPU是不合适的。
此状态的解决方案取决于对等方读取的内容。它不会在i之后消失t正确的程序是
select()
直到套接字变为可写。我同意@EJP。如果启用了
SA_RESTART
标志,就不会有
EINTR
,如果使用多路复用器,就知道什么时候适合读取。便携式应用程序应该检查
EAGAIN
ewoodblock
,如
man
页面中所述:“POSIX.1-2001允许在这种情况下返回任何一个错误,并且不要求这些常量具有相同的值,因此便携式应用程序应该检查这两种可能性。“
EAGAIN
不一定意味着对
O_NONBLOCK
文件描述符的阻塞调用,它依赖于实现。@Barracuda您最后的