为什么我通过socket发送一个图片文件时,字节数不一样?
Why is there a difference in bytes when I send a picture file through a socket?
我目前正在构建一个 C/C++ 应用程序,它通过网络套接字发送和接收图片文件。但是,当我通过套接字发送文件时,目标文件与源文件相比有轻微的字节差异。
为了使此应用程序正常运行,目标(客户端)文件必须与源(服务器)文件在字节方面完全相同。
客户代码:
int client_app(int argc, char *argv[]) {
// create a socket
int network_socket;
network_socket = socket(AF_INET, SOCK_STREAM, 0);
// specify an address for the socket
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(9002);
server_address.sin_addr.s_addr = INADDR_ANY;
// connect returns an integer
int connection_status = connect(network_socket, (struct sockaddr *) &server_address, sizeof(server_address));
if (connection_status == -1) {
printf("Error making connection to remote socket \n\n");
}
// read picture byte array
printf("Reading picture byte array...\n");
char p_array[BUFSIZ];
// convert it back into a pic
printf("Converting byte array to DNG...\n");
FILE *image = fopen("out_compressed.GPR", "w");
int nb;
while ((nb = read(network_socket, p_array, BUFSIZ)) > 0) {
fwrite(p_array, 1, nb, image);
bzero(p_array, BUFSIZ);
}
fclose(image);
// and then close the socket
close(network_socket);
}
服务器代码:
int server_app(int argc, char *argv[]) {
char *process_argv[100];
*process_argv = *argv;
int process_argc = 0;
for (int i = 1; i < 6; i++) {
process_argv[i] = argv[i + 1];
process_argc++;
}
process(process_argc, process_argv);
// create the server socket
int server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
// define the port number
int port = 9002;
// define the server address
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = INADDR_ANY;
// bind the socket to our specified IP and port
bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address));
// listen for connections on the socket
listen(server_socket, 5);
// create client socket for the server to send data to
int client_socket;
client_socket = accept(server_socket, nullptr, nullptr);
// get picture size
char *fpath = process_argv[4];
FILE *picture;
picture = fopen(fpath, "r");
int size;
fseek(picture, 0, SEEK_END);
size = ftell(picture);
fseek(picture, 0, SEEK_SET);
// send picture size
write(client_socket, &size, sizeof(size));
// send picture as byte array
char send_buffer[BUFSIZ];
int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
while (!feof(picture)) {
write(client_socket, send_buffer, nb);
nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}
// then close the sockets
close(server_socket);
close(client_socket);
}
当我在客户端收到一个文件时,它看起来大小相同,但与源文件不同,我无法对其执行某些压缩操作。
当 运行 cmp
在我的终端中比较源文件和目标文件时,我得到的结果是:
compressed.GPR out_compressed.GPR differ: char 1, line 1
如何让这些文件在字节方面完全相同?
谢谢!
在客户端中,您先发送文件大小,然后再发送内容。在服务器中,您假设只发送内容,即您将最初发送的大小信息作为内容存储在新文件中。
除此之外,您只是假设 write 实际上会写入给定的所有数据,这并不能保证。因此,您需要检查实际写入的字节数。如果你在 Windows 上,你最好将文件打开为二进制文件(即 "rb" 而不是 "r","wb" 而不是 "w"),因为你正在处理此处为二进制数据。
write(client_socket, &size, sizeof(size));
将图像文件的大小写入套接字(但没有检查写入是否成功。始终检查 return 代码以确保您写入了您认为的内容。)。客户端不使用此信息并将其集中到文件中。根据客户端当前的编码方式,套接字在完成文件后立即关闭,您可以丢弃计算和发送长度的代码。当套接字关闭时,文件就完成了。无需将长度作为助手发送。
另外,图像几乎可以肯定是二进制信息。使用
打开文件
FILE *image = fopen("out_compressed.GPR", "w");
和
picture = fopen(fpath, "r");
将以文本模式打开并可能执行一些转换(其中最著名的是 \r\n
到 \n
)并弄乱文件。分别用"wb"
和"rb"
打开。
主要有两个错误。
首先,服务器在内容之前写入文件大小,但客户端立即开始读取内容,因此文件大小成为客户端内容的一部分。客户端不需要大小,所以通过简单地不在服务器端发送文件大小来实现六个。
第二个问题是您对feof
的使用:
int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
while (!feof(picture)) {
write(client_socket, send_buffer, nb);
nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}
当fread
读取文件的最后一部分时,即小于缓冲区大小的部分,设置 EOF 标志。这意味着 feof
return 为真,您在将文件的最后一段写入套接字之前退出循环。
处理此问题的更好方法是在从文件读取 1 个或多个字节时循环:
int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
while (nb > 0) {
write(client_socket, send_buffer, nb);
nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}
除此之外,您不对调用的任何函数执行错误检查。您应该检查所有套接字和文件相关函数的 return 值,如果失败则使用 perror
打印错误消息。
我目前正在构建一个 C/C++ 应用程序,它通过网络套接字发送和接收图片文件。但是,当我通过套接字发送文件时,目标文件与源文件相比有轻微的字节差异。
为了使此应用程序正常运行,目标(客户端)文件必须与源(服务器)文件在字节方面完全相同。
客户代码:
int client_app(int argc, char *argv[]) {
// create a socket
int network_socket;
network_socket = socket(AF_INET, SOCK_STREAM, 0);
// specify an address for the socket
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(9002);
server_address.sin_addr.s_addr = INADDR_ANY;
// connect returns an integer
int connection_status = connect(network_socket, (struct sockaddr *) &server_address, sizeof(server_address));
if (connection_status == -1) {
printf("Error making connection to remote socket \n\n");
}
// read picture byte array
printf("Reading picture byte array...\n");
char p_array[BUFSIZ];
// convert it back into a pic
printf("Converting byte array to DNG...\n");
FILE *image = fopen("out_compressed.GPR", "w");
int nb;
while ((nb = read(network_socket, p_array, BUFSIZ)) > 0) {
fwrite(p_array, 1, nb, image);
bzero(p_array, BUFSIZ);
}
fclose(image);
// and then close the socket
close(network_socket);
}
服务器代码:
int server_app(int argc, char *argv[]) {
char *process_argv[100];
*process_argv = *argv;
int process_argc = 0;
for (int i = 1; i < 6; i++) {
process_argv[i] = argv[i + 1];
process_argc++;
}
process(process_argc, process_argv);
// create the server socket
int server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
// define the port number
int port = 9002;
// define the server address
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = INADDR_ANY;
// bind the socket to our specified IP and port
bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address));
// listen for connections on the socket
listen(server_socket, 5);
// create client socket for the server to send data to
int client_socket;
client_socket = accept(server_socket, nullptr, nullptr);
// get picture size
char *fpath = process_argv[4];
FILE *picture;
picture = fopen(fpath, "r");
int size;
fseek(picture, 0, SEEK_END);
size = ftell(picture);
fseek(picture, 0, SEEK_SET);
// send picture size
write(client_socket, &size, sizeof(size));
// send picture as byte array
char send_buffer[BUFSIZ];
int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
while (!feof(picture)) {
write(client_socket, send_buffer, nb);
nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}
// then close the sockets
close(server_socket);
close(client_socket);
}
当我在客户端收到一个文件时,它看起来大小相同,但与源文件不同,我无法对其执行某些压缩操作。
当 运行 cmp
在我的终端中比较源文件和目标文件时,我得到的结果是:
compressed.GPR out_compressed.GPR differ: char 1, line 1
如何让这些文件在字节方面完全相同?
谢谢!
在客户端中,您先发送文件大小,然后再发送内容。在服务器中,您假设只发送内容,即您将最初发送的大小信息作为内容存储在新文件中。
除此之外,您只是假设 write 实际上会写入给定的所有数据,这并不能保证。因此,您需要检查实际写入的字节数。如果你在 Windows 上,你最好将文件打开为二进制文件(即 "rb" 而不是 "r","wb" 而不是 "w"),因为你正在处理此处为二进制数据。
write(client_socket, &size, sizeof(size));
将图像文件的大小写入套接字(但没有检查写入是否成功。始终检查 return 代码以确保您写入了您认为的内容。)。客户端不使用此信息并将其集中到文件中。根据客户端当前的编码方式,套接字在完成文件后立即关闭,您可以丢弃计算和发送长度的代码。当套接字关闭时,文件就完成了。无需将长度作为助手发送。
另外,图像几乎可以肯定是二进制信息。使用
打开文件FILE *image = fopen("out_compressed.GPR", "w");
和
picture = fopen(fpath, "r");
将以文本模式打开并可能执行一些转换(其中最著名的是 \r\n
到 \n
)并弄乱文件。分别用"wb"
和"rb"
打开。
主要有两个错误。
首先,服务器在内容之前写入文件大小,但客户端立即开始读取内容,因此文件大小成为客户端内容的一部分。客户端不需要大小,所以通过简单地不在服务器端发送文件大小来实现六个。
第二个问题是您对feof
的使用:
int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
while (!feof(picture)) {
write(client_socket, send_buffer, nb);
nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}
当fread
读取文件的最后一部分时,即小于缓冲区大小的部分,设置 EOF 标志。这意味着 feof
return 为真,您在将文件的最后一段写入套接字之前退出循环。
处理此问题的更好方法是在从文件读取 1 个或多个字节时循环:
int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
while (nb > 0) {
write(client_socket, send_buffer, nb);
nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}
除此之外,您不对调用的任何函数执行错误检查。您应该检查所有套接字和文件相关函数的 return 值,如果失败则使用 perror
打印错误消息。