C: оставшиеся байты на сокете AF_UNIX - PullRequest
0 голосов
/ 06 февраля 2019

У меня возникли некоторые проблемы с AF_UNIX обменом сокетами, потому что после записи буфера данных, кажется, остаются некоторые зависающие байты, чтобы прочитать, что я не знаю, откуда они берутся.

Я пишу многопоточный серверПрограмма на C, которая общается с клиентами через AF_UNIX сокетов, должна реализовывать простой чат.Помимо прочего, сервер должен осуществлять передачу файлов между клиентами и серверами, и у меня возникли проблемы при попытке отправить довольно большой файл (269 КБ) с сервера на клиент.(С файлами меньшего размера у меня нет проблем)

Для передачи файлов я использую функцию mmap(), которая возвращает указатель на карту файла, который я хочу отправить, затем я использую write() для записи, чтоданные на сокете связаны с клиентом, который должен получить файл.После вызова write() я проверяю возвращаемое значение на размер файла.(всегда проверено)

Клиент, после получения файла, проверяет размер прочитанных данных (всегда проверяется) и начинает ждать других сообщений, поэтому он вызывает блокировку read(). Это - это точка, в которой я обнаружил ошибку, потому что клиент читает что-то, чего не должно быть, , как будто что-то осталось прочитать в сокете .Я отлаживал эту часть (как серверную, так и клиентскую) в течение двух дней, и я пока не смог понять причину проблемы.

Я уверен, что ни один другой поток не пишет в один и тот же сокет в одно и то же время

Кто-нибудь из вас имеет представление о причине этой ошибки?

Я пытаюсь опубликовать некоторый полезный код, думая о нормальной последовательности операций:

Прежде всего структура сообщения:

struct message_hdr
{
    op_t     op; 
    char sender[MAX_NAME_LENGTH+1];
};

struct message_data_hdr{
    char receiver[MAX_NAME_LENGTH+1];
    unsigned int   len;
};

struct message_data
{
    message_data_hdr_t hdr;
    char *buf;
};

struct message
{
    message_hdr_t  hdr;
    message_data_t data;
};

Сервер-> клиентская передача файла начинается ссервер, который отправляет message_hdr_t клиенту, ожидающему read() (клиент ожидает получить только message_hdr_t).

int sendHeader(long fd, message_hdr_t* hdr)
{
    if(hdr == NULL || fd < 0) {errno = EINVAL; return -1;}

    int test;
    struct iovec iov;

    iov.iov_base = hdr;
    iov.iov_len = sizeof(message_hdr_t);

    test = writev(fd, &iov, 1);

    return  test;
}

Клиент понимает из кода операции (message.hdr.op) что это сообщение типа файла и оно начинает ждать файла,

Итак, сервер отправляет его:

int sendData(long fd, message_data_t *msg)
{
    if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}

    int test;
    struct iovec iov;

    iov.iov_base = &(msg->hdr);
    iov.iov_len = sizeof(message_data_hdr_t);

    test = writev(fd, &(iov), 1);
    if(test == -1){return -1;}

    if (msg->hdr.len != 0)
    {
        test = write(fd, msg->buf, msg->hdr.len);
        if(test <= 0)
            return -1;
    }

    return test;
}

И клиент читает его:

int readData(long fd, message_data_t *data)
{
    if(data == NULL || fd < 0) {errno = EINVAL; return -1;}

    int test;
    struct iovec iov;

    iov.iov_base = &(data->hdr);
    iov.iov_len = sizeof(message_data_hdr_t);

    test = readv(fd, &iov, 1);
    if(test <= 0){return -1;}

    if(data->hdr.len != 0)
    {
        data->buf = malloc(data->hdr.len);
        if(data->buf == NULL){return -1;}

        test = read(fd, data->buf, data->hdr.len);
        if((unsigned int)test != data->hdr.len)
            return -1;
    }

    return test;
}

В этот момент клиент получил файл и перезапустил его в ожидании новых сообщений:

int readMsg(long fd, message_t *msg)
{
    if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}

    int test;

    test = readHeader(fd, &(msg->hdr));
    if(test == -1 || test == 0){return -1;}

    test += readData(fd, &(msg->data));
    return test;
}

Это точка, в которой клиент должен просто ждать, потому что нет сообщений о доходах, в этом случае он что-то читаетчто я не знаю, откуда это.

Когда я пытаюсьраспечатайте это нежелательное сообщение с помощью GDB:

{hdr = {op = 512, 
        sender = "\000\000\020G\032\324\t\000\000\n\000\000\000\000\030\021B\bC\n\000\000\v\000\000\000\000\021D\v\222\000"}, 
 data = {hdr = {receiver = "\000\000\000\000\021E\022C\n\000\000\b\v\000\000\000\000\021F\020I\n\000\000\020\000\006\b\002\n\000\000\006", 
         len = 131072}, 
 buf = 0x7ffff7f2f010 ""}`

Конечно, это бессмысленно.

Я надеюсь, что это описание будет полезно

Спасибо всем заранее.

1 Ответ

0 голосов
/ 07 февраля 2019

Хорошо, я решил свою проблему.Как написано в комментарии, эта проблема возникла из-за отсутствия проверки на частичную запись.

Теперь функция readData() выглядит следующим образом:

int readData(long fd, message_data_t *data)
{
    if(data == NULL || fd < 0) {errno = EINVAL; return -1;}

    int test;
    char* ph;
    unsigned int rd = 0;
    struct iovec iov;

    iov.iov_base = &(data->hdr);
    iov.iov_len = sizeof(message_data_hdr_t);

    test = readv(fd, &iov, 1);
    if(test <= 0){return -1;}

    if(data->hdr.len != 0)
    {
        data->buf = malloc(data->hdr.len);
        if(data->buf == NULL){return -1;}

        ph = data->buf;

        while (rd < data->hdr.len)
        {
            test = read(fd, ph, data->hdr.len - rd);
            if(test ==  -1)
                return -1;
            else if(test == 0)
            {
                errno = ENOENT;
                return -1;
            }

            rd += test;
            ph += test;
        }
    }

    return rd;
}

и sendData():

int sendData(long fd, message_data_t *msg)
{
    if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}

    int test;
    char* ph;
    unsigned int wr = 0;
    struct iovec iov;

    iov.iov_base = &(msg->hdr);
    iov.iov_len = sizeof(message_data_hdr_t);

    test = writev(fd, &(iov), 1);
    if(test == -1){return -1;}

    if(msg->hdr.len != 0)
    {
        ph = msg->buf;

        while (wr < msg->hdr.len)
        {
            test = write(fd, ph, msg->hdr.len - wr);
            if(test ==  -1)
                return -1;
            else if(test == 0)
            {
                errno = ENOENT;
                return -1;
            }

            wr += test;
            ph += test;
        }
    }

    return test;
}

Таким образом, я больше не нашел ошибку.

Спасибо за помощь!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...