我写了一个小的客户端 - 服务器代码,我在其中发送从客户端到服务器的整数和字符。所以我知道C语言中套接字编程的基础知识,比如要遵循的步骤和所有步骤。现在我想创建一个数据包并将其发送到我的服务器。我以为我会创建一个结构
struct packet
{
int srcID;
long int data;
.....
.....
.....
};
struct packet * pkt;
在执行send()之前,我认为我将使用
在数据包内写入值 pkt-> srcID = 01
pkt-> data = 1 2 3 4
我需要知道我是否在正确的道路上,如果是,那么我可以使用
发送 send(sockfd, &packet, sizeof(packet), 0)
接收
recv(newsockfd, &PACKET, sizeof(PACKET), 0)
我刚刚开始使用网络编程,所以我不确定我是否在正确的道路上。如果有人能以任何形式(理论,例子等)引导我提出问题,那将是非常有帮助的。提前谢谢。
答案 0 :(得分:4)
指针pkt
未在您的应用程序中定义。您有两种选择:
1)将pkt
声明为正常变量
struct packet pkt;
pkt.srcID = 01;
....
send(sockfd, &pkt, sizeof(struct packet), 0);
2)当您的数据包包含标头后跟有效负载时,第二种方法很有用:
char buffer[MAX_PACKET_SIZE];
struct packet *pkt = (struct packet *) buffer;
char *payload = buffer + sizeof(struct packet);
int packet_size; /* should be computed as header size + payload size */
pkt->srcID = 01;
...
packet_size = sizeof(struct packet) /* + payload size */ ;
send(sockfd, pkt, packet_size, 0);
....
更新(回答你的评论): 首先,您应该知道从TCP套接字接收可能不会提供整个数据包。您需要实现循环(如Nemo所建议)来读取整个数据包。由于您更喜欢第二个选项,因此您需要两个循环。第一个循环是读取数据包标头以提取有效负载大小,第二个循环是读取数据。在UDP的情况下,您不必担心部分接收。下面是一个示例代码(没有循环),其中sockfd是UDP套接字:
char buffer[MAX_PACKET_SIZE];
struct packet *pkt = (struct packet *) buffer;
char *payload = buffer + sizeof(struct packet);
int packet_size; /* should be computed as header size + payload size */
.....
/* read the whole packet */
if (recv(sockfd, pkt, MAX_PACKET_SIZE, 0) < 0) {
/* error in receiving the packet. It is up to you how to handle it */
}
/* Now, you can extract srcID as pkt->srcID */
/* you can get data by processing payload variable */
记住: *您需要实现其他用户提到的序列化 * UDP是不可靠的传输协议,而TCP是可靠的传输协议。
答案 1 :(得分:4)
当您要通过网络发送数据时,您需要考虑使用固定大小的标头,后跟可变长度的有效负载。
[1字节标题] [可变字节有效负载]
标题应该为您提供要发送的数据的大小,以便接收方始终读取固定大小的标头,然后确定数据包长度并读取其余字节。
例如:
int nRet = recv(nSock,(char*)pBuffer,MESSAGE_HEADER_LENGTH,0);
if (nRet ==MESSAGE_HEADER_LENGTH)
{
int nSizeOfPayload = //Get the length from pBuffer;
char* pData = new Char(nSizeOfPayload );
int nPayloadLen = recv(nSock, (char*)pData,nSizeOfPayload ,0);
}
如果您的结构有字符串,则应始终在字符串之前附加字符串的大小。
如果要将数据包发送到在不同机器上运行的两个不同应用程序,则需要事先商定如何表示字节数,即是先发送MSB还是先发送LSB。
答案 2 :(得分:3)
直接将struct
写入网络是诱人干净,简单,整洁......不幸的是,错误。 (这并不能阻止很多人,包括许多应该知道更多的人,无论如何都要这样做。)
这个 1 有几个原因:
struct
的成员大小不均匀时添加不可见的填充 - 并且此填充的布局也因架构而异。位域是另一种结构布局变量。char *
指针,因为您可能必须在struct
中途继续阅读(例如,您的struct
可能长达830个字节,但第一个读取只返回500字节)。 正确的方法是使用一个名为序列化的过程:对于要通过网络发送的每个数据结构,你有一个功能,它将内容规范化并将它们打包成一个char
缓冲区以定义的格式。这包括将整数转换为特定的字节序(像htonl()
这样的函数在这里很有用)。还有一个相应的函数可以将char
缓冲区解压缩到接收方使用的struct
形式。
现有几个用于序列化的代码库 - 例如,Google的Protocol Buffers。
另一个可行的替代方法是将数据结构序列化为文本格式,如JSON。这对于故障排除非常有用,因为这意味着您的网络协议在某种程度上是人类可读的。
1。预见到一些反对意见:是的,大多数问题都有解决方法。但这就是它们的原因:通常依赖于编译器特定功能的解决方法,取消了大多数简单优势,但仍然不是完全可靠的。