如何确保write(2)已将所有数据写入套接字/文件描述符?

时间:2015-03-13 08:04:53

标签: c sockets

我正在使用write(man 2 write)将数据写入具有已建立,阻塞且非常慢的TCP连接的套接字。我正在写大块的数据。 write返回写入的实际大小,当然发生并非所有数据都是由于(可能)超出此问题范围的原因而写入的。

为了确保我将写入调用封装在这样的小循环中:

do {
    ssize_t ret = write(client, p, count);
    if (ret <= 0)
        break;
    p += ret;
    count -= ret;
} while (count);

if (count != 0) 
    return -ENODEV;

有没有更好的方法,比如在文件描述符上设置一个标志,从而让低层处理它?<​​/ p>

2 个答案:

答案 0 :(得分:6)

我建议使用while循环而不是do {} while来为案例count == 0提供一致的行为。此外,一些失败的案例不是错误:

while (count > 0) {
    ssize_t ret = write(client, p, count);
    if (ret <= 0) {
        if (ret == 0)
            return -ENODEV;
        if (errno == EINTR)
            continue;
        else
            return -errno;
    }    
    p += ret;
    count -= ret;
}
如果在写入任何数据之前系统调用被EINTR中断,则设置

signal。在这种情况下应该重新启动write。如果client句柄设置为非阻止,您还应该处理EAGAINEWOULDBLOCK

来自wildplasser的答案的更紧凑和优雅的版本:

for (size_t done = 0; done < count; ) {
    ssize_t ret = write(client, p + done, count - done);

    if (ret == 0) return -ENODEV;
    if (ret < 0 && errno != EINTR) return -errno;
    done += ret;
}

答案 1 :(得分:1)

size_t done;
ssize_t ret;

for (done = 0; done < size; done += ret) {
    ret = write(client, buff + done, size-done);

    if (ret == 0) return -ENODEV;
    if (ret == -1 && errno == EINTR) { ret = 0; continue; }
    if (ret == -1)  return -errno;
}