简体   繁体   English

通过UDP winsock连接发送结构,结构的大小?

[英]Sending structs over a UDP winsock connection, size of struct?

I'll try to keep this short. 我会尽量保持这个简短。 So I'm making a network interface which, inherently will be used for creating simple video games. 所以我正在制作一个网络界面,其本身将用于创建简单的视频游戏。 I initially began designing it in TCP but I found it to be too slow so I switched to UDP. 我最初开始在TCP中设计它,但我发现它太慢,所以我切换到UDP。

My current idea is this: I want to send structs, where each struct corresponds to a specific message. 我目前的想法是:我想发送结构,每个结构对应一个特定的消息。 Of course, I need to know the size of the struct when the server or client receives it. 当然,我需要知道服务器或客户端收到它时的结构大小。 I fix this by first sending a standardized struct which contains the type of struct which is coming next, and then handle that accordingly. 我通过首先发送一个标准化的结构来解决这个问题,该结构包含下一个结构的类型,然后相应地处理它。 So I'll have something like a shared header file between the client and server which would look like this: 所以我会在客户端和服务器之间有类似共享的头文件,如下所示:

enum data_type { PLAYER_MESSAGE, PLAYER_LOCATION};

struct enum_type {
int nClientID;
data_type data_identifier;
};

struct player_message {
char message[128];
};

struct player_location {
int x, y;
};

Then, in the function that receives data sent by the other client I would do something like this: 然后,在接收其他客户端发送的数据的函数中,我会做这样的事情:

enum_type enum_check;
recvfrom(socket, (char*)&enum_check, sizeof(enum_type), 0, (struct sockaddr*)&client, &client_length);

switch(enum_check.data_identifier)
{

    case: PLAYER_MESSAGE
    player_message temp_message;
    recvfrom(socket, (char*)&temp_message, sizeof(player_message), 0, (struct sockaddr*)&client, client_length);
    //Print message
    break;

    case: PLAYER_LOCATION
    player_location temp_location;
    recvfrom(socket, (char*)&temp_location, sizeof(player_location), 0, (struct sockaddr*)&client, &client_length);
    //move player
    break;
}

The system works pretty well with all sorts of messages and also behaves decently under (some) stress. 该系统可以很好地处理所有类型的消息,并且在(某些)压力下表现得相当不错。 The problem of course is that every single client would be using the same recvfrom to first check the message type and then to handle it accordingly. 问题当然是每个客户端都会使用相同的recvfrom来首先检查消息类型然后相应地处理它。 In short, I don't trust this system one bit because it makes too many assumptions. 简而言之,我不相信这个系统,因为它做了太多的假设。 It makes the assumption that a receive giving a message type is always followed by the correct message. 它假设给出消息类型的接收始终跟随正确的消息。 With UDP, you can never really guarantee this is the case, which is leaving me stumped. 使用UDP,你永远无法保证这种情况,这让我感到难过。 (I am also aware of the problem with endianness and computer architectures with this approach, but I'm side-lining that for now.) (我也意识到使用这种方法的字节序和计算机体系结构的问题,但我现在正在为此做好准备。)

I figured a quick and easy approach would be to have only one type of struct, including the client ID, how to use its data (enum), room for around 10 numbers as well as some text. 我认为快速简单的方法是只有一种类型的结构,包括客户端ID,如何使用其数据(枚举),大约10个数字的空间以及一些文本。 This way I would not have to make assumptions but would of course send tons of needless data constantly (which wouldn't be a problem if bandwidth isn't really an issue, but it seems like a pretty dumb solution nonetheless). 这样我就不必做出假设,但当然会不断地发送大量不必要的数据(如果带宽不是真正的问题,这不会是一个问题,但它似乎是一个非常愚蠢的解决方案)。

Any suggestions here? 这里有什么建议? I've contemplated just using a network library such as ENET (then again, wouldn't I get the same problems trying to send structs?) but that seems like such a defeat. 我打算只使用像ENET这样的网络库(那么再次,我不会在尝试发送结构时遇到同样的问题吗?)但这似乎是一种失败。 I'm only doing this to learn after all. 毕竟我只是这样学习。

Good thing you already caught on to UDP sometimes reordering messages in arbitrary ways. 你已经接到UDP的好东西有时会以任意方式重新排序消息。

Your solution of always sending the same struct containing all possible info is nearly correct: 总是发送包含所有可能信息的相同结构的解决方案几乎是正确的:

Define yourself a base message, like eg: 定义自己的基本消息,例如:

struct msg_common{uint8_t sender, receiver; uint16_t message_id;};
// Take note that I used fixed-width types here!

And make all specific messages begin with that, reserving non-overlapping ranges of the common prefix for each one. 并使所有特定消息以此开头,为每个消息保留公共前缀的非重叠范围。

You won't get around validating that the message is valid, which means: 您无法验证消息是否有效,这意味着:

  • It is complete, aka. 它完整​​,又名。 the UDP packet contained the whole message. UDP数据包包含整个消息。
  • It should be processed now, aka. 它应该现在处理,也就是说。 there is no previous message which has to be processed first. 之前没有必须先处理的消息。
    • Either include all data neccessary for reconstructing previous non-acknowledged messages. 要么包括重建先前未确认消息所需的所有数据。
    • Or accept dropping an arbitrary amount of messages. 或者接受丢弃任意数量的消息。
    • Or allow for requesting a resend. 或者允许请求重新发送。
  • It does not violate some other semantic constraints of your game. 它不违反游戏的其他语义限制。

Why not use a union, of structs, where the 1st member gives the type of the struct and is common to all structs in the union. 为什么不使用结构的联合,第一个成员给出结构的类型,并且对于联合中的所有结构是通用的。 Your quick and easy approach isn't far off, since if all you have is 10 numbers the headers of the packets are going to dominate your bandwidth usage anyway. 您的快速简便方法并不遥远,因为如果您只拥有10个数字,那么数据包的标头无论如何都会主导您的带宽使用。 You really don't want one packet to check the type on the next - that is asking for trouble sinbce in UDP there is no guarantee that the packets arrive in the same order they were sent. 你真的不希望一个数据包检查下一个类型 - 这就是在UDP中要求麻烦,不能保证数据包按照它们发送的顺序到达。

I initially began designing it in TCP but I found it to be too slow so I switched to UDP. 我最初开始在TCP中设计它,但我发现它太慢,所以我切换到UDP。

I recommend reading this before you come into such conclusion: udp-vs-tcp-how-much-faster-is-it . 我建议你在得出这样的结论之前先阅读这篇文章: udp-vs-tcp- how-much-fast-is-it In most situration TCP is fairly good. 在大多数情况下,TCP相当不错。 I'm getting 400K packets per second for my TCP-based server running ping-pong test on entry level xeon i7 with dual NIC, and pretty quick latency. 我的基于TCP的服务器在入门级别的ie i7上使用双网卡运行乒乓测试,并且延迟非常快,我每秒获得400K数据包。

How fast do you need? 你需要多快?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM