草庐IT

c - 在 C 中通过 TCP (SOCK_STREAM) 套接字传递结构

coder 2023-09-17 原文

我有一个小型客户端服务器应用程序,我希望在其中使用 C 而不是 C++ 通过 TCP 套接字发送整个结构。假设结构如下:

    struct something{
int a;
char b[64];
float c;
}

我发现很多帖子说我需要使用 pragma pack 或在发送和接收之前序列化数据。

我的问题是,使用 JUST pragma pack 还是仅仅使用 serialzation 就足够了吗?还是我需要同时使用两者?

此外,由于序列化是处理器密集型过程,这会使您的性能急剧下降,那么在不使用外部库的情况下序列化结构的最佳方法是什么(我想要示例代码/算法)?

最佳答案

您需要以下内容才能通过网络可移植地发送结构:

  • 打包结构。对于 gcc 和兼容的编译器,使用 __attribute__((packed)) 执行此操作。

  • 除了固定大小的无符号整数、满足这些要求的其他压缩结构或任何前者的数组之外,不要使用任何成员。有符号整数也可以,除非您的机器不使用二进制补码表示。

  • 决定您的协议(protocol)是使用小端还是大端整数编码。在读取和写入这些整数时进行转换。

  • 此外,不要采用压缩结构成员的指针,大​​小为 1 或其他嵌套压缩结构的指针除外。参见 this answer .

编码和解码的简单示例如下。它假定字节顺序转换函数 hton8()ntoh8()hton32()ntoh32() 可用(前两个是空操作,但为了保持一致性)。

#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>

// get byte order conversion functions
#include "byteorder.h"

struct packet {
    uint8_t x;
    uint32_t y;
} __attribute__((packed));

static void decode_packet (uint8_t *recv_data, size_t recv_len)
{
    // check size
    if (recv_len < sizeof(struct packet)) {
        fprintf(stderr, "received too little!");
        return;
    }

    // make pointer
    struct packet *recv_packet = (struct packet *)recv_data;

    // fix byte order
    uint8_t x = ntoh8(recv_packet->x);
    uint32_t y = ntoh32(recv_packet->y);

    printf("Decoded: x=%"PRIu8" y=%"PRIu32"\n", x, y);
}

int main (int argc, char *argv[])
{
    // build packet
    struct packet p;
    p.x = hton8(17);
    p.y = hton32(2924);

    // send packet over link....
    // on the other end, get some data (recv_data, recv_len) to decode:
    uint8_t *recv_data = (uint8_t *)&p;
    size_t recv_len = sizeof(p);

    // now decode
    decode_packet(recv_data, recv_len);

    return 0;
}

就字节序转换函数而言,你系统的htons()/ntohs()htonl()/ntohl() 可用于分别将 16 位和 32 位整数转换为大端或从大端转换。但是,我不知道有任何用于 64 位整数的标准函数,或者与 little endian 之间的转换。您可以使用 my byte order conversion functions ;如果这样做,您必须通过定义 BADVPN_LITTLE_ENDIANBADVPN_BIG_ENDIAN 告诉它您机器的字节顺序。

就带符号整数而言,转换函数可以像我编写和链接的方法一样安全地实现(直接交换字节);只需将未签名更改为已签名即可。

更新:如果你想要一个高效的二进制协议(protocol),但不喜欢摆弄字节,你可以尝试像Protocol Buffers这样的东西。 (C implementation)。这允许您在单独的文件中描述消息的格式,并生成用于编码和解码指定格式的消息的源代码。我自己也实现了类似的东西,但大大简化了;见my BProto generatorsome examples (查看 .bproto 文件和 addr.h 以获取使用示例)。

关于c - 在 C 中通过 TCP (SOCK_STREAM) 套接字传递结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8000851/

有关c - 在 C 中通过 TCP (SOCK_STREAM) 套接字传递结构的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  4. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  5. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  6. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  7. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  8. ruby-on-rails - 一般建议和推荐的文件夹结构 - Sinatra - 2

    您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应

  9. ruby - 如何将 Puma::Configuration 传递给 Sinatra? - 2

    这是我的网络应用:classFront我是这样开始的(请不要建议使用Rack):Front.start!这是我的Puma配置对象,我不知道如何传递给它:require'puma/configuration'Puma::Configuration.new({log_requests:true,debug:true})说真的,怎么样? 最佳答案 配置与您运行的方式紧密相关puma服务器。运行的标准方式puma-pumaCLI命令。为了配置puma配置文件config/puma.rb或config/puma/.rb应该提供(参见examp

  10. ruby - 是否可以在不实际发送或读取数据的情况下查明 ruby​​ 套接字是否处于 ESTABLISHED 或 CLOSE_WAIT 状态? - 2

    s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成

随机推荐