Отправка int через TCP (C-программирование) - PullRequest
0 голосов
/ 15 ноября 2009

У меня есть сервер и клиентская программа (обе работают на одной машине). Клиент может отправить структуру на сервер с такими элементами, как «ID», «размер» и т. Д. Затем я хотел бы, чтобы сервер отправил ID-элемент (просто целое число) обратно клиенту в качестве ACK для проверки , но я просто не могу понять это, несмотря на то, что могу отправить структуру без проблем ..

Вот код из server.c:

/* having just recieved the struct */

int ACK_ID = struct_buffer->message_ID;
result = send(CLIENT_socket, &ACK_ID, sizeof(int), 0);

if (result == -1) {
    close(SERVER_socket);
    printf("\n\t[ERROR] Failed to send ACK.\n");
    exit(EXIT_FAILURE);
}

Вот код из client.c:

// Recieve ACK from server
int ACK_ID;
com_result = read(CLIENT_socket, &ACK_ID, sizeof(int), 0);

if ((com_result == -1) || (ACK_ID != metablocks[index].message_ID)) {
    printf("\n\t[ERROR] Failed to send metadata. ACK: %i\n", ACK_ID);
}

Когда я пытаюсь запустить это, я получаю следующий вывод из client.c:

[ОШИБКА] Не удалось отправить метаданные. ACK: 14

И, конечно, сервер сообщает мне, что не удалось отправить ACK. Значение целого числа ID, которое я пытаюсь отправить, должно быть 1, но оно получено как 14. Что я здесь не так делаю?

Обновление
Поэтому я просто попробовал то, что предложил мистер Шоули, и получил это сообщение об ошибке:

Частичное чтение: неопределенная ошибка: 0

Сначала я попробовал именно то, что он написал, но потом заметил, что код сравнивает com_result с sizeof(int). Поэтому я предположил, что это опечатка, и попытался заменить com_result на переменную ACK_ID в сравнении. Тот же результат.

Обновление 2
Просто добавил perror () на сервер, когда он не работает, и получил следующее сообщение об ошибке:

Неверный дескриптор файла

Я использую тот же сокет для этой операции, что и тот, который я использовал при получении структуры. Вот расширенный пример кода из server.c:

// Recieve connection
CLIENT_socket = accept(SERVER_socket, (struct sockaddr *)&CLIENT_address, &CLIENT_address_length);
if (CLIENT_socket == -1) {
    close(SERVER_socket);
    printf("\n\t[ERROR] Failed to accept client connection.\n");
    exit(EXIT_FAILURE);
}

printf("\n\tClient connected!\n");

int data_size;

// Read meta data from connection
data_size = sizeof(struct msg_meta);
result = read(CLIENT_socket, &meta_buffer_char, data_size, 0);
meta_buffer = (struct msg_meta *) meta_buffer_char;

if (result == -1) {
    close(SERVER_socket);
    printf("\n\t[ERROR] Failed to read from connection.\n");
    perror("\n\tRead");
    exit(EXIT_FAILURE);
} else if (result > 0) {
    printf("\n\tMessage recieved.\n");
    printf("\n");
}

// Send ACK back to client
int ACK_ID = meta_buffer->message_ID;
result = send(CLIENT_socket, &ACK_ID, sizeof(int), 0);

if (result == -1) {
    printf("\n\t[ERROR] Failed to send ACK.");
    perror("\n\tSend");
    printf("\n");
    close(SERVER_socket);
    exit(EXIT_FAILURE);
}

// Close sockets
close(SERVER_socket);
close(CLIENT_socket);

Ответы [ 5 ]

1 голос
/ 15 ноября 2009

Re: обновление 2 - убедитесь, что вы (a) используете правильный дескриптор файла и (b) не закрыли () или не закрыли () часть записи сокета.

1 голос
/ 15 ноября 2009

Я предполагаю, что read терпит неудачу, в результате чего com_result == -1. В этом случае значение ACK_ID является неопределенным мусором стека. Попробуйте вместо этого:

com_result = read(CLIENT_socket, &ACK_ID, sizeof(int), 0);
if (com_result < 0) {
    perror("read");
} else if (com_result != sizeof(int)) {
    /* handle partial read condition */
} else if (ACK_ID != metablocks[index].message_ID) {
    printf("\n\t[ERROR] Failed to receive metadata. ACK: %i\n", ACK_ID);
}

Существует ряд причин, по которым read() может завершиться с ошибкой или вернуть частичный результат - в конце концов, это TCP. Perror, по сути, вызовет для вас strerror(errno) и отобразит сообщение, которое вы предоставляете с добавленной к нему строкой ошибки. Когда системный вызов, такой как read() или send(), возвращает -1, он устанавливает для errno более описательное значение, которое можно отобразить с помощью perror() или strerror().

.

Обновление - частичное чтение

Что касается проблемы частичного чтения, вы обычно решаете ее, либо (1) игнорируя ее, либо (2) выполняя чтение в цикле, пока не получите все ожидаемые байты. Что-то вроде:

int status = 0;
char *byte_ptr = (char*)&ACK_ID;
ssize_t bytes_left = sizeof(ACK_ID);
while (bytes_left > 0) {
   ssize_t rc = read(CLIENT_socket, byte_ptr, bytes_left);
   if (rc < 0) {
       if (errno == EINTR) {
           continue; /* interrupted system call */
       }
       perror("read");
       status = -1;
       break;
   } else if (rc == 0) {
       /* EOF */
       break;
   }
   bytes_left -= rc;
   byte_ptr += rc;
}
if (status == 0) {
    if (bytes_left == 0) {
        /* safely use the value stored in ACK_ID */
    } else {
        /* handle premature socket closure */
    }
}

Обычно это заключено в общую библиотечную функцию, чтобы упростить жизнь. Я бы порекомендовал прочитать W. UNIX Сетевое программирование Ричарда Стивена, том 1 , если вы еще этого не сделали. Это именно то, что он делает в своей библиотечной функции readn().

1 голос
/ 15 ноября 2009

Вам необходимо проверить ошибку, которая генерируется при отправке. Вы должны включить библиотеку ошибок #include <cerrno> и затем проверить значение глобального errno. Существует множество макросов, в которых объявляется тип ошибки. Это даст вам больше информации при отладке, почему он не смог отправить ACK.

Вот список возможных возвращаемых значений ошибок (значения ошибок - макросы, определенные в cerrno), взятые из здесь :

     [EACCES]           The SO_BROADCAST option is not set on the socket and a broadcast address is given as
                        the destination.

     [EAGAIN]           The socket is marked non-blocking and the requested operation would block.

     [EBADF]            An invalid descriptor is specified.

     [ECONNRESET]       A connection is forcibly closed by a peer.

     [EFAULT]           An invalid user space address is specified for a parameter.

     [EHOSTUNREACH]     The destination address specifies an unreachable host.

     [EINTR]            A signal interrupts the system call before any data is transmitted.

     [EMSGSIZE]         The socket requires that message be sent atomically, and the size of the message to
                        be sent makes this impossible.  IOV_MAX.

     [ENETDOWN]         The local network interface used to reach the destination is down.

     [ENETUNREACH]      No route to the network is present.

     [ENOBUFS]          The system is unable to allocate an internal buffer.  The operation may succeed when
                        buffers become available.

     [ENOBUFS]          The output queue for a network interface is full.  This generally indicates that the
                        interface has stopped sending, but may be caused by transient congestion.

     [ENOTSOCK]         The argument socket is not a socket.

     [EOPNOTSUPP]       socket does not support (some of) the option(s) specified in flags.

     [EPIPE]            The socket is shut down for writing or the socket is connection-mode and is no
                        longer connected.  In the latter case, and if the socket is of type SOCK_STREAM, the
                        SIGPIPE signal is generated to the calling thread.

     The sendmsg() and sendto() system calls will fail if:

     [EAFNOSUPPORT]     Addresses in the specified address family cannot be used with this socket.

     [EDESTADDRREQ]     The socket is not connection-mode and does not have its peer address set, and no
                        destination address is specified.

     [EISCONN]          A destination address was specified and the socket is already connected.

     [ENOENT]           A component of the pathname does not name an existing file or the path name is an
                        empty string.

     [ENOMEM]           Insufficient memory is available to fulfill the request.

     [ENOTCONN]         The socket is connection-mode, but is not connected.

     [ENOTDIR]          A component of the path prefix of the pathname in the socket address is not a direc-tory. directory.
                        tory.

     The send() system call will fail if:

     [EDESTADDRREQ]     The socket is not connection-mode and no peer address is set.

     [ENOTCONN]         The socket is not connected or otherwise has not had the peer pre-specified.

     The sendmsg() system call will fail if:

     [EINVAL]           The sum of the iov_len values overflows an ssize_t.

     [EMSGSIZE]         The socket requires that message be sent atomically, and the size of the message to
                        be sent makes this impossible, or the msg_iovlen member of the msghdr structure
                        pointed to by message is less than or equal to  or is greater than IOV_MAX.
0 голосов
/ 15 ноября 2009

Сначала попробуйте использовать htonl перед отправкой int, и ntohl после его получения. Затем вы должны ВСЕГДА отправлять и получать циклы, поэтому вы отправляете и извлекаете ВЕСЬ объем ваших данных.

0 голосов
/ 15 ноября 2009

Вы можете проверить, почему send не работает. Например, используйте perror:

if (result == -1) {
    perror("server error while sending ack");
    ....

Не вызывайте никакие другие функции между ошибочными send и perror, так как это приведет к сбросу errno.

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