通过TCP发送结构(C编程)

我有一个客户端和服务器程序,我想从客户端发送整个结构,然后在服务器上输出结构成员“ID”。

我已完成所有连接等,并已设法通过以下方式发送字符串:

send(socket, string, string_size, 0); 

那么,是否可以通过send()发送结构而不是字符串? 我可以将服务器上的缓冲区替换为相同类型的空结构吗?

如果你正确地做的话,Welll ……通过网络发送结构有点难。

卡尔是对的 – 您可以通过网络发送结构,说:

 send(socket, (char*)my_struct, sizeof(my_struct), 0); 

但事情就是这样:

  • sizeof(my_struct)可能在客户端和服务器之间发生变化。 编译器通常会进行一些填充和对齐,因此除非您明确定义对齐(可能使用#pragma pack()),否则该大小可能不同。
  • 另一个问题往往是字节顺序。 有些机器是big-endian,有些是little-endian,所以字节的排列可能不同。 实际上,除非您的服务器或客户端在非英特尔硬件上运行(可能不是这种情况),否则这个问题在理论上比在实践中更多。

因此,人们经常提出的解决方案是使用一个序列化结构的例程。 也就是说,它一次向结构发送一个数据成员,确保客户端和服务器只发送()和recv()您编入程序的确切指定字节数。

客户端和服务器机器“是否相同”? 你提出的建议只有在每一端的C编译器在内存中的结构完全相同时才有效。 有很多原因可能不是这种情况。 例如,客户端和服务器宏可能具有不同的体系结构,那么它们在内存中表示数字的方式(big-endian,little-endian)可能会有所不同。 即使客户机器和服务器机器具有相同的体系结构,两个不同的C编译器可能对于它们如何在存储器中布置结构具有不同的策略(例如,字段之间的填充以在字边界上对齐整数)。 即使是具有不同标志的相同的控制器也可能给出不同的结果。

实际上,我猜你的客户端和服务器是同一种机器,所以你提出的是可行的,但你需要知道,作为一般规则,它不会,这就是为什么CORBA等标准被发明的原因或者为什么人们使用一些通用的表示,比如XML。

如果客户端和服务器以完全相同的方式布局结构,则可以表示字段大小相同,具有相同的填充。 例如,如果结构中有一个long ,那么一台机器上可能是32位,另一台机器上可能是64位,在这种情况下,结构将无法正确接收。

在您的情况下,如果客户端和服务器总是在非常相似的C实现上(例如,如果这只是您用于学习一些基本概念的代码,或者由于某些其他原因,您知道您的代码将只永远不得不在你当前版本的OSX上运行),然后你可以侥幸逃脱它。 请记住,您的代码不一定能在其他平台上正常运行,并且在适用于大多数实际情况之前还有更多工作要做。

对于大多数客户端 – 服务器应用程序,这意味着答案是您不能一般地执行此操作。 您实际做的是根据发送的字节数,顺序,含义等来定义消息。 然后在每一端,您执行特定于平台的操作,以确保您正在使用的结构具有完全所需的布局。 即便如此,如果您发送struct little-endian的整数成员,则可能必须进行一些字节交换,然后您希望代码在big-endian机器上运行。 存在数据交换格式,如XML,json和Google的协议缓冲区,因此您无需执行此类繁琐工作。

[编辑:还记得当然有些结构成员永远不能通过网络发送。 例如,如果你的结构中有一个指针,那么地址指的是发送机器上的内存,并且在接收端没用。 抱歉,如果这对你来说已经很明显了,但当他们刚刚开始使用C时,对每个人来说肯定不是很明显。

你可以,但你需要知道两件重要的事情。

  1. 两端的程序必须兼容ABI 。 它们通常是两端运行在相同的处理器体系结构,相同的OS上,使用相同的编译器和编译器标志进行编译。
  2. TCP是一个流。 你必须确保发送整个结构。 查看send()调用的文档 – 它返回发送的nr个字节 – 这可能比你告诉它的要少。 接收方也一样。 仅仅因为您发送了1个发送调用的结构,并不意味着您将通过1次recv调用接收它。 它需要几次recv调用才能获得所有部分。

是的,相同类型的结构都具有相同的大小。 如果你的指针投射正确,你就可以走了。

一般来说,这样做是个坏主意,即使您的客户端和服务器以同样的方式在内存中布局结构也是如此。

即使您不打算来回传递更复杂的数据结构(包括指针),我建议您在通过网络发送数据之前对其进行序列化。

将数据作为文本文件发送,然后在收到后对其进行解码。 如果您希望将数据作为发送数据,这是最好的方法!