Правильный способ записи / чтения данных через пятерку - PullRequest
0 голосов
/ 23 октября 2011

В качестве домашней работы я создал большой проект, имитирующий сервер почтовых ящиков (только через процесс на том же компьютере, поэтому через 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);
}

Ответы [ 2 ]

2 голосов
/ 23 октября 2011

ИМХО функция Network_IO () не имеет смысла. Его единственная цель - «демультиплексировать» коды операций для вызовов чтения / записи, которые были переданы ему функциями Network_Send () и Network_Receive (). Лучше было бы вызвать read () и записать напрямую в функции Network_Send () и Network_Receive (). Ваш тип возвращаемого значения (Boolean) также странный.

Условия ошибок read () и write () могут отличаться, в будущем, возможно, в одном из них потребуется обработать не только EINTR. Также: ваши функции block , что означает: они не возвращаются, пока желаемая сумма не будет отправлена ​​или получена. Также обратите внимание, что для каналов и fifos объем буферного пространства, предоставляемого ядром, очень ограничен, обычно 1 страница памяти. Это увеличивает вероятность блокировки чтения или записи при чтении или записи и приводит к (как минимум) двум переключениям контекста на блок передаваемых данных.

метод "пока не закончится"; Мат предоставил стандартный способ ведения дел. Также будьте готовы к чтению / записи, возвращая ноль.

РЕДАКТИРОВАНИЕ: то, что имел в виду Мат, заключается в том, что вам нужно обрабатывать частичные операции чтения / записи: вам нужно начинать с , где вы остановились , отправляя / получая оставшуюся часть буфера. Вот начало:

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;
}
2 голосов
/ 23 октября 2011

Для случая 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;
}

То же самое для случая чтения.

Кстати, хотя с назначением и конструкцией ?: действительно трудно читать.

...