Отправка длины буфера и буфера через сокет в c - PullRequest
2 голосов
/ 11 июля 2009

Я пытаюсь отправить строку в серверное приложение, используя C, но я столкнулся с проблемой. Я довольно новичок в сетевом программировании, и я думаю, что мой код вызывает неправильное дерево.

Предполагается, что сообщение имеет длину сообщения + сообщение и распаковывается с другой стороны питон-сервером как таковым (buf является исходными входящими данными):

msg_len_bytes = buf[0:4]
msg_len = struct.unpack("!L", msg_len_bytes)[0]

! означает сетевой порядок байтов, а L означает длинный без знака.

Достаточно просто отправить обычную строку. отправить (носок, сообщение, strlen (сообщение), 0);

Но, добавляя длину сообщения, я не могу понять, на что. Вот код для моего клиента до сих пор:

struct msgstruct {
        uint32_t length;
        char send_data[4096];
};

int main()

{
    int sock;
    struct msgstruct message;
    char data[4096] = "<MOP><test/></MOP>";

    for ( int i = 0; i < strlen(data); i++ ) {
      message.send_data[i] = data[1];
    }

    struct hostent *host;
    struct sockaddr_in server_addr;

    unsigned long buflen = sizeof(message.send_data);
    uint32_t bufsend = htonl(buflen);

    message.length = bufsend;

    host = gethostbyname("127.0.0.1");

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Socket");
        exit(1);
    }

    server_addr.sin_family = AF_INET;     
    server_addr.sin_port = htons(12998);   
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&(server_addr.sin_zero),8); 


    if (connect(sock, (struct sockaddr *)&server_addr,
                sizeof(struct sockaddr)) == -1) {
        perror("Connect");
        exit(1);
    }

    if(send(sock, message.length + message.send_data, sizeof(message), 0) == -1){
        printf("\nSocket error.");
        exit(1);
    }
    return 0;
}

Я пробовал несколько вариантов, но я всегда получаю ошибку сокета. Это потому, что я смешиваю типы во втором аргументе для отправки? Я получаю ошибки компиляции, если я пытаюсь отправить структуру.

Ответы [ 4 ]

5 голосов
/ 11 июля 2009

Вы можете использовать 2 последовательных отправки:

send(sock, &message.length, sizeof(message.length), 0);
send(sock, message.send_data, message.length*sizeof(char), 0);

Или лучше подготовить буфер с первыми 4 байтами в качестве длины сообщения:

char buff[MAX_BUFF] = "";
int  len_disp = sizeof(message.length);
memcpy(buff, &message.length, len_disp);
memcpy(&buff[len_disp], &message.length, message.length*sizeof(char));
send(sock, buff, message.length*sizeof(char) + len_disp, 0);

EDIT: Для небольших сообщений комментируйте - отключение алгоритма Nagle.

BOOL bNagleEnabled = FALSE;
if(setsockopt(sAccept, IPPROTO_TCP, TCP_NODELAY, (char *)&bNagleEnabled, sizeof(BOOL)))
{
  ReportError("Setting TCP_NODELAY socket option failed");
  return -2;
}
4 голосов
/ 11 июля 2009

Ошибка в этом коде:

send(sock, message.length + message.send_data, sizeof(message), 0)

Прототип для отправки:

ssize_t send(int s, const void *buf, size_t len, int flags);

Обратите внимание, что параметр 2 является указателем. В вашем коде вы указали его как длину (введите uint32_t), добавленную в буфер (введите char *). Это добавление приведет к символу *, (указателю на символ), но к указателю на непредсказуемую и бессмысленную область памяти.

Чтобы получить указатель на буфер, вы хотите:

send(sock, &message, sizeof(message), 0)

Обратите внимание, что получение адреса структуры не переносимо или не всегда желательно из-за проблем с заполнением. Но на типичной 32-битной архитектуре это должно подойти.

Это отправит данные, начиная со структуры сообщения, но отправив 4100 (4096 + 4) байтов !. Я не думаю, что вы собираетесь отправить так много. Третий параметр говорит, сколько байтов для отправки, и должен быть установлен в:

sizeof(uint32_t) + strlen(data);  // 4-byte Integer + Length of the data "<MOP><test/></MOP>"

Обратите внимание, что не включает в себя Null-Terminator для данных, но ваш начальный цикл for также не копировал Null-Terminator

(Если вы хотите использовать нулевой терминатор, сделайте ваш начальный цикл for равным strlen (data) + 1 и используйте strlen (data) + 1 в других местах а также).

В идеале, вы должны кэшировать strlen (данные) в локальную переменную, а не вызывать ее так часто. (вы также неоднократно вызываете strlen в начальном цикле for).

Ваше окончательное утверждение будет выглядеть так:

if(send(sock, &message, sizeof(uint32_t)+strlen(data), 0) == -1){
    printf("\nSocket error.");
    exit(1);
}

Попробуйте, и дайте мне знать, как это происходит.

0 голосов
/ 30 июня 2012

В основном все должно превратить буфер и отправить / получить через сокет. Так что обычно я делаю:

 //Send
 memcpy(buffer, &structure_u_defined, sizeof(structure_u_defined));
 send(&sock, buffer,...);// lol I am using a public pc

 //receive
 recv(&sock, buffer,...);
 memcpy(&structure_u_defined_for_receive, buffer, sizeof(buffer));
0 голосов
/ 11 июля 2009

Цикл, который копирует данные в экземпляр message, прерван, у него есть 1, где он должен иметь i.

Второй аргумент send() должен быть указателем на первый байт для отправки. Вместо этого вы даете ему сумму числа с прямым порядком байтов (которое, если ваша платформа не является байтом с прямым порядком байтов, будет очень неправильным) и базового адреса случайного массива. Это неправильно.

Вам нужно:

if(send(sock, &message, sizeof message.length + strlen(data), 0) == -1) {

Существует также другая путаница, вы, кажется, «хотите» всегда посылать полный 4K, а не только необходимую сумму.

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