C语言写的UDP客户端收不到包

C-language written UDP client doesn't receive a packet

我正在用 C 写一个客户端-服务器程序。我无法处理这个问题:客户端向服务器发送消息,他收到消息并且一切正常,但是当服务器向客户端发送消息时,它不会捕获 UDP 数据包。服务器成功完成,而客户端一直在等待消息但没有收到消息。这是代码。

udpClient.c:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdlib.h>

#define BUF_SIZE 1024
#define BAD_EXIT_STATUS 1

void throw(int code, char * message) {
    printf("%s\n", message);
    exit(code);
}

char * readline(FILE * input) {
    int size = 64;
    char * line = malloc(size * sizeof(char));
    char currentString[64];

    while (1) {
        fgets(currentString, sizeof(currentString), input);

        if (strstr(currentString, "\n")) {
            strcat(line, currentString);
            break;
        } else {
            size += 64;
            line = realloc(line, size * sizeof(char));
            strcat(line, currentString);
        }
    }
    return line;
}

typedef struct Args {
    char* IP;       // -a IP
    int port;       // -p port
} Args;

Args get_args(int argc, char **argv) {
    Args args;
    char * addr;
    char * port;
    if ((addr = getenv("L2ADDR"))) {
        args.IP = addr;
    } else {
        args.IP = "127.0.0.1";
    }
    if ((port = getenv("L2PORT"))) {
        args.port = atoi(port);
    } else {
        args.port = 1234;
    }

    int opt;
    while((opt = getopt(argc, argv, "a:p:vh")) != -1) {
        switch(opt) {
            case 'a':
                args.IP = optarg;
                break;
            case 'p':
                args.port = atoi(optarg);
                break;
            case 'v':
                printf("Lab2Server beta v.0.1.0\n");
                exit(0);
            case 'h':
                printf("You can use: \n"
                       "\t-a [string] -- Sets ip address;\n"
                       "\t-p [string] -- Sets port;\n"
                       "\t-v -- Shows a version.\n");
                exit(0);
            default:
                throw(BAD_EXIT_STATUS, "Use key -h to get some help.\n");
        }
    }

    return args;
}


int main(int argc, char **argv){
    Args args = get_args(argc, argv);
    
    char c;
    bool a = true;
    
    int sockfd;
    struct sockaddr_in serverAddr;
    
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        throw(BAD_EXIT_STATUS, "Socket creation failed");
    }
    
    
    memset(&serverAddr, 0, sizeof(serverAddr));

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(args.port);
    serverAddr.sin_addr.s_addr = inet_addr(args.IP);
    char buffer[BUF_SIZE];
        printf("[Client]: ");
        char *request = readline(stdin);
        int n;
        socklen_t len;
    
        sendto(sockfd, (const char *) request, strlen(request), 0, (const struct sockaddr*)&serverAddr, sizeof(serverAddr));
        printf("[+]Data Send: %s", buffer);
        n = recvfrom(sockfd, (char *) buffer, BUF_SIZE, 0, (struct sockaddr *) &serverAddr, &len);
        printf("[Server]: %s", buffer);
    close(sockfd);
    return 0;

}

udpServer.c:

#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <math.h>
#include "utils.h"

#define BUF_SIZE 1024
#define ERROR_N_IS_BIG 1
#define ERROR_T_LESS_THAN_F 2
#define ERROR_N_IS_0 3
#define ERROR_NOT_ENOUGH_NUMBERS 4


int ds_size;
double *ds;

char *createResponse(char *buffer) {
    return buffer;
}

int main(int argc, char **argv){
    
    Args args = get_args(argc, argv); //прием аргов
    FILE *output = fopen(args.logFile, "w+"); // запись логов
    
    int sockfd; //для сокета
    struct sockaddr_in serveraddr, cliaddr; //сокеты сервера и клиента
    bool a = true;
    
    //обработка ошибки создания сокета
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        throw(BAD_EXIT_STATUS, "Socket creation failed");
    }
    

    memset(&serveraddr, 0, sizeof(serveraddr)); //выделение памяти под СС
    memset(&cliaddr, 0, sizeof(cliaddr)); // память под СК
    
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(args.port);
    serveraddr.sin_addr.s_addr = inet_addr(args.IP);
    
    if (bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
        throw(BAD_EXIT_STATUS, "Bind failed");
    }
    
    if (args.isDaemon) {
        pid_t process_id = 0;

        process_id = fork();
        if (process_id < 0) {
            throw(BAD_EXIT_STATUS, "Fork failed!\n");
        }
        if (process_id > 0) {
            printf("Server started with pid %d\n", process_id);
            exit(0);
        }

        umask(0);
        chdir("/");
        if (setsid() < 0) {
            throw(BAD_EXIT_STATUS, "Error on setsid()");
        }

        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
    }
    int n;
    socklen_t len;
    char buffer[BUF_SIZE];
    n = recvfrom(sockfd, (char *)buffer, BUF_SIZE, MSG_WAITALL, (struct sockaddr*)& cliaddr, &len);
    printf("[+]Data Received: %s", buffer);
    char *hello = "Hello from server";
    sendto(sockfd, (const char *) hello, strlen(hello), 0, (const struct sockaddr*)&cliaddr, sizeof(cliaddr));
    printf("Hello message sent.\n");


    return 0;
    
}

我还使用了下一个文件来制作程序 运行 和密钥

Utils.c

#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "utils.h"

void throw(int code, char * message) {
    printf("%s\n", message);
    exit(code);
}

Args get_args(int argc, char **argv) {
    Args args;
    char * wait;
    char * addr;
    char * port;
    char * logfile;
    if ((wait = getenv("L2WAIT"))) {
        args.waitFor = atoi(wait);
    } else {
        args.waitFor = 0;
    }
    if ((addr = getenv("L2ADDR"))) {
        args.IP = addr;
    } else {
        args.IP = "127.0.0.1";
    }
    if ((port = getenv("L2PORT"))) {
        args.port = atoi(port);
    } else {
        args.port = 1234;
    }
    if ((logfile = getenv("L2LOGFILE"))) {
        args.logFile = logfile;
    } else {
        args.logFile = "/tmp/lab2.log";
    }
    args.isDaemon = false;

    int opt;
    while((opt = getopt(argc, argv, "w:dl:a:p:vh")) != -1) {
        switch(opt) {
            case 'w':
                args.waitFor = atoi(optarg);
                break;
            case 'd':
                args.isDaemon = true;
                break;
            case 'l':
                args.logFile = optarg;
                break;
            case 'a':
                args.IP = optarg;
                break;
            case 'p':
                args.port = atoi(optarg);
                break;
            case 'v':
                printf("Lab2Server beta v.0.1.0\n");
                exit(0);
            case 'h':
                printf("You can use: \n"
                   "\t-w [int] -- Sets the delay;\n"
                   "\t-d -- Starts as daemon;\n"
                   "\t-a [string] -- Sets ip address;\n"
                   "\t-p [string] -- Sets port;\n"
                   "\t-l [string] -- Sets log file;\n"
                   "\t-v -- Shows a version.\n");
                exit(0);
            default:
                throw(BAD_EXIT_STATUS, "Use key -h to get some help.\n");
        }
    }

    return args;
}

double random_double(double min, double max) {
    return (double)rand() / RAND_MAX * (max - min) + min;
}

void logMessage(FILE * output, struct sockaddr_in client_addr, char * message) {
    time_t t = time(NULL);
    char * ip = inet_ntoa(client_addr.sin_addr);
    struct tm tm = *localtime(&t);
    fprintf(output, "[%d-%02d-%02d %02d:%02d:%02d | %s]: %s", tm.tm_year + 1900, tm.tm_mon + 1,
            tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ip, message);
    fflush(output);
}

Utils.h

#include <netinet/in.h>

#ifndef UTILS_H
#define UTILS_H

#define BAD_EXIT_STATUS 1

typedef struct Args {
    int waitFor;    // -w N
    bool isDaemon;  // -d
    char* logFile;  // -l path
    char* IP;       // -a IP
    int port;       // -p port
} Args;

Args get_args(int, char**);

void throw(int, char*);

double random_double(double, double);

void logMessage(FILE *, struct sockaddr_in, char *);

#endif

这些程序是使用 Make 文件编译的:

.PHONY: build clean

udpServer: udpServer.o utils.o
    gcc -o udpServer udpServer.o utils.o -lm

udpClient: udpClient.o
    gcc -o udpClient udpClient.o

udpServer.o: udpServer.c
    gcc -c -o udpServer.o udpServer.c -lm

udpClient.o: udpClient.c
    gcc -c -o udpClient.o udpClient.c

utils.o: utils.c
    gcc -c -o utils.o utils.c

clean:
    rm -rf *.o

build:
    make udpServer
    make udpClient
    make clean

所以当你完成文件后,你需要 运行 make build 来编译一个程序,然后你 运行 一个带有命令 ./udpServer -a 127.0.0.1 -p 1234 的服务器(在“ -a" 你可以 post 你想要的任何 IP 地址,在 "-p" 之后你 post 你想要的任何端口)并且在一个新的 window 你 运行 一个客户端使用命令./udpClient -a 127.0.0.1 -p 1234(在“-a”之后你可以post你想要的任何IP地址,在“-p”之后你可以post你想要的任何端口)。

在文件 udpServer.c 中,函数 sendto() 使用了无效的 cliaddr。为了获得正确的值,我们在调用 recvfrom().

之前初始化 len
socklen_t len = sizeof(struct sockaddr);
recvfrom(sockfd, (char *)buffer, BUF_SIZE, MSG_WAITALL, (struct sockaddr*)& cliaddr, &len);

recvfrom 的手册推荐

The argument addrlen is a value-result argument, which the caller should initialize before the call to the size of the buffer associated with src_addr and modified on return to indicate the actual size of the source address.