You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言UDP Socket多线程文件传输:服务端无法识别success消息

UDP Socket文件传输校验和匹配失败问题排查

问题概述

用C语言实现基于UDP Socket的服务端与客户端程序,支持服务端向客户端发送图片文件、多线程处理、校验和传输错误检测。文件传输功能正常,但校验和正确(传输成功)时,客户端发送"success"消息,服务端收到的是"success"附加二进制内容的消息,导致strcmp匹配失败,循环无法退出,同时多会话功能受此问题影响。

服务端代码(Server.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <unistd.h>

#define PORT 8888
#define BUF_SIZE 1254
#define MAX_CLIENTS 100

typedef struct
{
    struct sockaddr_in address;
    int socket;
    int id;
} Client;

Client clients[MAX_CLIENTS];
pthread_t threads[MAX_CLIENTS];
int clientCount = 0;
pthread_mutex_t mutex;

unsigned short calculateChecksum(FILE *file)
{
    fseek(file, 0, SEEK_SET);
    unsigned short checksum = 0;
    int ch;
    while ((ch = fgetc(file)) != EOF)
    {
        checksum += (unsigned char)ch; 
    }

    fseek(file, 0, SEEK_SET);
    return checksum;
}

void *handleClient(void *arg)
{
    int id = *((int *)arg);
    ssize_t bytesRead;

    // video file
    FILE *file;
    char *filename = "image.jpg";
    // open video file
    file = fopen(filename, "rb");
    if (file == NULL)
    {
        perror("File opening failed");
        exit(EXIT_FAILURE);
    }
    char buf[BUF_SIZE];
    ssize_t bytes_read;
    // check file size
    fseek(file, 0, SEEK_END);
    size_t file_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    // define loop count
    size_t loopcount = file_size / BUF_SIZE;
    // what is the residue
    size_t residue = file_size % BUF_SIZE;
    char buffer[BUF_SIZE];
    int i=0;
    pthread_mutex_lock(&mutex);
    strcpy(buffer,"retry");
    while (1)
    {   
        //problem 
        printf("received message: %s\n", buffer);
        if (!strcmp(buffer, "success"))
        {
            break;
        }
        else if(!strcmp(buffer, "retry"))
        {   printf("sending videofile\n");
            // data sending
            for (int i = 0; i < loopcount; i++)
            {

                size_t bytesread = fread(buffer, 1, BUF_SIZE, file);

                sendto(clients[id].socket, buffer, BUF_SIZE, 0, (struct sockaddr *)&clients[id].address, sizeof(clients[id].address));
            }

            // residue data sending
            size_t bytesread = fread(buffer, 1, residue, file);
            sendto(clients[id].socket, buffer, residue, 0, (struct sockaddr *)&clients[id].address, sizeof(clients[id].address));

            printf("sending video file completed\n");
            char fileFinMessage[] = "fin";

            if (sendto(clients[id].socket, fileFinMessage, sizeof(fileFinMessage), 0, (struct sockaddr *)&clients[id].address, sizeof(clients[id].address)) < 0)
            {
                perror("sendto() failed");
                exit(EXIT_FAILURE);
            }
            // sending checksum
            unsigned short checksum = calculateChecksum(file);
            sprintf(buffer, "%d", checksum);
            printf("checksum is %d \n", checksum);
            sendto(clients[id].socket, buffer, BUF_SIZE, 0, (struct sockaddr *)&clients[id].address, sizeof(clients[id].address));

        }
        //is data transfer has not error?
        if(recvfrom(clients[id].socket, buffer, sizeof(buffer), 0, NULL, NULL)==-1){
            perror("recvfrom() failed");
            exit(EXIT_FAILURE);
        }
    }

    fclose(file);
    pthread_mutex_lock(&mutex);
    return 0;
}

int main()
{
    int serv_sock;
    struct sockaddr_in serv_adr;
    char message[BUF_SIZE];
    char buf[BUF_SIZE];
    socklen_t clnt_adr_sz = sizeof(struct sockaddr_in);
    ssize_t str_len;

    // Create socket
    if ((serv_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // socket address
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = INADDR_ANY;
    serv_adr.sin_port = htons(PORT);

    // binding socket
    if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
    {
        perror("bind() error");
        exit(1);
    }

    // mutex Init
    pthread_mutex_init(&mutex, NULL);

    printf("waiting for client...\n");

    // Create a thraed for each client
    while (1)
    {
        // receive message from client
        str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr *)&clients[clientCount].address, &clnt_adr_sz);
        if (str_len < 0)
        {
            perror("recvfrom error");
            exit(1);
        }

        // print message
        message[str_len] = '\0';
        printf("client %d say: %s\n", clientCount + 1, message);

        // Add client to the list
        clients[clientCount].socket = serv_sock;
        clients[clientCount].id = clientCount;
        
        clientCount++;
        // send a message to the client
        sprintf(message, "hello i am server, you are Client %d\n", clientCount);
        if (sendto(clients[clientCount - 1].socket, message, strlen(message), 0, (struct sockaddr *)&clients[clientCount - 1].address, sizeof(clients[clientCount - 1].address)) < 0)
        {
            perror("sendto error");
            exit(1);
        }
        // create thread for client
        pthread_create(&threads[clientCount+1], NULL, handleClient, (void *)&clients[clientCount+1].id);
        pthread_join(threads[clientCount+1], NULL);
    }

    close(serv_sock);
    pthread_mutex_destroy(&mutex);
    return 0;
}

客户端代码(Client.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>

#define BUFSIZE 1254
#define PORT 8888

unsigned short calculateChecksum(FILE *file)
{
    fseek(file, 0, SEEK_SET);
    unsigned short checksum = 0;
    int ch;

    while ((ch = fgetc(file)) != EOF)
    {
        checksum += (unsigned char)ch; 
    }

    fseek(file, 0, SEEK_SET);
    return checksum;
}

void fileRecive(int sock, struct sockaddr_in *serv_addr, socklen_t adr_sz)
{
    FILE *file = fopen("received_image.jpg", "wb");
    if (file == NULL)
    {
        perror("File opening failed");
        exit(EXIT_FAILURE);
    }

    char message[BUFSIZE];
    size_t nbyte = BUFSIZE;
    while (1)
    {
        nbyte = recvfrom(sock, message, nbyte, 0, (struct sockaddr *)serv_addr, &adr_sz);
        if (!strcmp(message, "fin"))
        {
            printf("File received successfully.\n");
            break;
        }
        fwrite(message, nbyte, 1, file);
    }

    fclose(file);
}

int main()
{
    int sock;
    char message[BUFSIZE];
    int str_len;
    socklen_t adr_sz;

    struct sockaddr_in serv_addr, client_addr;

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1)
    {
        printf("socket() error\n");
        exit(1);
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(PORT);

    char hello_message[] = "hello i am client";
    sendto(sock, hello_message, strlen(hello_message), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    adr_sz = sizeof(client_addr);
    str_len = recvfrom(sock, message, BUFSIZE, 0, (struct sockaddr *)&client_addr, &adr_sz);

    message[str_len] = '\0';
    printf("Server say: %s\n", message);

    fileRecive(sock, &serv_addr, adr_sz);

    FILE *file2;
    char *filename = "received_image.jpg";
    
    if (file2 == NULL)
    {
        perror("File opening failed");
        exit(EXIT_FAILURE);
    }
    // checksum 검사
    recvfrom(sock, message, BUFSIZE, 0, (struct sockaddr *)&client_addr, &adr_sz);
    while (1)
    {   
        file2 = fopen(filename, "rb");
        printf("received checksum = %s\n", message);
        unsigned short checksum = calculateChecksum(file2);
        printf("calcualte checksum = %d\n", checksum);
        char checksumCheck[BUFSIZE];
        sprintf(checksumCheck, "%d", checksum);
        if (!strcmp(message, checksumCheck))
        {   
            printf("checksum correct\n");
            //problem
            sprintf(message, "success");
            printf("%s\n", message);
            sendto(sock, message, strlen(message), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
            fclose(file2);
            break;
        }
        else
        {
            printf("checksum incorrect");
            printf("\n");
            char retry_message[BUFSIZE] = "retry";
            sendto(sock, retry_message, strlen(retry_message), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
            fileRecive(sock, &serv_addr, adr_sz);
        }
    }

    close(sock);

    return 0;
}

服务端异常输出

waiting for client...
client 1 say: hello i am client
received message: retry
sending videofile
sending video file completed
checksum is 7317
received message: successn;-�L=n��="��}�K+�#S�l����'A6=�MH�dC+ܐ�D�܏0)j��<YF�%0�;�j��'R�n
��W�|�l�6WL�.ʜ�ȫP��#�P��!<����2��PBB�9Cq5^�M`n�6D��Z��5HM�-

sbW&�����#iT���_ȝٶ�b�z�*l���d7vȆ�Cs��s�5�ڎɾɘfX�&�̘���/�C":
                                                          �r4��d&��ۈ�yq�2�+6xѡt;A���%A*bI��5y�5AO?�ND�N��c\�2ƹ1��ف��nbI�JOcس\I��xVlic%�lr{Ѥ�!L��q�}����%ثF݉��ϧ��BT8D����0�؆���'��m[��H��#s��Hod�541��,��O��

问题根源与修复方案

1. 核心问题:字符串未正确终止

服务端在recvfrom接收客户端消息后,没有手动添加字符串结束符'\0'。由于之前buffer存储过二进制文件内容,残留的二进制数据会被当成字符串的一部分,导致strcmp匹配"success"时失败。

修复代码(服务端handleClient函数中):

// 替换原有的recvfrom代码
ssize_t recv_len = recvfrom(clients[id].socket, buffer, sizeof(buffer)-1, 0, NULL, NULL);
if(recv_len == -1){
    perror("recvfrom() failed");
    exit(EXIT_FAILURE);
}
buffer[recv_len] = '\0'; // 强制添加字符串结束符,截断残留数据

注意使用sizeof(buffer)-1作为接收长度,避免写入'\0'时越界。

2. 校验和发送冗余数据

服务端发送校验和时,使用BUF_SIZE作为发送长度,导致客户端接收的message缓冲区中残留之前的二进制数据。应发送校验和字符串的实际长度。

修复代码(服务端发送校验和部分):

sprintf(buffer, "%d", checksum);
printf("checksum is %d \n", checksum);
// 发送实际字符串长度(包含'\0')
sendto(clients[id].socket, buffer, strlen(buffer)+1, 0, (struct sockaddr *)&clients[id].address, sizeof(clients[id].address));

3. 多线程处理逻辑错误

  • 线程创建时索引越界:threads[clientCount+1]和clients[clientCount+1].id会超出数组范围,应使用clientCount-1(因为clientCount刚完成自增)。
  • pthread_join放在主循环中会阻塞主线程,导致无法同时处理多个客户端,需移除该行,后续可通过线程回收机制处理结束的线程。

修复代码(服务端main函数中):

// 替换原线程创建代码
pthread_create(&threads[clientCount-1], NULL, handleClient, (void *)&clients[clientCount-1].id);
// 注释掉pthread_join,避免阻塞主线程
// pthread_join(threads[clientCount-1], NULL);

4. 客户端"fin"判断存在越界风险

客户端fileRecive函数中使用strcmp(message, "fin")判断文件传输结束,但message存储二进制数据时可能没有'\0',会导致strcmp越界访问内存。

修复代码(客户端fileRecive函数中):

nbyte = recvfrom(sock, message, nbyte, 0, (struct sockaddr *)serv_addr, &adr_sz);
// 比较前3个字节判断是否为"fin",同时确保接收长度足够
if (nbyte >= 3 && strncmp(message, "fin", 3) == 0) {
    printf("File received successfully.\n");
    break;
}

5. 客户端野指针错误

客户端中FILE *file2;未初始化就进行if(file2 == NULL)判断,属于野指针操作,会导致错误的文件状态判断。

修复代码(客户端main函数中):

// 直接初始化file2,再判断是否打开成功
FILE *file2 = fopen(filename, "rb");
if (file2 == NULL)
{
    perror("File opening failed");
    exit(EXIT_FAILURE);
}

内容的提问来源于stack exchange,提问作者user24723398

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:45:53