同样的压缩文件在两台电脑上显示大小不一样是啥原因

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同样的压缩文件在两台电脑上显示大小不一样是啥原因相关的知识,希望对你有一定的参考价值。

这个跟每台电脑的配置不同,和硬盘大小不同。在各个电脑的里头所显示的显卡里头,现实的现实的东西,现实的曾量不同。嗯,不可能两天在哪都显示的文件大小是一样的。嗯,这个问题是一很平常普通的问题。所以干什么这个问题不用纠结,所有的电脑都存在电脑里。这个跟每台电脑的配置不同,和硬盘大小不同。在各个电脑的里头所显示的显卡里头,现实的现实的东西,现实的曾量不同。嗯,不可能两天在哪都显示的文件大小是一样的。嗯,这个问题是一很平常普通的问题。所以干什么这个问题不用纠结,所有的电脑都存在电脑里。这个跟每台电脑的配置不同,和硬盘大小不同。在各个电脑的里头所显示的显卡里头,现实的现实的东西,现实的曾量不同。嗯,不可能两天在哪都显示的文件大小是一样的。嗯,这个问题是一很平常普通的问题。所以干什么这个问题不用纠结,所有的电脑都存在电脑里。这个跟每台电脑的配置不同,和硬盘大小不同。在各个电脑的里头所显示的显卡里头,现实的现实的东西,现实的曾量不同。嗯,不可能两天在哪都显示的文件大小是一样的。嗯,这个问题是一很平常普通的问题。所以干什么这个问题不用纠结,所有的电脑都存在电脑里。 参考技术A 电脑不同的型号也会有不同,另外传输过程中也会少些。 参考技术B 文件传输过程中会有损坏,如果解压正常就没问题 参考技术C 因为电脑的里头所显示的显卡里头,所显示的现实的东西,显示大小的量不同。嗯,不可能文件在哪都显示的文件大小是一样的。嗯,这个问题是一很平常普通的问题。所以干什么这个问题不用纠结,所有的电脑都存在电脑里。这个跟每台电脑的配置不同,和硬盘大小不同。因为电脑的里头所显示的显卡里头,所显示的现实的东西,显示大小的量不同。嗯,不可能文件在哪都显示的文件大小是一样的。嗯,这个问题是一很平常普通的问题。所以干什么这个问题不用纠结,所有的电脑都存在电脑里。这个跟每台电脑的配置不同,和硬盘大小不同。 参考技术D 电脑型号不一样

使用 recv() 确定数据包大小的最佳方法是啥?

【中文标题】使用 recv() 确定数据包大小的最佳方法是啥?【英文标题】:What is the best way to determine packet size with recv()?使用 recv() 确定数据包大小的最佳方法是什么? 【发布时间】:2020-05-10 16:07:17 【问题描述】:

对于套接字编程和一般的 C 语言来说非常陌生。我正在尝试编写一个基本程序来在两台机器之间发送和接收数据。我知道recv 不会一次获取您的所有数据——您实际上必须循环它,直到它读完整个消息。

我没有在两台机器上设置限制,而是在客户端创建了一个简单的Message 结构:

struct Message 
    size_t length;
    char contents[1024 - sizeof(size_t)];
 message; 
message.length = sizeof(struct Message);
message.contents = information_i_want_to_send;

当它到达服务器时,我已将 recv 读入缓冲区:received = recv(ioSock, &buffer, 1024, 0)(巧合的是,它的大小与我的 Message 结构相同——但假设它不是......)。

然后我像这样从缓冲区中提取Message.length

size_t messagelength;
messagelength = *((size_t *) &buffer);

然后我在received < messagelength 时将recv 循环到缓冲区中。 这行得通,但我不禁觉得它真的很丑,而且感觉很hacky。 (特别是如果第一个 recv 调用读取小于 sizeof(size_t) 或者机器是不同的位架构,在这种情况下 size_t 转换将不起作用..)。有没有更好的方法来做到这一点?

【问题讨论】:

您应该查看htonlntohl 以及该范围内的其他函数 消息只存在于数据报协议中,例如UDP。 TCP 中没有消息,需要自己对消息进行定界。 ***.com/a/59271376/412080 Re "我知道 recv 不会一次获取您的所有数据 -- 您实际上必须循环它,直到它读取整个消息。",使用 TCP,是的。使用 UDP,我认为recv 只得到一个数据报。 你在正确的轨道上,但你不应该使用 struct 作为网络协议。 【参考方案1】:

有两种方法可以做到这一点......

1.) 使用二进制同步协议。 (使用 STX - Start of Text 和 ETX - End of Text )来识别文本的开始和结束。

2.) 在 Data 的开头附加正在发送的数据的字节数。套接字将读取这些字节数,并获得要从套接字接收的字节数。然后读取所有数据,得到所需的数据量。

嗯...好像很难...??举个例子吧。

需要发送的实际数据:ABCDEFGHIJ

新数据格式:0010ABCDEFGHIJ

服务器端所需数据:ABCDE

recv 函数将读取前 4 个字节以获取实际数据的字节数(在循环中直到它获得 4 个字节):

int received1= recv(ioSock, recvbuf, 4, 0);

根据上述情况,'recvbuf' 将 0010 转换为整数将给出值 '10' 可以存储在某个整数变量中。所以我们有:

int toReadVal = 10

现在我们只需要在下一次 recv 调用中读取这 10 位数字:

int received= recv(ioSock, recvbuf1, toReadVal, 0);

最后,我们得到 recvbuf1 的值为 ABCDEFGHIG。现在您可以根据需要截断该值。

【讨论】:

还有其他几种方法可以做到这一点。 STX 和 ETX 表示 传输 的开始/结束,而不是“文本”。没有recv() 调用保证填充缓冲区。您的回答没有以任何有用的方式扩展 OP 的代码, 我喜欢提到 STX/ETX,尤其是与在消息本身中附加消息长度相比。 @user207421 ... 整个谷歌世界都将 stx/etx 解释为文本的开始/结束。顺便说一句,recv() 函数不能保证填充缓冲区,这就是为什么在为循环逻辑提供的括号中存在“注意”的原因。我们可以解释逻辑但不能提供所有的验证和代码。这可能有助于获得正确的解决方案。 Re "整个 google 世界都将 stx/etx 解释为文本的开始/结束。",包括UNICODE,至少在提到 U+0002 和 U+ 时0003 @ikegami 不要使用 grep 关键字“注意”。这句话是“为循环逻辑提供的括号中有一个'注意'”。 :-p【参考方案2】:

你有一个固定大小的消息,所以你可以使用这样的东西:

#include <errno.h>
#include <limits.h>

// Returns the number of bytes read.
// EOF was reached if the number of bytes read is less than requested.
// On error, returns -1 and sets errno.
ssize_t recv_fixed_amount(int sockfd, char *buf, size_t size) 
   if (size > SSIZE_MAX) 
      errno = EINVAL;
      return -1;
   

   ssize_t bytes_read = 0;
   while (size > 0) 
      ssize_t rv = recv(sockfd, buf, size, 0); 
      if (rv < 0)
         return -1;
      if (rv == 0)
         return bytes_read;

      size -= rv;
      bytes_read += rv;
      buf += rv;
   

   return bytes_read;

它会像这样使用:

typedef struct 
   uint32_t length;
   char contents[1020];
 Message;

Message message;

ssize_t bytes_read = recv_fixed_amount(sockfd, &(message.length), sizeof(message.length));
if (bytes_read == 0) 
   printf("EOF reached\n");
   exit(EXIT_SUCCESS);


if (bytes_read < 0) 
   perror("recv");
   exit(EXIT_FAILURE);


if (bytes_read != sizeof(message.length)) 
   fprintf(stderr, "recv: Premature EOF.\n");
   exit(EXIT_FAILURE);


bytes_read = recv_fixed_amount(sockfd, &(message.content), sizeof(message.content));
if (bytes_read < 0) 
   perror("recv");
   exit(EXIT_FAILURE);


if (bytes_read != msg_size) 
   fprintf(stderr, "recv: Premature EOF.\n");
   exit(EXIT_FAILURE);

注意事项:

size_t 不会在所有地方都一样,所以我改用uint32_t

我独立读取字段,因为结构中的填充可能因实现而异。他们也需要以这种方式发送。

接收者正在使用来自流的信息填充message.length,但实际上并未使用它。

恶意或有问题的发件人可能会为message.length 提供一个太大的值,如果它不验证它会使接收器崩溃(或更糟)。 contents 也是如此。如果这是预期的,它可能不是 NUL 终止的。


但是如果长度不固定怎么办?然后发件人需要以某种方式传达读者需要阅读多少内容。一种常见的方法是长度前缀。

typedef struct 
   uint32_t length;
   char contents[];
 Message;

uint32_t contents_size;
ssize_t bytes_read = recv_fixed_amount(sockfd, &contents_size, sizeof(contents_size));
if (bytes_read == 0) 
   printf("EOF reached\n");
   exit(EXIT_SUCCESS);


if (bytes_read < 0) 
   perror("recv");
   exit(EXIT_FAILURE);


if (bytes_read != sizeof(contents_size)) 
   fprintf(stderr, "recv: Premature EOF.\n");
   exit(EXIT_FAILURE);


Message *message = malloc(sizeof(Message)+contents_size);
if (!message) 
   perror("malloc");
   exit(EXIT_FAILURE);


message->length = contents_size;

bytes_read = recv_fixed_amount(sockfd, &(message->contents), contents_size);
if (bytes_read < 0) 
   perror("recv");
   exit(EXIT_FAILURE);


if (bytes_read != contents_size) 
   fprintf(stderr, "recv: Premature EOF.\n");
   exit(EXIT_FAILURE);

注意事项:

message-&gt;length 包含 message-&gt;contents 的大小而不是结构的大小。这更有用。

另一种方法是使用哨兵值。这是一个告诉读者消息结束的值。这就是终止 C 字符串的 NUL。这更复杂,因为您不知道提前阅读多少。逐字节读取成本太高,因此通常使用缓冲区。

 while (1) 
     extend_buffer_if_necessary();
     recv_into_buffer();
     while (buffer_contains_a_sentinel()) 
        // This also shifts the remainder of the buffer's contents.
        extract_contents_of_buffer_up_to_sentinel();
        process_extracted_message();      
     
 

使用标记值的好处是不需要提前知道消息的长度(因此发送者可以在消息完全创建之前开始发送。)

缺点与 C 字符串相同:消息不能包含标记值,除非使用某种形式的转义机制。在这和阅读器的复杂性之间,您可以看到为什么长度前缀通常比哨兵值更受欢迎。 :)


最后,对于要在完全创建之前开始发送的大消息的标记值,有一个比标记值更好的解决方案:以长度为前缀的块序列。一个人继续读取块,直到遇到大小为 0 的块,表示结束。

HTTP 支持以长度为前缀的消息(以Content-Length: &lt;length&gt; 标头的形式)和这种方法(以Transfer-Encoding: chunked header 的形式)。

【讨论】:

我明白了,谢谢。这比我的方法干净得多。所以看起来无论如何,要干净地发送数据,您需要有一些协议/固定参数? IE。在您的示例中,消息前缀的大小是发送者和接收者都同意的。 是的,当使用流协议(例如 TCP,或从文件中读取一系列记录)时,如果该信息不在消息本身。 即使在处理数据报 (UDP) 或从文件中读取单个记录时,您仍然需要对字段的大小(如果有多个)和字节序那些字段。 我做了一些改进,并添加了长度前缀的替代方法。 @chux-ReinstateMonica,这通常是链路级协议(例如 PPP)而不是应用层协议(例如 HTTP)的问题。应用程序层的滑点表明代码中存在错误。而噪声会在链路层引起它。

以上是关于同样的压缩文件在两台电脑上显示大小不一样是啥原因的主要内容,如果未能解决你的问题,请参考以下文章

为啥同一个word文档在两台电脑上打开里面的格式不一样

使用TCP在同一台电脑上可以建立连接,在两台电脑上却连接失败的原因分析

将已经开发完的web项目部署到两台电脑上,但是两台电脑的运行效果不一样,有一台更新不好

为啥文件压缩后和原文件大小不一样

【DELPHI】如何在两台电脑间传送文件?

程序脱壳是啥意思?