我正在将 C 程序写入将固定大小的文件(从服务器传输到客户端稍微超过2Mb)。我在 Linux 上使用 TCP套接字,我写的代码如下:
服务器(发件人)
while (1) {
int nread = read(file, buffer, bufsize);
if (nread == 0) // EOF
break;
if (nread < 0) {
// handle errors
}
char* partial = buffer;
while (nread > 0) {
int nwrite = write(socket, partial, nread);
if (nwrite <= 0) {
// handle errors
}
nread -= nwrite;
partial += nwrite;
}
}
// file sent
shutdown(socket, SHUT_WR);
客户(接收方)
while (filesize > 0) {
nread = read(socket, buffer, bufsize);
if (nread == 0) {
// EOF - if we reach this point filesize is still > 0
// so the transfer was incomplete
break;
}
if (nread < 0) {
// handle errors
}
char* partial = buffer;
while (nread > 0) {
nwrite = write(file, partial, nread);
if (nwrite <= 0) {
// handle errors
}
nread -= nwrite;
partial += nwrite;
filesize -= nwrite;
}
}
if (filesize > 0) {
// incomplete transfer
// handle error
}
close(socket);
在我的笔记本电脑上测试代码时(客户端和服务器&#34;在本地主机上#34;并且通信发生在环回接口上),有时客户端会退出,因为read
收到 EOF ,而不是因为它收到了所有filesize
个字节。由于我在服务器上使用shutdown
,这意味着没有其他数据可供阅读。
(请注意,服务器发送了所有字节并正确执行了shutdown
)
你能解释一下为什么会这样吗?
丢失的字节在哪里?
-----
编辑1 - 澄清
有些用户提出了一些澄清,所以我在这里发布答案:
filesize
是一个固定值,在客户端和服务器中都是硬编码的。shutdown
。编辑2
用户 Cornstalks pointed me一个非常有趣的article关于TCP的非常可靠的行为。
这篇文章非常值得一读,描述了在TCP套接字之间发送未知数据量时的一些技巧。描述的技巧如下:
close(2)
或shutdown(2)
后打开套接字,直到所有数据都已成功发送。shutdown(2)
向发件人发送数据的接收者发出信号。阅读完文章后,我升级了我的代码以实现1号和5号技巧。
这就是我实现5号技巧的方法:
服务器(发件人)
// sending loop ...
// file sent
shutdown(socket, SHUT_WR);
// wait acknowledgement from the client
ack = read(socket, buffer, bufsize);
if (ack < 0) {
// handle errors
}
客户(接收方)
// receiving loop..
if (filesize > 0) {
// incomplete transfer
// handle error
}
// send acknowledgement to the server
// this will send a FIN and trigger a read = 0 on the server
shutdown(socket, SHUT_WR);
close(socket);
2号,3号和4号技巧怎么样?
不需要Trick number 2 ,因为只要服务器接受连接,应用程序就会继续进行文件传输。没有额外的消息被交换。
特技3 已经实施
特技4号也已实施。如前所述,文件大小是硬编码的,因此无需更换文件。
这是否解决了我原来的问题?
否我的问题没有解决。错误仍在发生,截至今天,只有在localhost上使用客户端和服务器测试应用程序时才会发生错误。
你怎么看?有没有办法阻止这种情况?
答案 0 :(得分:0)
您&#39;重:
read
填充缓冲区,即使write()
没有写完整个缓冲区的情况下进行了很好的辩护。您需要执行(1),并且您不需要执行(2)因为您处于阻止模式且POSIX确保write()
不会返回所有数据是写的。
两个循环的简单版本:
while ((nread = read(inFD, buffer, 0, sizeof buffer)) > 0)
{
write(outFD, buffer, 0, nread);
}
if (nread == -1)
; // error
更正确的版本会检查write()
的结果当然是错误。