Winsock: UDP recvfrom () не заполняет тот же IP-адрес, который использовался для отправки данных - PullRequest
0 голосов
/ 17 октября 2019

Я кодирую эхо-клиент и сервер UDP. Они оба работают на моей машине. Клиент настроен на отправку сообщения на IP-адрес :: 1. В этот момент мой сервер получает сообщение и должен напечатать что-то похожее на это.

Processing client at ::1

Вместо этого сервер продолжает получать другой IP-адрес.

Processing client at 132:e3d5::

Примечание. Получаемый IP-адрес каждый раз отличается.

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

Вот код:

Client.c

void main(int argc, char* argv[])   // argc is # of strings following command, argv[] is array of ptrs to the strings
{
    WSADATA wsaData;                // contains details about WinSock DLL implementation
    struct sockaddr_in6 serverInfo; // standard IPv6 structure that holds server socket info
    char* serverIPaddr, * phrase, echoBuffer[RCVBUFSIZ];
    int serverPort, msgLen, fromSize, echoLen;

    // Verify correct number of command line arguments
    if (argc != 4) {
        printf("Invalid amount of arguments entered. \nPlease make sure to only enter the following: \n\n <application name> <ip address> <port number> <message>");
        exit(1);
    }

    // Retrieve the command line arguments.
    // to be converted from char to int. 
    serverIPaddr = argv[1];
    serverPort = atoi(argv[2]);
    phrase = argv[3];
    msgLen = strlen(phrase) + 1; // We are sending the null terminator

    // Initialize Winsock 2.0 DLL. 
    if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
        // Failed
        printf("Couldn't initialize Winsock 2.0 DLL");
        exit(1);
    }

    // Create an IPv6 UPD stream socket.  Now that Winsock DLL is loaded, we can signal any errors as shown on next line:
    int sock;
    sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == INVALID_SOCKET) {
        DisplayFatalErr("socket() function failed.");
        exit(1);
    }
    printf("Socket created successfully. Press enter to continue...");
    getchar();

    memset(&serverInfo, 0, sizeof(serverInfo));
    serverInfo.sin6_family = AF_INET6;
    serverInfo.sin6_port = htons(serverPort);
    inet_pton(AF_INET6, serverIPaddr, &serverInfo.sin6_addr);

    if (msgLen == 0) {
        DisplayFatalErr("Message was empty, please make sure to include a message.");
        exit(1);
    }

    // Send data to server
    if (sendto(sock, phrase, msgLen, 0, (struct sockaddr*) & serverInfo, sizeof(serverInfo)) != msgLen) {
        DisplayFatalErr("sendto() function failed.");
        exit(1);
    }

    fromSize = sizeof(serverInfo);
    if ((echoLen = recvfrom(sock, echoBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & serverInfo.sin6_addr, &fromSize)) != msgLen) {
        // We lost some data check for error first
        if (echoLen < 0) {
            DisplayFatalErr("recvfrom() function failed.");
            exit(1);
        }
    }

    // Output message (Will create soon)

    printf("");
    printf("\nData was received from the server. Press enter to continue...");
    getchar();

    if (closesocket(sock) != 0) {
        DisplayFatalErr("closesocket() function failed.");
    }
    printf("socket closed successfully. Press enter to continue...");
    getchar();

    if (WSACleanup() != 0) {
        DisplayFatalErr("WSACleanup() function failed.");
    }

    exit(0);
}

Server.c

void main(int argc, char* argv[])   // argc is # of strings following command, argv[] is array of ptrs to the strings
{
    WSADATA wsaData;                // contains details about WinSock DLL implementation
    struct sockaddr_in6 serverInfo; // standard IPv6 structure that holds server socket info
    int serverPort, clientSock, rcvLen, fromSize;
    char* rcvBuffer[RCVBUFSIZ];
    serverPort = 0;

    // Verify correct number of command line arguments
    if (argc != 2) {
        serverPort = DEFAULT_PORT;
    }
    else {
        serverPort = atoi(argv[1]);
    }

    // Initialize Winsock 2.0 DLL
    if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
        // Failed
        printf("Couldn't initialize Winsock 2.0 DLL");
        exit(1);
    }

    // Create an IPv6 UDP stream socket.
    int sock;
    sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == INVALID_SOCKET) {
        DisplayFatalErr("socket() function failed.");
        exit(1);
    }

    // Don't forget any necessary format conversions.
    memset(&serverInfo, 0, sizeof(serverInfo));
    serverInfo.sin6_family = AF_INET6;
    serverInfo.sin6_port = htons(serverPort);
    serverInfo.sin6_addr = in6addr_any;
    //inet_pton(AF_INET6, serverIPaddr, &serverInfo.sin6_addr);

    // Bind the server socket to the sockadder structure.
    if (bind(sock, (struct sockadder*) & serverInfo, sizeof(serverInfo)) == SOCKET_ERROR) {
        DisplayFatalErr("bind() function failed.");
        exit(1);
    }

    printf("ST's IPv6 echo server is ready for client connection on port: %i\r\n", serverPort);


    // Forever Loop waiting for messages
    for (;;) {
        struct sockaddr_in6 clientInfo; // Hold client port & adder recvfrom
        memset(&clientInfo, 0, sizeof(clientInfo));
        clientInfo.sin6_family = AF_INET6;

        fromSize = sizeof(clientInfo);
        if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo.sin6_addr, &fromSize)) < 0) { //THIS IS WHERE I BELIEVE THE ERROR TO BE. WHEN LOOKING AT THE ADDRESS ON THE DEBUGGER IT DOESN'T MATCH THE ONE I SENT USING THE CLIENT
            DisplayFatalErr("recvfrom() function failed.");
        }

        // Processing Socket
        char* clientAddr[INET6_ADDRSTRLEN];
        inet_ntop(AF_INET6, &(clientInfo.sin6_addr), clientAddr, INET6_ADDRSTRLEN);
        int clientPort = ntohs(clientInfo.sin6_port);

        printf("Processing the client at %s, client port %i, server port %i.\r\n", clientAddr, clientPort, serverPort);
        printf("Message Received: %s\r\n", rcvBuffer);
        // Send data back
        if (sendto(sock, rcvBuffer, rcvLen, 0, (struct sockaddr*) & clientInfo, sizeof(clientInfo)) != rcvLen) {
            DisplayFatalErr("sendto() function failed.");
        }
    }
}

Дайте мне знать, если вам понадобится, чтобы я предоставил любую другую информацию.

Ответы [ 2 ]

3 голосов
/ 17 октября 2019

Вы правы, проблема здесь:

if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo.sin6_addr, &fromSize)) < 0) {
//                                                                                   ^^^^^^^^^^

Вы не передаете sockaddr *, вы передаете struct in6_addr *. Просто бросьте .sin6_addr часть:

if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo, &fromSize)) < 0) {
1 голос
/ 17 октября 2019

Когда сервер вызывает recvfrom(), вы передаете ему in6_addr* вместо sockaddr_in6* в параметре from. Вам нужно передать указатель на все sockaddr_in6, а не указатель только на его sin6_addr поле:

recvfrom(..., (struct sockaddr*) &clientInfo, ...) // <-- NOT &clientInfo.sin6_addr!

Есть и другие проблемы на сервере.

Как только сервер получил сообщение, он вызывает inet_ntop(), чтобы преобразовать исходный IP-адрес клиента в строку. Однако он передается в массиве char*[], но вместо этого ему необходимо передать массив char[]:

char clientAddr[INET6_ADDRSTRLEN]; // <-- char, NOT char*!
inet_ntop(AF_INET6, &(clientInfo.sin6_addr), clientAddr, INET6_ADDRSTRLEN);

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

printf("Message Received: %.*s\r\n", rcvLen, rcvBuffer);

И при вызове sendto() для отправки сообщения клиенту, вы должны использовать фактический fromSize, сообщенный recvfrom() вместо использования sizeof(clientInfo) для размера адреса клиента:

sendto(..., (struct sockaddr*) &clientInfo, fromSize)
...