passing primitive or struct type as function argument

时间:2021-08-28 05:28:00

I'm trying to write some reasonably generic networking code. I have several kinds of packets, each represented by a different struct. The function where all my sending occurs looks like:

我正在尝试编写一些合理的通用网络代码。我有几种数据包,每个数据包由不同的结构表示。我发送的所有发送功能如下:

- (void)sendUpdatePacket:(MyPacketType)packet{ 
    for(NSNetService *service in _services) 
        for(NSData *address in [service addresses]) 
            sendto(_socket, &packet, sizeof(packet), 0, [address bytes], [address length]); 
}

I would really like to be able to send this function ANY kind of packet, not just MyPacketType packets.

我真的希望能够发送这个函数任何类型的数据包,而不仅仅是MyPacketType数据包。

I thought maybe if the function def was:

我想也许如果函数def是:

- (void)sendUpdatePacket:(void*)packetRef

I could pass in anykind of pointer to packet. But, without knowing the type of packet, I can't dereference the pointer.

我可以将任何指针传递给数据包。但是,在不知道数据包类型的情况下,我无法取消引用指针。

How do I write a function to accept any kind of primitive/struct as its argument?

如何编写一个函数来接受任何类型的原语/结构作为其参数?

2 个解决方案

#1


4  

What you are trying to achieve is polymorphism, which is an OO concept.

你想要实现的是多态,这是一个OO概念。

So while this would be quite easy to implement in C++ (or other OO languages), it's a bit more challenging in C.

因此,虽然这在C ++(或其他OO语言)中很容易实现,但在C语言中更具挑战性。

One way you could get around is it to create a generic "packet" structure such as this:

你可以解决的一种方法是创建一个通用的“数据包”结构,如下所示:

typedef struct {
    void* messageHandler;
    int   messageLength;
    int*  messageData;
} packet;

Where the messageHandler member is a function pointer to a callback routine which can process the message type, and the messageLength and messageData members are fairly self-explanatory.

其中messageHandler成员是一个指向可以处理消息类型的回调例程的函数指针,而messageLength和messageData成员则相当不言自明。

The idea is that the method which you pass the packetStruct to would use the Tell, Don't Ask principle to invoke the specific message handler pointer to by messageHandler, passing in the messageLength and messageData without interpreting it.

我们的想法是,您传递packetStruct的方法将使用Tell,Do not Ask原则来调用messageHandler指定的特定消息处理程序指针,传递messageLength和messageData而不解释它。

The dispatch function (pointed to by messageHandler) would be message-specific and will be able to cast the messageData to the appropriate meaningful type, and then the meaningful fields can be extracted from it and processed, etc.

dispatch函数(由messageHandler指向)将是特定于消息的,并且能够将messageData强制转换为适当的有意义类型,然后可以从中提取有意义的字段并进行处理等。

Of course, this is all much easier and more elegant in C++ with inheritance, virtual methods and the like.

当然,在使用继承,虚拟方法等的C ++中,这一切都变得更加容易和优雅。


Edit:

In response to the comment:

回应评论:

I'm a little unclear how "able to cast the messageData to the appropriate meaningful type, and then the meaningful fields can be extracted from it and processed, etc." would be accomplished.

我有点不清楚“能够将messageData转换为适当的有意义类型,然后可以从中提取有意义的字段并进行处理等”。会完成的。

You would implement a handler for a specific message type, and set the messageHandler member to be a function pointer to this handler. For example:

您将为特定的消息类型实现处理程序,并将messageHandler成员设置为此处理程序的函数指针。例如:

void messageAlphaHandler(int messageLength, int* messageData)
{
    MessageAlpha* myMessage = (MessageAlpha*)messageData;

    // Can now use MessageAlpha members...
    int messageField = myMessage->field1;
    // etc...
}

You would define messageAlphaHandler() in such a way to allow any class to get a function pointer to it easily. You could do this on startup of the application so that the message handlers are registered from the beginning.

您可以通过这种方式定义messageAlphaHandler(),以允许任何类轻松获取指向它的函数指针。您可以在启动应用程序时执行此操作,以便从头开始注册消息处理程序。

Note that for this system to work, all message handlers would need to share the same function signature (i.e. return type and parameters).

请注意,要使此系统正常工作,所有消息处理程序都需要共享相同的函数签名(即返回类型和参数)。

Or for that matter, how messageData would be created in the first place from my struct.

或者就此而言,如何从我的struct中创建messageData。

How are you getting you packet data? Are you creating it manually, reading it off a socket? Either way, you need to encode it somewhere as a string of bytes. The int* member (messageData) is merely a pointer to the start of the encoded data. The messageLength member is the length of this encoded data.

你是如何得到分组数据的?你是手动创建它,从套接字读取它?无论哪种方式,您需要将其编码为某个字节串。 int *成员(messageData)仅仅是指向编码数据开始的指针。 messageLength成员是此编码数据的长度。

In your message handler callback, you don't want probably don't want to continue to manipulate the data as raw binary/hex data, but instead interpret the information in a meaningful fashion according to the message type.

在消息处理程序回调中,您可能不希望继续将数据作为原始二进制/十六进制数据进行操作,而是根据消息类型以有意义的方式解释信息。

Casting it to a struct essentially maps the raw binary information on to a meaningful set of attributes matching to the protocol of the message you are processing.

将其转换为结构本质上将原始二进制信息映射到与您正在处理的消息的协议匹配的一组有意义的属性。

#2


3  

The key is that you must realize that everything in a computer is just an array of bytes (or, words, or double words).

关键是你必须意识到计算机中的所有东西都只是一个字节数组(或单词或双字)。

ZEN MASTER MUSTARD is sitting at his desk staring at his monitor staring at a complex pattern of seemingly random characters. A STUDENT approaches.

ZEN MASTER MUSTARD坐在他的办公桌旁,盯着他的显示器,盯着看似随机的复杂图案。学生接近。

Student: Master? May I interrupt?

学生:师父?我可以打断吗?

Zen Master Mustard: You have answered your own inquiry, my son.

禅师芥末:我的儿子,你已经回答了自己的询问。

S: What?

ZMM: By asking your question about interrupting me, you have interrupted me.

ZMM:通过询问关于打断我的问题,你打扰了我。

S: Oh, sorry. I have a question about moving structures of varying size from place to place.

S:哦,对不起。我有一个关于从一个地方到另一个地方移动不同大小的结构的问题。

ZMM: If that it true, then you should consult a master who excels at such things. I suggest, you pay a visit to Master DotPuft, who has great knowledge in moving large metal structures, such as tracking radars, from place to place. Master DotPuft can also cause the slightest elements of a feather-weight strain gage to move with the force of a dove's breath. Turn right, then turn left when you reach the door of the hi-bay. There dwells Master DotPuft.

ZMM:如果这是真的,那么你应该咨询一位擅长此类事情的大师。我建议您访问Master DotPuft,他对移动大型金属结构(如跟踪雷达)的知识非常了解。 Master DotPuft还可以使羽毛重量应变计​​中最轻微的元素随着鸽子的呼吸力而移动。向右转,然后在到达hi-bay门时向左转。那里住着Master DotPuft。

S: No, I mean moving large structures of varying sizes from place to place in the memory of a computer.

S:不,我的意思是在计算机的存储器中将不同大小的大型结构从一个地方移动到另一个地方。

ZMM: I may assist you in that endeavor, if you wish. Describe your problem.

ZMM:如果你愿意的话,我可以帮助你做出这样的努力。描述你的问题。

S: Specifically, I have a c function that I want to accept several different types of structs (they will be representing different type of packets). So my struct packets will be passed to my function as void*. But without knowing the type, I can't cast them, or really do much of anything. I know this is a solvable problem, because sento() from socket.h does exactly that:

S:具体来说,我有一个c函数,我想接受几种不同类型的结构(它们将代表不同类型的数据包)。所以我的struct包将作为void *传递给我的函数。但是在不知道类型的情况下,我无法投射它们,或者真的做了很多事情。我知道这是一个可解决的问题,因为来自socket.h的sento()正是这样做的:

    ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr,socklen_t dest_len);

where sendto would be called like:

sendto将被称为:

    sendto(socketAddress, &myPacket, sizeof(myPacket), Other args....);

ZMM: Did you describe your problem to Zen Master MANTAR! ?

ZMM:你有没有向Zen Master MANTAR描述你的问题! ?

S: Yeah, he said, "It's just a pointer. Everything in C is a pointer." When I asked him to explain, he said, "Bok, bok, get the hell out of my office."

S:是的,他说,“这只是一个指针.C中的所有东西都是指针。”当我让他解释时,他说,“博克,博克,让我离开我的办公室。”

ZMM: Truly, you have spoken to the master. Did this not help you?

ZMM:你真的和主人说过话。这对你没有帮助吗?

S: Um, er, no. Then I asked Zen Master Max.

S:恩,呃,不。然后我问Zen Master Max。

ZMM: Wise is he. What was his advice to you useful?

ZMM:明智的是他。他对你的建议有用吗?

S: No. When I asked him about sendto(), he just swirled his fists in the air. It's just an array of bytes."

S:不。当我问他关于sendto()时,他只是在空中旋转拳头。它只是一个字节数组。“

ZMM: Indeed, Zen Master Max has tau.

ZMM:的确,Zen Master Max有tau。

S: Yeah, he has tau, but how do I deal with function arguments of type void*?

S:是的,他有tau,但我如何处理void *类型的函数参数?

ZMM: To learn, you must first unlearn. The key is that you must realize that everything in a computer is just an array of bytes (or, words, or double words). Once you have a pointer to the beginning of a buffer, and the length of the buffer, you can sent it anywhere without a need to know the type of data placed in the buffer.

ZMM:要学习,你必须先忘掉。关键是你必须意识到计算机中的所有东西都只是一个字节数组(或单词或双字)。一旦有了指向缓冲区开头的指针和缓冲区的长度,就可以将它发送到任何地方而无需知道缓冲区中放置的数据类型。

S: OK.

ZMM: Consider a string of man-readable text. "You plan a tower that will pierce the clouds? Lay first the foundation of humility." It is 82 bytes long. Or, perhaps, 164 if the evil Unicode is used. Guard yourself against the lies of Unicode! I can submit this text to sendto() by providing a pointer to the beginning of the buffer that contains the string, and the length of the buffer, like so:

ZMM:考虑一串人类可读的文本。 “你计划一座能够刺破云层的塔楼?首先奠定谦卑的基础。”它长82个字节。或者,如果使用邪恶的Unicode,可能是164。保护自己免受Unicode的谎言!我可以通过提供指向包含字符串的缓冲区开头的指针和缓冲区的长度来将此文本提交给sendto(),如下所示:

char characterBuffer[300];      // 300 bytes
strcpy(characterBuffer, "You plan a tower that will pierce the clouds? Lay first the foundation of humility.");
// note that sizeof(characterBuffer) evaluates to 300 bytes.
sendto(socketAddress, &characterBuffer, sizeof(characterBuffer));

ZMM: Note well that the number of bytes of the character buffer is automatically calculated by the compiler. The number of bytes occupied by any variable type is of a type called "size_t". It is likely equivalent to the type "long" or "unsinged int", but it is compiler dependent.

ZMM:请注意,编译器会自动计算字符缓冲区的字节数。任何变量类型占用的字节数都是名为“size_t”的类型。它可能等同于“long”或“unsinged int”类型,但它依赖于编译器。

S: Well, what if I want to send a struct?

S:嗯,如果我想发送结构怎么办?

ZMM: Let us send a struct, then.

ZMM:那么让我们发一个结构。

struct
{
     int integerField;          // 4 bytes
     char characterField[300];  // 300 bytes
     float floatField;          // 4 bytes
} myStruct;

myStruct.integerField = 8765309;
strcpy(myStruct.characterField, "Jenny, I got your number.");
myStruct.floatField = 876.5309;

// sizeof(myStruct) evaluates to 4 + 300 + 4 = 308 bytes
sendto(socketAddress, &myStruct, sizeof(myStruct);

S: Yeah, that's great at transmitting things over TCP/IP sockets. But what about the poor receiving function? How can it tell if I am sending a character array or a struct?

S:是的,通过TCP / IP套接字进行传输非常棒。但是糟糕的接收功能呢?它如何判断我是否正在发送字符数组或结构?

ZMM: One way is to enumerate the different types of data that may be sent, and then send the type of data along with the data. Zen Masters refer to this as "metadata", that is to say, "data about the data". Your receiving function must examine the metadata to determine what kind of data (struct, float, character array) is being sent, and then use this information to cast the data back into its original type. First, consider the transmitting function:

ZMM:一种方法是枚举可能发送的不同类型的数据,然后将数据类型与数据一起发送。 Zen Masters将此称为“元数据”,即“有关数据的数据”。您的接收函数必须检查元数据以确定要发送的数据类型(struct,float,字符数组),然后使用此信息将数据转换回其原始类型。首先,考虑传输功能:

enum
{
    INTEGER_IN_THE_PACKET =0 ,
    STRING_IN_THE_PACKET =1,  
    STRUCT_IN_THE_PACKET=2
} typeBeingSent;

struct
{
     typeBeingSent dataType;
     char data[4096];
} Packet_struct;

Packet_struct myPacket;

myPacket.dataType = STRING_IN_THE_PACKET;
strcpy(myPacket.data, "Nothing great is ever achieved without much enduring.");
sendto(socketAddress, myPacket, sizeof(Packet_struct);

myPacket.dataType = STRUCT_IN_THE_PACKET;
memcpy(myPacket.data, (void*)&myStruct, sizeof(myStruct);
sendto(socketAddress, myPacket, sizeof(Packet_struct);

S: All right.

S:好的。

ZMM: Now, just us walk along with the receiving function. It must query the type of the data that was sent and the copy the data into a variable declared of that type. Forgive me, but I forget the exact for of the recvfrom() function.

ZMM:现在,我们一起走接收功能。它必须查询已发送数据的类型,并将数据复制到声明该类型的变量中。原谅我,但我忘记了recvfrom()函数的确切结果。

   char[300] receivedString;
   struct myStruct receivedStruct;  

   recvfrom(socketDescriptor, myPacket, sizeof(myPacket);

   switch(myPacket.dataType)
   {
       case STRING_IN_THE_PACKET:
            // note the cast of the void* data into type "character pointer"
            &receivedString[0] = (char*)&myPacket.data; 
            printf("The string in the packet was \"%s\".\n", receivedString); 
            break;

       case STRUCT_IN_THE_PACKET:
            // note the case of the void* into type "pointer to myStruct"
            memcpy(receivedStruct, (struct myStruct *)&myPacket.data, sizeof(receivedStruct));
            break;
    }

ZMM: Have you achieved enlightenment? First, one asks the compiler for the size of the data (a.k.a. the number of bytes) to be submitted to sendto(). You send the type of the original data is sent along as well. The receiver then queries for the type of the original data, and uses it to call the correct cast from "pointer to void" (a generic pointer), over to the type of the original data (int, char[], a struct, etc.)

ZMM:你获得了启蒙吗?首先,要求编译器提交要发送到sendto()的数据大小(a.k.a.字节数)。您发送原始数据的类型也是一样的。然后接收器查询原始数据的类型,并使用它从“指向void的指针”(通用指针)调用正确的转换,转换为原始数据的类型(int,char [],一个结构,等等。)

S: Well, I'll give it a try.

S:嗯,我试一试。

ZMM: Go in peace.

ZMM:安静地走吧。

#1


4  

What you are trying to achieve is polymorphism, which is an OO concept.

你想要实现的是多态,这是一个OO概念。

So while this would be quite easy to implement in C++ (or other OO languages), it's a bit more challenging in C.

因此,虽然这在C ++(或其他OO语言)中很容易实现,但在C语言中更具挑战性。

One way you could get around is it to create a generic "packet" structure such as this:

你可以解决的一种方法是创建一个通用的“数据包”结构,如下所示:

typedef struct {
    void* messageHandler;
    int   messageLength;
    int*  messageData;
} packet;

Where the messageHandler member is a function pointer to a callback routine which can process the message type, and the messageLength and messageData members are fairly self-explanatory.

其中messageHandler成员是一个指向可以处理消息类型的回调例程的函数指针,而messageLength和messageData成员则相当不言自明。

The idea is that the method which you pass the packetStruct to would use the Tell, Don't Ask principle to invoke the specific message handler pointer to by messageHandler, passing in the messageLength and messageData without interpreting it.

我们的想法是,您传递packetStruct的方法将使用Tell,Do not Ask原则来调用messageHandler指定的特定消息处理程序指针,传递messageLength和messageData而不解释它。

The dispatch function (pointed to by messageHandler) would be message-specific and will be able to cast the messageData to the appropriate meaningful type, and then the meaningful fields can be extracted from it and processed, etc.

dispatch函数(由messageHandler指向)将是特定于消息的,并且能够将messageData强制转换为适当的有意义类型,然后可以从中提取有意义的字段并进行处理等。

Of course, this is all much easier and more elegant in C++ with inheritance, virtual methods and the like.

当然,在使用继承,虚拟方法等的C ++中,这一切都变得更加容易和优雅。


Edit:

In response to the comment:

回应评论:

I'm a little unclear how "able to cast the messageData to the appropriate meaningful type, and then the meaningful fields can be extracted from it and processed, etc." would be accomplished.

我有点不清楚“能够将messageData转换为适当的有意义类型,然后可以从中提取有意义的字段并进行处理等”。会完成的。

You would implement a handler for a specific message type, and set the messageHandler member to be a function pointer to this handler. For example:

您将为特定的消息类型实现处理程序,并将messageHandler成员设置为此处理程序的函数指针。例如:

void messageAlphaHandler(int messageLength, int* messageData)
{
    MessageAlpha* myMessage = (MessageAlpha*)messageData;

    // Can now use MessageAlpha members...
    int messageField = myMessage->field1;
    // etc...
}

You would define messageAlphaHandler() in such a way to allow any class to get a function pointer to it easily. You could do this on startup of the application so that the message handlers are registered from the beginning.

您可以通过这种方式定义messageAlphaHandler(),以允许任何类轻松获取指向它的函数指针。您可以在启动应用程序时执行此操作,以便从头开始注册消息处理程序。

Note that for this system to work, all message handlers would need to share the same function signature (i.e. return type and parameters).

请注意,要使此系统正常工作,所有消息处理程序都需要共享相同的函数签名(即返回类型和参数)。

Or for that matter, how messageData would be created in the first place from my struct.

或者就此而言,如何从我的struct中创建messageData。

How are you getting you packet data? Are you creating it manually, reading it off a socket? Either way, you need to encode it somewhere as a string of bytes. The int* member (messageData) is merely a pointer to the start of the encoded data. The messageLength member is the length of this encoded data.

你是如何得到分组数据的?你是手动创建它,从套接字读取它?无论哪种方式,您需要将其编码为某个字节串。 int *成员(messageData)仅仅是指向编码数据开始的指针。 messageLength成员是此编码数据的长度。

In your message handler callback, you don't want probably don't want to continue to manipulate the data as raw binary/hex data, but instead interpret the information in a meaningful fashion according to the message type.

在消息处理程序回调中,您可能不希望继续将数据作为原始二进制/十六进制数据进行操作,而是根据消息类型以有意义的方式解释信息。

Casting it to a struct essentially maps the raw binary information on to a meaningful set of attributes matching to the protocol of the message you are processing.

将其转换为结构本质上将原始二进制信息映射到与您正在处理的消息的协议匹配的一组有意义的属性。

#2


3  

The key is that you must realize that everything in a computer is just an array of bytes (or, words, or double words).

关键是你必须意识到计算机中的所有东西都只是一个字节数组(或单词或双字)。

ZEN MASTER MUSTARD is sitting at his desk staring at his monitor staring at a complex pattern of seemingly random characters. A STUDENT approaches.

ZEN MASTER MUSTARD坐在他的办公桌旁,盯着他的显示器,盯着看似随机的复杂图案。学生接近。

Student: Master? May I interrupt?

学生:师父?我可以打断吗?

Zen Master Mustard: You have answered your own inquiry, my son.

禅师芥末:我的儿子,你已经回答了自己的询问。

S: What?

ZMM: By asking your question about interrupting me, you have interrupted me.

ZMM:通过询问关于打断我的问题,你打扰了我。

S: Oh, sorry. I have a question about moving structures of varying size from place to place.

S:哦,对不起。我有一个关于从一个地方到另一个地方移动不同大小的结构的问题。

ZMM: If that it true, then you should consult a master who excels at such things. I suggest, you pay a visit to Master DotPuft, who has great knowledge in moving large metal structures, such as tracking radars, from place to place. Master DotPuft can also cause the slightest elements of a feather-weight strain gage to move with the force of a dove's breath. Turn right, then turn left when you reach the door of the hi-bay. There dwells Master DotPuft.

ZMM:如果这是真的,那么你应该咨询一位擅长此类事情的大师。我建议您访问Master DotPuft,他对移动大型金属结构(如跟踪雷达)的知识非常了解。 Master DotPuft还可以使羽毛重量应变计​​中最轻微的元素随着鸽子的呼吸力而移动。向右转,然后在到达hi-bay门时向左转。那里住着Master DotPuft。

S: No, I mean moving large structures of varying sizes from place to place in the memory of a computer.

S:不,我的意思是在计算机的存储器中将不同大小的大型结构从一个地方移动到另一个地方。

ZMM: I may assist you in that endeavor, if you wish. Describe your problem.

ZMM:如果你愿意的话,我可以帮助你做出这样的努力。描述你的问题。

S: Specifically, I have a c function that I want to accept several different types of structs (they will be representing different type of packets). So my struct packets will be passed to my function as void*. But without knowing the type, I can't cast them, or really do much of anything. I know this is a solvable problem, because sento() from socket.h does exactly that:

S:具体来说,我有一个c函数,我想接受几种不同类型的结构(它们将代表不同类型的数据包)。所以我的struct包将作为void *传递给我的函数。但是在不知道类型的情况下,我无法投射它们,或者真的做了很多事情。我知道这是一个可解决的问题,因为来自socket.h的sento()正是这样做的:

    ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr,socklen_t dest_len);

where sendto would be called like:

sendto将被称为:

    sendto(socketAddress, &myPacket, sizeof(myPacket), Other args....);

ZMM: Did you describe your problem to Zen Master MANTAR! ?

ZMM:你有没有向Zen Master MANTAR描述你的问题! ?

S: Yeah, he said, "It's just a pointer. Everything in C is a pointer." When I asked him to explain, he said, "Bok, bok, get the hell out of my office."

S:是的,他说,“这只是一个指针.C中的所有东西都是指针。”当我让他解释时,他说,“博克,博克,让我离开我的办公室。”

ZMM: Truly, you have spoken to the master. Did this not help you?

ZMM:你真的和主人说过话。这对你没有帮助吗?

S: Um, er, no. Then I asked Zen Master Max.

S:恩,呃,不。然后我问Zen Master Max。

ZMM: Wise is he. What was his advice to you useful?

ZMM:明智的是他。他对你的建议有用吗?

S: No. When I asked him about sendto(), he just swirled his fists in the air. It's just an array of bytes."

S:不。当我问他关于sendto()时,他只是在空中旋转拳头。它只是一个字节数组。“

ZMM: Indeed, Zen Master Max has tau.

ZMM:的确,Zen Master Max有tau。

S: Yeah, he has tau, but how do I deal with function arguments of type void*?

S:是的,他有tau,但我如何处理void *类型的函数参数?

ZMM: To learn, you must first unlearn. The key is that you must realize that everything in a computer is just an array of bytes (or, words, or double words). Once you have a pointer to the beginning of a buffer, and the length of the buffer, you can sent it anywhere without a need to know the type of data placed in the buffer.

ZMM:要学习,你必须先忘掉。关键是你必须意识到计算机中的所有东西都只是一个字节数组(或单词或双字)。一旦有了指向缓冲区开头的指针和缓冲区的长度,就可以将它发送到任何地方而无需知道缓冲区中放置的数据类型。

S: OK.

ZMM: Consider a string of man-readable text. "You plan a tower that will pierce the clouds? Lay first the foundation of humility." It is 82 bytes long. Or, perhaps, 164 if the evil Unicode is used. Guard yourself against the lies of Unicode! I can submit this text to sendto() by providing a pointer to the beginning of the buffer that contains the string, and the length of the buffer, like so:

ZMM:考虑一串人类可读的文本。 “你计划一座能够刺破云层的塔楼?首先奠定谦卑的基础。”它长82个字节。或者,如果使用邪恶的Unicode,可能是164。保护自己免受Unicode的谎言!我可以通过提供指向包含字符串的缓冲区开头的指针和缓冲区的长度来将此文本提交给sendto(),如下所示:

char characterBuffer[300];      // 300 bytes
strcpy(characterBuffer, "You plan a tower that will pierce the clouds? Lay first the foundation of humility.");
// note that sizeof(characterBuffer) evaluates to 300 bytes.
sendto(socketAddress, &characterBuffer, sizeof(characterBuffer));

ZMM: Note well that the number of bytes of the character buffer is automatically calculated by the compiler. The number of bytes occupied by any variable type is of a type called "size_t". It is likely equivalent to the type "long" or "unsinged int", but it is compiler dependent.

ZMM:请注意,编译器会自动计算字符缓冲区的字节数。任何变量类型占用的字节数都是名为“size_t”的类型。它可能等同于“long”或“unsinged int”类型,但它依赖于编译器。

S: Well, what if I want to send a struct?

S:嗯,如果我想发送结构怎么办?

ZMM: Let us send a struct, then.

ZMM:那么让我们发一个结构。

struct
{
     int integerField;          // 4 bytes
     char characterField[300];  // 300 bytes
     float floatField;          // 4 bytes
} myStruct;

myStruct.integerField = 8765309;
strcpy(myStruct.characterField, "Jenny, I got your number.");
myStruct.floatField = 876.5309;

// sizeof(myStruct) evaluates to 4 + 300 + 4 = 308 bytes
sendto(socketAddress, &myStruct, sizeof(myStruct);

S: Yeah, that's great at transmitting things over TCP/IP sockets. But what about the poor receiving function? How can it tell if I am sending a character array or a struct?

S:是的,通过TCP / IP套接字进行传输非常棒。但是糟糕的接收功能呢?它如何判断我是否正在发送字符数组或结构?

ZMM: One way is to enumerate the different types of data that may be sent, and then send the type of data along with the data. Zen Masters refer to this as "metadata", that is to say, "data about the data". Your receiving function must examine the metadata to determine what kind of data (struct, float, character array) is being sent, and then use this information to cast the data back into its original type. First, consider the transmitting function:

ZMM:一种方法是枚举可能发送的不同类型的数据,然后将数据类型与数据一起发送。 Zen Masters将此称为“元数据”,即“有关数据的数据”。您的接收函数必须检查元数据以确定要发送的数据类型(struct,float,字符数组),然后使用此信息将数据转换回其原始类型。首先,考虑传输功能:

enum
{
    INTEGER_IN_THE_PACKET =0 ,
    STRING_IN_THE_PACKET =1,  
    STRUCT_IN_THE_PACKET=2
} typeBeingSent;

struct
{
     typeBeingSent dataType;
     char data[4096];
} Packet_struct;

Packet_struct myPacket;

myPacket.dataType = STRING_IN_THE_PACKET;
strcpy(myPacket.data, "Nothing great is ever achieved without much enduring.");
sendto(socketAddress, myPacket, sizeof(Packet_struct);

myPacket.dataType = STRUCT_IN_THE_PACKET;
memcpy(myPacket.data, (void*)&myStruct, sizeof(myStruct);
sendto(socketAddress, myPacket, sizeof(Packet_struct);

S: All right.

S:好的。

ZMM: Now, just us walk along with the receiving function. It must query the type of the data that was sent and the copy the data into a variable declared of that type. Forgive me, but I forget the exact for of the recvfrom() function.

ZMM:现在,我们一起走接收功能。它必须查询已发送数据的类型,并将数据复制到声明该类型的变量中。原谅我,但我忘记了recvfrom()函数的确切结果。

   char[300] receivedString;
   struct myStruct receivedStruct;  

   recvfrom(socketDescriptor, myPacket, sizeof(myPacket);

   switch(myPacket.dataType)
   {
       case STRING_IN_THE_PACKET:
            // note the cast of the void* data into type "character pointer"
            &receivedString[0] = (char*)&myPacket.data; 
            printf("The string in the packet was \"%s\".\n", receivedString); 
            break;

       case STRUCT_IN_THE_PACKET:
            // note the case of the void* into type "pointer to myStruct"
            memcpy(receivedStruct, (struct myStruct *)&myPacket.data, sizeof(receivedStruct));
            break;
    }

ZMM: Have you achieved enlightenment? First, one asks the compiler for the size of the data (a.k.a. the number of bytes) to be submitted to sendto(). You send the type of the original data is sent along as well. The receiver then queries for the type of the original data, and uses it to call the correct cast from "pointer to void" (a generic pointer), over to the type of the original data (int, char[], a struct, etc.)

ZMM:你获得了启蒙吗?首先,要求编译器提交要发送到sendto()的数据大小(a.k.a.字节数)。您发送原始数据的类型也是一样的。然后接收器查询原始数据的类型,并使用它从“指向void的指针”(通用指针)调用正确的转换,转换为原始数据的类型(int,char [],一个结构,等等。)

S: Well, I'll give it a try.

S:嗯,我试一试。

ZMM: Go in peace.

ZMM:安静地走吧。