Klien UDP tertulis bahasa C tidak menerima paket

Selamat tinggal! Saya sedang menulis program klien-server di C. Saya tidak dapat menangani masalah ini: Klien mengirim pesan ke server, dia menerimanya dan semuanya baik-baik saja, tetapi ketika Server mengirim pesan ke Klien, pesan itu tidak tertangkap paket UDP. Server selesai dengan sukses, sementara Klien terus menunggu pesan dan tidak menerimanya. Ini kodenya.

udpKlien.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;

}

Saya juga menggunakan file berikutnya untuk membuat program berjalan dengan kunci

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

Program-program ini dikompilasi menggunakan Make-file:

.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

Jadi ketika Anda menyelesaikan file, Anda perlu menjalankan make build untuk membuat program dikompilasi dan kemudian Anda menjalankan server dengan perintah ./udpServer -a 127.0.0.1 -p 1234 (setelah "-a" Anda dapat memposting alamat IP apa pun yang Anda inginkan dan setelah "-p" Anda memposting apa saja port yang Anda inginkan) dan di jendela baru Anda menjalankan Klien dengan perintah ./udpClient -a 127.0.0.1 -p 1234 (setelah "-a" Anda dapat memposting alamat IP apa pun yang Anda inginkan dan setelah "-p" Anda memposting port apa pun yang Anda inginkan)

Tolong, bantu saya mengatasi masalah ini.... Saya di sini untuk menjawab semua pertanyaan Anda tentang program saya. Terima kasih banyak atas bantuannya!


person Марк Эскола    schedule 12.12.2019    source sumber
comment
apakah pengirim dan penerima berjalan di host yang sama?   -  person dvhh    schedule 12.12.2019
comment
Tentu saja! Saya menggunakan port dan alamat IP yang sama   -  person Марк Эскола    schedule 12.12.2019
comment
Tidak yakin ada bedanya, tapi coba gunakan len alih-alih sizeof(cliaddr) dalam panggilan sendto() server.   -  person Barmar    schedule 12.12.2019
comment
Anda tidak mengirimkan terminator nol, dan recvfrom() tidak mengisi buffer dengan nol, jadi Anda tidak dapat mencetak buffer dengan format %s.   -  person Barmar    schedule 12.12.2019
comment
Terima kasih atas jawabannya! Saya akan mencobanya!   -  person Марк Эскола    schedule 13.12.2019


Jawaban (1)


Dalam file udpServer.c, fungsi sendto() menggunakan cliaddr yang tidak valid. Untuk mendapatkan nilai yang tepat, kita menginisialisasi len sebelum memanggil recvfrom().

socklen_t len = sizeof(struct sockaddr);
recvfrom(sockfd, (char *)buffer, BUF_SIZE, MSG_WAITALL, (struct sockaddr*)& cliaddr, &len);

Manual recvfrom merekomendasikan hal itu

Argumen addrlen adalah argumen nilai-hasil, yang harus diinisialisasi oleh pemanggil sebelum panggilan ke ukuran buffer yang terkait dengan src_addr dan dimodifikasi sebagai imbalannya untuk menunjukkan ukuran sebenarnya dari alamat sumber.

person Thach Pham    schedule 12.12.2019
comment
Oh terima kasih sudah berhasil tapi tidak perlu meletakkan & sebelum len (taruh di baris kedua). Saya menghapusnya dan berhasil. Terima kasih banyak! - person Марк Эскола; 15.12.2019