使用C语言套接字发送HTTP响应中的二进制文件

8

我正在尝试在HTTP响应中发送一个二进制文件(PNG图像)。

FILE *file;
char *buffer;
int fileLen;

//Open file
file = fopen("1.png", "rb");
if (!file)
{
    return;
}

//Get file length
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);

//Allocate memory
buffer=(char *)malloc(fileLen+1);
if (!buffer)
{
    fprintf(stderr, "Memory error!");
    fclose(file);
    return;
}

//Read file contents into buffer
fread(buffer, fileLen, 1, file);
fclose(file);
//free(buffer);




char header[102400];

sprintf(header, 
"HTTP/1.1 200 OK\n"
"Date: Thu, 19 Feb 2009 12:27:04 GMT\n"
"Server: Apache/2.2.3\n"
"Last-Modified: Wed, 18 Jun 2003 16:05:58 GMT\n"
"ETag: \"56d-9989200-1132c580\"\n"
"Content-Type: image/png\n"
"Content-Length: %i\n"
"Accept-Ranges: bytes\n"
"Connection: close\n"
        "\n", fileLen);

char *reply = (char*)malloc(strlen(header)+fileLen);
strcpy(reply, header);
strcat(reply, buffer);

printf("msg %s\n", reply);


//return 0;
int sd = socket(PF_INET, SOCK_STREAM, 0);

struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8081);
addr.sin_addr.s_addr = INADDR_ANY;

if(bind(sd,&addr,sizeof(addr))!=0)
{
    printf("bind error\n");
}

if (listen(sd, 16)!=0)
{
    printf("listen error\n");
}

for(;;)
{
    int size = sizeof(addr);
    int client = accept(sd, &addr, &size);

    if (client > 0)
    {
        printf("client connected\n");
        send(client, reply, strlen(reply), 0);
    }
}

但是我的浏览器无法理解这个 =( 我到底做错了什么?
更新:我尝试发送文本数据 - 没问题。但二进制数据失败了。
4个回答

10
问题在于您的消息正文被视为以 null 结尾的文本字符串(您在其上使用 strcatstrlen),但它实际上是二进制数据(PNG 文件)。因此,strcatstrlen 都会停在图像中的第一个0字节处(通常相当早)。
您的程序甚至还打印出响应主体:请注意,它给出了正确的标题,但一旦 PNG 标头(二进制数据)开始,只有几个字节。
  1. strcat(reply, buffer) 这行代码,其中 buffer 可能包含 0 字节。将其更改为 memcpy(reply+strlen(header), buffer, fileLen)
  2. send(client, reply, strlen(reply), 0) 这行代码,其中 reply 可能包含 0 字节。预先计算回复的长度,或将 strlen 替换为 strlen(header)+fileLen
另一个 bug 是当您完成后没有关闭连接,因此浏览器将无限等待。在 send 后面添加以下代码以解决此问题:
close(client);

请注意,这实际上不会修复您放置的调试打印,因为它使用printf并且在遇到空字节时会停止。但是套接字行为将是正确的;我在Web浏览器中进行了测试,它可以正常工作。 - mgiuca

1
HTTP协议规定它期望使用"\r\n"而不是"\n"。尝试一下吧。 :)

不,那没帮助。我尝试发送文本数据 - 用 "\n" 是可以的。但是二进制数据失败了。 - spe
Windows会自动将输出中的\n转换为\r\n - dns

0

我尝试跟着你的做法,但是无法让它工作。相反,我发现将头文件和文件分开发送更容易。

例如:

send(client, header, strlen(header), 0);
send(client, buffer, fileLen + 1, 0);

0
strcat(reply, buffer); // this is incorrect, because png(buffer) may contain zero byte
send(client, reply, strlen(reply), 0);
strlen(reply) // this is incorrect, because png may contain zero byte

不仅如此,注意当strcat从缓冲区复制到回复时也会在遇到空字节时失败--请参见我的答案。 - mgiuca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接