通过 fifo 写入/读取数据的正确方法
作为作业,我创建了一个模拟邮箱服务器的大项目(仅通过同一台计算机上的进程,因此通过 fifo,这是一个作业)
我无法发布该项目,因为它很大(有很多文件),但我可以说有时我会丢失一些数据或者它不能保持其完整性。
我使用这些代码片段来传输数据,是不是有些错误?Network_IO 是我正在谈论的功能:
#include "Network.h"
int Network_Open(const char* path,int oflag)
{
return open(path,oflag);
}
ssize_t Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize)
{
ssize_t retsize = 0;
ssize_t tmpDataSize = (ssize_t)dataSize;
errno = 0;
if (tmpDataSize == 0) return 0;
while ((retsize = (opcode == NetworkOpCode_Write? write(fifo,data,tmpDataSize) : read(fifo,data,tmpDataSize))) != tmpDataSize)
{
if (errno != EINTR) break;
}
return retsize;
}
Boolean Network_Send(int fifo,const void* data,size_t dataSize)
{
return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize);
}
Boolean Network_Receive(int fifo,void* data,size_t dataSize)
{
return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Read,data,dataSize);
}
Boolean Network_Close(int fifo)
{
if (fifo >= 0)
return close(fifo) == 0;
}
编辑 1: 我用来实际测试的代码片段
Boolean Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize)
{
ssize_t retsize = 0;
ssize_t tmpDataSize = (ssize_t)dataSize;
ssize_t sentDataSize = 0;
errno = 0;
if (tmpDataSize == 0) return True;
while (sentDataSize < tmpDataSize)
{
switch(opcode)
{
case NetworkOpCode_Write:
retsize = write(fifo,data + sentDataSize,tmpDataSize - sentDataSize);
break;
case NetworkOpCode_Read:
retsize = read(fifo,data + sentDataSize,tmpDataSize - sentDataSize);
break;
}
if (retsize < 0)
{
if (errno != EINTR) return False;
else
{
errno = 0;
continue;
}
}
sentDataSize += retsize;
}
if (errno != 0)
return False;
return sentDataSize == tmpDataSize;
}
Boolean Network_Send(int fifo,const void* data,size_t dataSize)
{
return Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize);
}
Boolean Network_Receive(int fifo,void* data,size_t dataSize)
{
return Network_IO(fifo,NetworkOpCode_Read,data,dataSize);
}
I've created, as an homework, a big project which simulate a mailbox server (only through process on the same computer, so through fifo, it's a homework)
I can't post the project because is big (there are a lot of files), but I can say that sometimes I lost some data or it doesn't preserve it's integrity.
I use these code snippet to transmit data, is it somewhat wrong?Network_IO is the function I'm talking about:
#include "Network.h"
int Network_Open(const char* path,int oflag)
{
return open(path,oflag);
}
ssize_t Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize)
{
ssize_t retsize = 0;
ssize_t tmpDataSize = (ssize_t)dataSize;
errno = 0;
if (tmpDataSize == 0) return 0;
while ((retsize = (opcode == NetworkOpCode_Write? write(fifo,data,tmpDataSize) : read(fifo,data,tmpDataSize))) != tmpDataSize)
{
if (errno != EINTR) break;
}
return retsize;
}
Boolean Network_Send(int fifo,const void* data,size_t dataSize)
{
return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize);
}
Boolean Network_Receive(int fifo,void* data,size_t dataSize)
{
return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Read,data,dataSize);
}
Boolean Network_Close(int fifo)
{
if (fifo >= 0)
return close(fifo) == 0;
}
Edit 1: Code snippet which I'm using to test actually
Boolean Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize)
{
ssize_t retsize = 0;
ssize_t tmpDataSize = (ssize_t)dataSize;
ssize_t sentDataSize = 0;
errno = 0;
if (tmpDataSize == 0) return True;
while (sentDataSize < tmpDataSize)
{
switch(opcode)
{
case NetworkOpCode_Write:
retsize = write(fifo,data + sentDataSize,tmpDataSize - sentDataSize);
break;
case NetworkOpCode_Read:
retsize = read(fifo,data + sentDataSize,tmpDataSize - sentDataSize);
break;
}
if (retsize < 0)
{
if (errno != EINTR) return False;
else
{
errno = 0;
continue;
}
}
sentDataSize += retsize;
}
if (errno != 0)
return False;
return sentDataSize == tmpDataSize;
}
Boolean Network_Send(int fifo,const void* data,size_t dataSize)
{
return Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize);
}
Boolean Network_Receive(int fifo,void* data,size_t dataSize)
{
return Network_IO(fifo,NetworkOpCode_Read,data,dataSize);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
恕我直言,Network_IO() 函数没有任何作用。它的唯一目的是“多路分解”读/写调用的操作码,这些操作码由 Network_Send() 和 Network_Receive() 函数提供给它。更好的方法是直接在 Network_Send() 和 Network_Receive() 函数中调用 read() 并写入。您选择的返回类型(布尔值)也很奇怪。
read() 和 write() 上的错误条件可能不同,将来可能不仅仅需要在其中之一中处理 EINTR。另外:您的函数阻止,这意味着:在实际发送或接收所需的金额之前,它们不会返回。另请注意,对于管道和 fifo,内核提供的缓冲区空间量非常有限,通常为 1 个内存页。这增加了读取器或写入器阻塞读取或写入的机会,并导致每个传输的数据块(至少)两次上下文切换。
“循环直到完成”方法; Mat 提供的内容是关于做事的标准方式。还要做好读/写返回零的准备。
编辑:Mat 的意思是您需要处理部分读/写:您需要从上次中断的地方开始,发送/接收缓冲区的剩余部分。这是一个开始:
IMHO the Network_IO() function serves no purpose. It's only purpose is to 'demultiplex' the opcodes for read/write calls, that were given to it by the Network_Send() and Network_Receive() functions. Better would be to call read() and write directly in the Network_Send() and Network_Receive() functions. Your choice of return type (Boolean) is also strange.
The error conditions on read() and write() could be different, in the future maybe more than just EINTR needs to be handled in one of them. Also: your functions block, that means: they don't return until the desired amount has actually been sent or received. Also note that for pipes and fifos, the amount of bufferspace supplied by the kernel is very limited, typically 1 memory page. This increases the chance of the reader or writer blocking in reads or writes, and results in (at least) two context switches per block of data transferred.
The "loop until done" method; as supplied by Mat is about the standard way of doing things. Also be be prepared for read/write returning zero.
EDIT: what Mat meant is that you need to handle partial reads/writes: you need to start over where you left off, sending/receiving the remaining part of the buffer. Here is a start:
对于
write
情况,您的代码可以归结为想象一下,在第一个
write
上,仅写入一个字节。如果发生这种情况,您需要下一个write
尝试从data+1
开始推送tmpDataSize-1
字节。但是您现在所做的将重新发送所有内容,包括第一个字节。在伪代码中,逻辑应该类似于:
读取情况相同。
顺便说一句,虽然带有赋值和
?:
构造,但确实很难阅读。For the
write
case, your code boils down toImagine that on the first
write
, only one byte gets written. If that happens, you need the nextwrite
to attempt to pushtmpDataSize-1
bytes, starting atdata+1
. But what you do now will resend everything, including that first byte.In pseudo-code, the logic should be something like:
Same thing for the read case.
BTW, that while with an assignment and a
?:
construct is really hard to read.