作为家庭作业,我创建了一个模拟邮箱服务器的大项目(只能通过同一台计算机上的进程,所以通过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);
}
答案 0 :(得分:2)
对于write
案例,您的代码归结为
while ((retsize = write(fifo,data,tmpDataSize)) != tmpDataSize) { ... }
想象一下,在第一个write
上,只写入一个字节。如果发生这种情况,您需要下一个write
尝试从tmpDataSize-1
开始推送data+1
个字节。但是你现在所做的将重新发送所有内容,包括第一个字节。
在伪代码中,逻辑应该是这样的:
while (bytesLeftToSend > 0) {
sent = write(fifo, data, bytesLeftToSend);
if (sent == -1) {
// report error and bail out
}
bytesLeftToSend -= sent;
data += sent;
}
阅读案例也是如此。
顺便说一下,虽然有一个作业和一个?:
结构,但实际上很难阅读。
答案 1 :(得分:2)
恕我直言,Network_IO()函数没有用处。它的唯一目的是“解复用”读取/写入调用的操作码,这些操作由Network_Send()和Network_Receive()函数提供给它。更好的方法是调用read()并直接在Network_Send()和Network_Receive()函数中编写。您选择的返回类型(布尔值)也很奇怪。
read()和write()的错误条件可能不同,将来可能不仅仅需要在其中一个中处理EINTR。另外:您的功能阻止,这意味着:在实际发送或接收到所需金额之前,它们不会返回。另请注意,对于管道和fifos,内核提供的缓冲区空间量非常有限,通常为1个内存页。这增加了读取器或写入器在读取或写入时阻塞的可能性,并且每个传输数据块导致(至少)两次上下文切换。
“循环完成”方法;由Mat提供的是标准的做事方式。也可以准备读/写返回零。
编辑:Mat的意思是你需要处理部分读/写:你需要从你离开的开始,发送/接收缓冲区的剩余部分。这是一个开始:
int mywrite(int fd, char *buff, size_t size)
{
int rc;
size_t done, todo;
for (done=0; done < size; ) {
todo = size - done;
rc = write (fd, buff+done, todo);
switch (rc) {
case -1: /* some read error: check it */
switch(errno) {
case EINTR: continue;
/* ... maybe some other cases you need to handle */
default: return -1;
}
break;
case 0: /* (in some cases) the other side closed the connection */
/* handle it here; possibly return error */
break;
default: /* the normal case */
done += rc;
break;
}
}
return done;
}