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

如何在C语言Socket中单次发送文本与二进制混合消息?

在C语言中构建包含文本与二进制的Socket复合消息

1. 明确消息结构规范

你需要的复合消息分为两部分:

  • 文本头部:以键值对形式携带messageId和messageLength,末尾用两个连续换行作为头部与二进制数据的分隔符
  • 二进制数据段:图片的原始字节流,长度由头部的messageLength指定

这种结构既保证头部的可读性,又能通过明确的长度值避免二进制数据(可能包含换行等特殊字节)与头部混淆。

2. 发送端:构建并发送复合消息

核心思路是先拼接文本头部,再将二进制数据追加到头部之后,最后一次性发送完整缓冲区(或循环发送直到全部数据发出)。

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

#define MAX_BUFFER (1024 * 1024) // 按需调整,确保能容纳头部+图片数据

int main() {
    // 替换为实际已建立连接的socket描述符
    int sockfd = ...;

    // 1. 准备头部信息
    int messageId = 12;
    int imgDataLen = 34; // 实际应替换为图片文件的真实字节长度
    char header[256];
    snprintf(header, sizeof(header), "messageId: %d, messageLength: %d\n\n", messageId, imgDataLen);
    int headerLen = strlen(header);

    // 2. 读取图片二进制数据(示例:模拟读取,实际用fread从文件获取)
    unsigned char* imgData = malloc(imgDataLen);
    if (!imgData) {
        perror("malloc failed");
        return -1;
    }
    memset(imgData, 0x00, imgDataLen); // 模拟填充图片数据

    // 3. 构建完整消息缓冲区
    unsigned char* fullMsg = malloc(headerLen + imgDataLen);
    if (!fullMsg) {
        perror("malloc failed");
        free(imgData);
        return -1;
    }
    memcpy(fullMsg, header, headerLen);
    memcpy(fullMsg + headerLen, imgData, imgDataLen);

    // 4. 发送完整消息(循环确保全部数据发出)
    ssize_t totalSent = 0;
    size_t totalSize = headerLen + imgDataLen;
    while (totalSent < totalSize) {
        ssize_t sent = send(sockfd, fullMsg + totalSent, totalSize - totalSent, 0);
        if (sent == -1) {
            perror("send failed");
            break;
        }
        totalSent += sent;
    }
    printf("Sent %zd bytes total\n", totalSent);

    // 清理资源
    free(imgData);
    free(fullMsg);
    close(sockfd);
    return 0;
}

3. 接收端:解析复合消息

接收端需要先读取头部直到找到分隔符,解析出messageLength后,再精准读取对应长度的二进制数据。

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

#define HEADER_BUF_SIZE 1024

int main() {
    // 替换为实际已建立连接的socket描述符
    int sockfd = ...;
    char headerBuf[HEADER_BUF_SIZE] = {0};
    ssize_t headerRead = 0;
    int foundSeparator = 0;

    // 1. 循环读取头部,直到找到"\n\n"分隔符
    while (!foundSeparator && headerRead < HEADER_BUF_SIZE - 1) {
        ssize_t bytes = recv(sockfd, headerBuf + headerRead, HEADER_BUF_SIZE - 1 - headerRead, 0);
        if (bytes <= 0) {
            perror("recv header failed");
            close(sockfd);
            return -1;
        }
        headerRead += bytes;
        headerBuf[headerRead] = '\0';

        char* sep = strstr(headerBuf, "\n\n");
        if (sep) {
            *sep = '\0'; // 截断头部,仅保留键值对部分
            foundSeparator = 1;
        }
    }

    if (!foundSeparator) {
        fprintf(stderr, "No header separator found\n");
        close(sockfd);
        return -1;
    }

    // 2. 解析头部参数
    int messageId = -1;
    int msgLength = -1;
    if (sscanf(headerBuf, "messageId: %d, messageLength: %d", &messageId, &msgLength) != 2) {
        fprintf(stderr, "Invalid header format\n");
        close(sockfd);
        return -1;
    }
    printf("Received messageId: %d, messageLength: %d\n", messageId, msgLength);

    // 3. 读取二进制图片数据
    unsigned char* imgData = malloc(msgLength);
    if (!imgData) {
        perror("malloc failed");
        close(sockfd);
        return -1;
    }

    ssize_t totalRead = 0;
    while (totalRead < msgLength) {
        ssize_t bytes = recv(sockfd, imgData + totalRead, msgLength - totalRead, 0);
        if (bytes <= 0) {
            perror("recv image data failed");
            free(imgData);
            close(sockfd);
            return -1;
        }
        totalRead += bytes;
    }

    // 4. 处理图片数据(示例:写入文件)
    FILE* fp = fopen("received_img.jpg", "wb");
    if (fp) {
        fwrite(imgData, 1, msgLength, fp);
        fclose(fp);
        printf("Image saved to received_img.jpg\n");
    }

    // 清理资源
    free(imgData);
    close(sockfd);
    return 0;
}

关键注意事项

  • 必须依赖messageLength:二进制数据可能包含换行符,不能仅靠分隔符判断数据结束
  • 循环收发:send和recv不一定一次性传输全部数据,必须循环处理直到完成
  • 内存安全:动态分配的缓冲区要及时释放,避免内存泄漏
  • 缓冲区大小:头部缓冲区要足够容纳完整头部,消息缓冲区要能装下头部+最大尺寸的二进制数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 18:42:40