用C语言编写的UDP客户端未收到数据包

时间:2019-12-12 06:53:23

标签: c server udp client 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-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

因此,完成文件后,您需要运行make build来编译程序,然后使用命令./udpServer -a 127.0.0.1 -p 1234运行服务器(在“ -a”之后,您可以发布任何IP地址您想要的,在“ -p”之后发布所需的任何端口),然后在新窗口中运行带有命令./udpClient -a 127.0.0.1 -p 1234的客户端(“ -a”之后可以发布所需的任何IP地址,而在“”之后-p“,则可以发布所需的任何端口)

请帮我解决这个问题。...我在这里回答您有关我程序的所有问题。非常感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

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

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

recvfrom的手册建议

  

自变量 addrlen 是一个值结果自变量,调用方应在调用之前将其初始化 到与之关联的缓冲区大小src_addr,并在返回时进行修改以指示源地址的实际大小。