Сервер IPv6 в c - ошибка при получении - PullRequest
2 голосов
/ 13 января 2020

Я написал небольшую c -программу для сервера IPv6 с winsock2.h Когда я запускаю программу в Visual Studio, я все время получаю следующее сообщение: сбой recvfrom

Я просто могу не найти ошибку в функции recvfrom. Может быть, кто-то может понять, почему моя программа не работает в этот момент, спасибо! : -)

С уважением, Кен

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma comment(lib, "Ws2_32.lib")

int main(int argc, char* argv[]) {

    SOCKET server_socket;
    struct sockaddr_in6 server_addr, client_addr;
    socklen_t client_len;
    char buf[1024];
    char clientIP[256];

    WSADATA data;
    WORD version = MAKEWORD(2, 2);
    if (WSAStartup(version, &data) == SOCKET_ERROR) {

        printf("WSASStartup failed\n");
        WSACleanup();
        exit(EXIT_FAILURE);
    }

    server_socket = socket(AF_INET6, SOCK_DGRAM, 0);
    if (server_socket == -1) {

        printf("creating socket failed\n");
        WSACleanup();
        exit(EXIT_FAILURE);
    }
    else {

        printf("creating socket successful\n");
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin6_addr = in6addr_any;
    server_addr.sin6_family = AF_INET6;
    server_addr.sin6_port = htons(5001);

    if (bind(server_socket, (const struct sockaddr*) & server_addr,
        sizeof(server_addr)) == -1) {

        printf("bind socket failed\n");
        WSACleanup();
        exit(EXIT_FAILURE);
    }
    else {

        printf("bind socket successful\n");
    }

    while (1) {

        memset(&client_addr, 0, sizeof(client_addr));
        memset(buf, 0, sizeof(buf));

        if (recvfrom(server_socket, (char*)buf, 1024, 0,
            (struct sockaddr*) & client_addr,
            sizeof(client_addr)) == -1) {

            printf("recvfrom failed\n");
            WSACleanup();
            exit(EXIT_FAILURE);
        }
        else {

            printf("recvfrom successful");
        }

        printf("%s\n", buf);
        printf("IP: %s\n", inet_ntop(AF_INET6, &client_addr.sin6_addr,
            clientIP, 256));
    }

    closesocket(server_socket);

    WSACleanup();

    return 0;
}

1 Ответ

1 голос
/ 14 января 2020

Вы неправильно используете recvfrom(). Параметр fromlen ожидает получения указателя int*, который указывает на int, который на входе указывает размер байта буфера sockaddr, передаваемого в параметре from, а на выходе получает размер байта sockaddr, записанных в буфер from.

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

  • WSAStartup() не возвращает SOCKET_ERROR при ошибке возвращает действительный код ошибки.

  • вы игнорируете возвращаемое значение recvfrom(), которое говорит вам, сколько байтов было фактически записано в ваш buf. Вы предполагаете, что buf всегда завершается нулем при передаче его в printf("%s"), но это не гарантируется. Вы обнуляете buf, чтобы инициализировать его нулевыми терминаторами, что хорошо, если recvfrom() получает дейтаграмму, содержащую менее 1024 байтов. Но если он получит дейтаграмму с ровно 1024 байтами, тогда все ваши нулевые терминаторы будут перезаписаны, и для printf() не останется ни одного терминатора (если recvfrom() получит дейтаграмму с более чем 1024 байтами, произойдет сбой с ошибка WSAEMSGSIZE, которая не является фатальной ошибкой, но вы рассматриваете ее так, как если бы она была). Вместо того чтобы вообще полагаться на нулевые терминаторы, вы можете передать возвращаемое значение от recvfrom() до printf() для размера буфера. Не нужно тратить накладные расходы на обнуление того, что recvfrom() будет перезаписано.

Попробуйте вместо этого:

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma comment(lib, "Ws2_32.lib")

int main(int argc, char* argv[]) {

    SOCKET server_socket;
    struct sockaddr_in6 server_addr, client_addr;
    int client_len, num_recvd;
    char buf[1024];
    char clientIP[256];

    WSADATA data;
    WORD version = MAKEWORD(2, 2);

    int errCode = WSAStartup(version, &data);
    if (errCode != 0) {
        printf("WSAStartup failed, error %d\n", errCode);
        WSACleanup();
        return EXIT_FAILURE;
    }

    server_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    if (server_socket == INVALID_SOCKET) {
        errCode = WSAGetLastError();
        printf("creating socket failed, error %d\n", errCode);
        WSACleanup();
        return EXIT_FAILURE;
    }

    printf("creating socket successful\n");

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin6_addr = in6addr_any;
    server_addr.sin6_family = AF_INET6;
    server_addr.sin6_port = htons(5001);

    if (bind(server_socket, (struct sockaddr*) &server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
        errCode = WSAGetLastError();
        printf("bind socket failed, error %d\n", errCode);
        closesocket(server_socket);
        WSACleanup();
        return EXIT_FAILURE;
    }

    printf("bind socket successful\n");

    while (1) {

        client_len = sizeof(client_addr);

        num_recvd = recvfrom(server_socket, buf, sizeof(buf), 0, (struct sockaddr*) &client_addr, &client_len);
        if (num_recvd == SOCKET_ERROR) {
            errCode = WSAGetLastError();
            if (errCode != WSAEMSGSIZE) {
                printf("recvfrom failed, error %d\n", errCode);
                closesocket(server_socket);
                WSACleanup();
                return EXIT_FAILURE;
            }
            printf("recvfrom truncated a datagram larger than %u bytes!\n", sizeof(buf));
            num_recvd = sizeof(buf);
        }
        else {
            printf("recvfrom successful\n");
        }

        printf("%.*s\n", num_recvd, buf);
        printf("IP: %s\n", inet_ntop(AF_INET6, &client_addr.sin6_addr, clientIP, 256));
    }

    closesocket(server_socket);
    WSACleanup();

    return 0;
}
...