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

Posted

技术标签:

【中文标题】C语言写的UDP客户端收不到包【英文标题】:C-language written UDP client doesn't receive a packet 【发布时间】:2020-04-05 12:37:47 【问题描述】:

我正在用 C 编写一个客户端-服务器程序。我无法处理这个问题:客户端向服务器发送一条消息,他得到它并且一切正常,但是当服务器向客户端发送一条消息时,它没有't catch 一个 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”之后你可以发布任何你想要的端口)。

【问题讨论】:

发送方和接收方是否在同一台主机上运行? 当然!我使用相同的端口和 IP 地址 不确定是否有区别,但请尝试在服务器的sendto() 调用中使用len 而不是sizeof(cliaddr) 您没有发送空终止符,并且recvfrom() 不会用空值填充缓冲区,因此您无法使用%s 格式打印buffer 谢谢大家的回答!我会试试的! 【参考方案1】:

在文件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的手册建议

addrlen 参数是一个值结果参数,调用者应该初始化 在调用之前 到与之关联的缓冲区的大小src_addr 并在返回时修改以指示源地址的实际大小。

【讨论】:

哦,谢谢它的工作,但它没有必要把 & 放在 len 之前(你把它放在第二行)。我擦掉了它,它起作用了。非常感谢!

以上是关于C语言写的UDP客户端收不到包的主要内容,如果未能解决你的问题,请参考以下文章

udp为啥收不到广播中的数据,该怎么处理

如果启用了环回,为啥发送方收不到它的多播 UDP 数据包?

C语言 UDP socket 简单客户端 编程,急

发送的 Scapy 数据包收不到

TCP

C#使用UDP协议开发服务端遇到的问题~~