UDP-клиент подключается к recvfrom только в определенной ситуации

#c #sockets

#c #сокеты

Вопрос:

Я пишу код на C , который реализует UDP-сервер и клиент.

Код работает нормально, когда я пишу два кода, один для сервера, а другой для клиента, как в этом примере:https://www.geeksforgeeks.org/udp-server-client-implementation-c / .

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

Проблема в том, что, реализуя этот способ и тестируя на двух терминалах, выполняющих один и тот же код, с разными аргументами командной строки, один для сервера, а другой для клиента, клиент подключается к recvfrom при получении ответа сервера.

 #include <unistd.h> 
#include <stdio.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <netinet/in.h> 
#include <string.h> 
#include <arpa/inet.h>
#include <unistd.h>
#define MAXLINE 1024
#define PORT 32000

int send(){
    int sockfd; 
    char buffer[MAXLINE]; 
    char *hello = "Hello from server"; 
    struct sockaddr_in servaddr, cliaddr; 
    
    // Creating socket file descriptor 
    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
        perror("socket creation failed"); 
        exit(EXIT_FAILURE); 
    } 
    
    memset(amp;servaddr, 0, sizeof(servaddr)); 
    memset(amp;cliaddr, 0, sizeof(cliaddr)); 
    
    // Filling server information 
    servaddr.sin_family = AF_INET; // IPv4 
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    servaddr.sin_port = htons(PORT); 
    
    // Bind the socket with the server address 
    if ( bind(sockfd, (const struct sockaddr *)amp;servaddr, 
            sizeof(servaddr)) < 0 ) 
    { 
        perror("bind failed"); 
        exit(EXIT_FAILURE); 
    } 
    
    socklen_t len;
    int n; 
    n = recvfrom(sockfd, (char *)buffer, MAXLINE, 
                MSG_WAITALL, ( struct sockaddr *) amp;cliaddr, 
                amp;len); 
    buffer[n] = ''; 
    printf("Client : %sn", buffer); 
    sendto(sockfd, (const char *)hello, strlen(hello), 
        MSG_CONFIRM, (const struct sockaddr *) amp;cliaddr, 
            len); 
    printf("Hello message sent.n"); 

    return 0; 
}

int receive(){
    int sockfd; 
    char buffer[MAXLINE]; 
    char *hello = "Hello from client"; 
    struct sockaddr_in   servaddr; 

    // Creating socket file descriptor 
    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
        perror("socket creation failed"); 
        exit(EXIT_FAILURE); 
    } 

    memset(amp;servaddr, 0, sizeof(servaddr)); 
    
    // Filling server information 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_port = htons(PORT); 
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    
    int n;
    socklen_t len; 
    
    sendto(sockfd, (const char *)hello, strlen(hello), 
        MSG_CONFIRM, (const struct sockaddr *) amp;servaddr, 
            sizeof(servaddr)); 
    printf("Hello message sent.n"); 
        
    n = recvfrom(sockfd, (char *)buffer, MAXLINE, 
                MSG_WAITALL, (struct sockaddr *) amp;servaddr, 
                amp;len); 
    buffer[n] = ''; 
    printf("Server : %sn", buffer); 

    return 0; 
}

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

    int command = atoi(argv[1]);

    if(command == 0){
        send();
        
    }
    if(command == 1){
        receive();
    }


    return 0;
}
  

Ожидаемые результаты примерно такие, которые я получаю при запуске клиента и сервера с разделенными кодами:

На стороне сервера:

Привет от клиента

Отправлено приветственное сообщение

На стороне клиента:

Отправлено приветственное сообщение

Привет с сервера

Но что я получаю при запуске приведенного выше кода, так это

На стороне сервера:

Привет от клиента

Отправлено приветственное сообщение

На стороне клиента:

Отправлено приветственное сообщение

—застрял здесь—

Что я делаю не так?

Ответ №1:

В вашей send() функции вы не инициализируете len длину буфера, в котором recvfrom может храниться адрес клиента.

Согласно справочной странице для recvfrom :

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

Если src_addr не равен нулю, и базовый протокол предоставляет адрес источника сообщения, этот адрес источника помещается в буфер, на который указывает src_addr. В этом случае addrlen является аргументом значения-результата. Перед вызовом он должен быть инициализирован размером буфера, связанного с src_addr. При возврате addrlen обновляется, чтобы содержать фактический размер исходного адреса.

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

 socklen_t len = sizeof(cliaddr); // The size of the buffer you're passing to store the client address
  

Комментарии:

1. Только один вопрос: в моем клиенте я не должен выполнять эту инициализацию?

2. Ах да, вы также должны сделать это в своей receive() функции. Это не приводило к сбою вашей тестовой программы, но могло вызвать другие проблемы. Оставление len неинициализированным может привести к повреждению памяти, если в конечном итоге значение окажется больше sizeof(serveaddr)