ENet教程翻译
这篇文章原来是放在我QQ空间里的。现在把它copy过来。
ENet官网:http://enet.bespin.org/
该文原文出处:http://enet.bespin.org/Tutorial.html
本篇文章只是翻译,其中的源码也是官方的。而且源码是ENet1.3.0版的。后边的文章会有一个示例程序的源码。
这边有一个名词peer要理解。这个peer就是P2P(peer-to-peer)中的peer,个人感觉就是指对等网络中的一个节点。
本人英文水平有限,中文水平也有限,不明白地方还是看英文原文吧~~~这里将原文也贴出了。
至于代码里的注释。。。本人比较懒。。。
对照翻译如下:
enet
http://enet.bespin.orgENet's purpose is to provide a relatively thin, simple and robust network communication layer on top of UDP (User Datagram Protocol). The primary feature it provides is optional reliable, in-order delivery of packets.
ENet omits certain higher level networking features such as authentication, lobbying, server discovery, encryption, or other similar tasks that are particularly application specific so that the library remains flexible, portable, and easily embeddable.
Tutorial
InitializationSending a packet to an ENet peer
教程
初始化
创建一个ENet的服务端
创建一个ENet的客户端
管理一个ENet的host(主机端)
发送一个packet给一个ENet的peer
断开一个ENet peer的连接
连接到一个ENet的host(主机端)
Initialization
You should include the file <enet/enet.h> when using ENet. Do not include <enet.h> without the directory prefix, as this may cause file name conflicts on some systems.Before using ENet, you must call enet_initialize() to initialize the library. Upon program exit, you should call enet_deinitialize() so that the library may clean up any used resources.
初始化
当使用ENet的时候,你应该包含头文件<enet/enet.h>。不要只包含<enet.h>而没有文件夹前缀,因为在某些系统,这可能命名的冲突。
在使用ENet之前,你必须调用enet_initialize()以初始化链接库。当程序结束时你应该调用enet_deinitiakize(),这样链接库就可以清掉所有的用过的资源。
#include <enet/enet.h> int main (int argc, char ** argv) { if (enet_initialize () != 0) { fprintf (stderr, "An error occurred while initializing ENet.\n"); return EXIT_FAILURE; } atexit (enet_deinitialize); ... ... ... }
Creating an ENet server
Servers in ENet are constructed with enet_host_create(). You must specify an address on which to receive data and new connections, as well as the maximum allowable numbers of connected peers. You may optionally specify the incoming and outgoing bandwidth of the server in bytes per second so that ENet may try to statically manage bandwidth resources among connected peers in addition to its dynamic throttling algorithm; specifying 0 for these two options will cause ENet to rely entirely upon its dynamic throttling algorithm to manage bandwidth.When done with a host, the host may be destroyed with enet_host_destroy(). All connected peers to the host will be reset, and the resources used by the host will be freed.
创建一个ENet的Server(服务端)
ENet中用enet_host_create()构造Server。你必须指定一个地址用来接收数据和新的连接请求,最大的允许连接peer的数量。你也可以指定(可选)server传入和传出的带宽(bytes每秒),这样ENet除了会使用它的动态节流算法外,还会试着静态的处理连接的peer之间带宽资源;如果将这两个参数指定为0,ENet将会完全依赖于它的动态节流算法来处理带宽。
当要结束一个host(主机端口?)时,需要调用enet_host_destroy()来析构host。所有连接到该host的peers会被重置,所用被该host使用的资源将被释放。
ENetAddress address; ENetHost * server; /* Bind the server to the default localhost. */ /* A specific host address can be specified by */ /* enet_address_set_host (& address, "x.x.x.x"); */ address.host = ENET_HOST_ANY; /* Bind the server to port 1234. */ address.port = 1234; server = enet_host_create (& address /* the address to bind the server host to */, 32 /* allow up to 32 clients and/or outgoing connections */, 2 /* allow up to 2 channels to be used, 0 and 1 */, 0 /* assume any amount of incoming bandwidth */, 0 /* assume any amount of outgoing bandwidth */); if (server == NULL) { fprintf (stderr, "An error occurred while trying to create an ENet server host.\n"); exit (EXIT_FAILURE); } ... ... ... enet_host_destroy(server);
Creating an ENet client
Clients in ENet are similarly constructed with enet_host_create() when no address is specified to bind the host to. Bandwidth may be specified for the client host as in the above example. The peer count controls the maximum number of connections to other server hosts that may be simultaneously open.ENetHost * client; client = enet_host_create (NULL /* create a client host */, 1 /* only allow 1 outgoing connection */, 2 /* allow up 2 channels to be used, 0 and 1 */, 57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */, 14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */); if (client == NULL) { fprintf (stderr, "An error occurred while trying to create an ENet client host.\n"); exit (EXIT_FAILURE); } ... ... ... enet_host_destroy(client);
Managing an ENet host
ENet uses a polled event model to notify the programmer of significant events. ENet hosts are polled for events with enet_host_service(), where an optional timeout value in milliseconds may be specified to control how long ENet will poll; if a timeout of 0 is specified,enet_host_service() will return immediately if there are no events to dispatch. enet_host_service() will return 1 if an event was dispatched within the specified timeout.Currently there are only four types of significant events in ENet:
An event of type ENET_EVENT_TYPE_NONE is returned if no event occurred within the specified time limit. enet_host_service() will return 0 with this event.
An event of type ENET_EVENT_TYPE_CONNECT is returned when either a new client host has connected to the server host or when an attempt to establish a connection with a foreign host has succeeded. Only the "peer" field of the event structure is valid for this event and contains the newly connected peer.
An event of type ENET_EVENT_TYPE_RECEIVE is returned when a packet is received from a connected peer. The "peer" field contains the peer the packet was received from, "channelID" is the channel on which the packet was sent, and "packet" is the packet that was sent. The packet contained in the "packet" field must be destroyed with enet_packet_destroy() when you are done inspecting its contents.
An event of type ENET_EVENT_TYPE_DISCONNECT is returned when a connected peer has either explicitly disconnected or timed out. Only the "peer" field of the event structure is valid for this event and contains the peer that disconnected. Only the "data" field of the peer is still valid on a disconnect event and must be explicitly reset.
管理一个ENet 的host
ENet使用轮询模式来响应有效事件。通过enet_host_service()来对ENet hosts进行事件轮询。可以设置一个自选的超时时间(单位毫秒)来控制ENet轮询的间隔时间。如果将超时时间设为0,当没有事件要调度处理时enet_host_service()会立即返回。当有事件要调度处理时,enet_host_serveice()会在超时时间内返回1 。
当前ENet中只有四种有效事件类型:
返回ENET_EVENT_TYPE_NONE这种事件类型,表示在指定的时间限制内没有事件发生。在这种事件下,enet_host_service()会返回0 。
返回ENET_EVENT_TYPE_CONNECT这种事件类型,表示或者有一个新的client host连接到了server host,或者一个和外部host建立连接的尝试已经成功。对于这种事件,事件结构体中只有”peer”字段是有效的,而且”peer”字段包含着新连接的peer的信息。
返回ENET_EVENT_TYPE_RECEIVE这种事件类型,表示从一个连接的peer处接收到一个packet(数据包)。”peer”字段包含该packet来自的peer的信息,”ChannelID”是该packet发过来是使用的channel(频道),”packet”字段就是该被发送的packet 。当你检查完”packet”字段中的packet的内容后,必须使用enet_packet_destroy()来将该packet析构。
返回ENET_EVENT_TYPE_DISCONNECT这种事件类型,表示一个连接的peer或者显式断开或者超时了。对于这种事件,事件结构中只有packet字段是有效的,而且”peer”字段包含着该断开的peer的信息,peer中只有”data”字段在连接断开事件时依然有效,必须显式地重置。
ENetEvent event; /* Wait up to 1000 milliseconds for an event. */ while (enet_host_service (client, & event, 1000) > 0) { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: printf ("A new client connected from %x:%u.\n", event.peer -> address.host, event.peer -> address.port); /* Store any relevant client information here. */ event.peer -> data = "Client information"; break; case ENET_EVENT_TYPE_RECEIVE: printf ("A packet of length %u containing %s was received from %s on channel %u.\n", event.packet -> dataLength, event.packet -> data, event.peer -> data, event.channelID); /* Clean up the packet now that we're done using it. */ enet_packet_destroy (event.packet); break; case ENET_EVENT_TYPE_DISCONNECT: printf ("%s disconected.\n", event.peer -> data); /* Reset the peer's client information. */ event.peer -> data = NULL; } } ... ... ...
Sending a packet to an ENet peer
Packets in ENet are created with enet_packet_create(), where the size of the packet must be specified. Optionally, initial data may be specified to copy into the packet.Certain flags may also be supplied to enet_packet_create() to control various packet features:
ENET_PACKET_FLAG_RELIABLE specifies that the packet must use reliable delivery. A reliable packet is guarenteed to be delivered, and a number of retry attempts will be made until an acknowledgement is received from the foreign host the packet is sent to. If a certain number of retry attempts is reached without any acknowledgement, ENet will assume the peer has disconnected and forcefully reset the connection. If this flag is not specified, the packet is assumed an unreliable packet, and no retry attempts will be made nor acknowledgements generated.
A packet may be resized (extended or truncated) with enet_packet_resize().
A packet is sent to a foreign host with enet_peer_send(). enet_peer_send() accepts a channel id over which to send the packet to a given peer. Once the packet is handed over to ENet with enet_peer_send(), ENet will handle its deallocation andenet_packet_destroy() should not be used upon it.
One may also use enet_host_broadcast() to send a packet to all connected peers on a given host over a specified channel id, as with enet_peer_send().
Queued packets will be sent on a call to enet_host_service(). Alternatively, enet_host_flush() will send out queued packets without dispatching any events.
发送一个packet给一个ENet的peer
ENet中用enet_packet_create()产生packet,packet的大小必须指定。可选地,初始的数据可以指定并copy到packet中。
某些标志也可以传到enet_packet_create()来控制各种packet的特性。
ENET_PACKET_FLAG_RELIABLE 指定packet必须使用可靠的传输。一个可靠的packet是保证会被被传送出去的,该传输可能会有一定数量的重发直到发送方接收到packet的目标host发出的确认信息(就是三次握手)。如果到达了设定的重发次数,发送方依然没有接收到确认信息,ENet将会认为该peer已经断开了连接,并且会强制地重置该连接。如果这个标志位没被设置,该packet会被认为是不可靠packet,不会尝试重发也不会产生确认信息。
一个packet可以用enet_packet_resize()来重设定大小(扩展或截断)。
一个packet通过用enet_peer_send()发到外部host。enet_peer_send()接受一个channel的id,并通过该channel来发送该packet到给定的peer。当packet通过enet_peer_send()交给ENet后,ENet将会处理它的释放问题,不要对它使用enet_packet_destroy()。
你也可以使用enet_host_broadcast()来将一个packet通过给定的host的指定channel发送给所有连接着的peer,就像用enet_peer_send()。
当调用enet_host_service()时,排队的packets 会被发送。另外,enet_host_flush()将发送排队的packets而不处理任何事件。
/* Create a reliable packet of size 7 containing "packet\0" */ ENetPacket * packet = enet_packet_create ("packet", strlen ("packet") + 1, ENET_PACKET_FLAG_RELIABLE); /* Extend the packet so and append the string "foo", so it now */ /* contains "packetfoo\0" */ enet_packet_resize (packet, strlen ("packetfoo") + 1); strcpy (& packet -> data [strlen ("packet")], "foo"); /* Send the packet to the peer over channel id 0. */ /* One could also broadcast the packet by */ /* enet_host_broadcast (host, 0, packet); */ enet_peer_send (peer, 0, packet); ... ... ... /* One could just use enet_host_service() instead. */ enet_host_flush (host);
Disconnecting an ENet peer
Peers may be gently disconnected with enet_peer_disconnect(). A disconnect request will be sent to the foreign host, and ENet will wait for an acknowledgement from the foreign host before finally disconnecting. An event of type ENET_EVENT_TYPE_DISCONNECT will be generated once the disconnection succeeds. Normally timeouts apply to the disconnect acknowledgement, and so if no acknowledgement is received after a length of time the peer will be forcefully disconnected.enet_peer_reset() will forcefully disconnect a peer. The foreign host will get no notification of a disconnect and will time out on the foreign host. No event is generated.
断开一个ENet peer的连接
Peers 可以通过enet_peer_disconnect()来平缓地断开连接。一个断开连接的请求会被发送到外部host,ENet在最后断开连接前,会等待来自外部主机的确认。一个事件类型为ENET_EVENT_TYPE_DISCONNECT的事件会产生当成功地断开连接。通常超时应用于断开连接的确认,所以如果一定时间后没收到确认信息,那个peer会被强制断开。
使用enet_peer_rest()会强制断开一个peer的连接。外部host将不会得到断开连接的通知,并且会认为外部host超时(?)。不会有事件产生。
ENetEvent event; enet_peer_disconnect (peer, 0); /* Allow up to 3 seconds for the disconnect to succeed and drop any packets received packets. */ while (enet_host_service (client, & event, 3000) > 0) { switch (event.type) { case ENET_EVENT_TYPE_RECEIVE: enet_packet_destroy (event.packet); break; case ENET_EVENT_TYPE_DISCONNECT: puts ("Disconnection succeeded."); return; ... ... ... } } /* We've arrived here, so the disconnect attempt didn't */ /* succeed yet. Force the connection down. */ enet_peer_reset (peer); ... ... ...
Connecting to an ENet host
A connection to a foreign host is initiated with enet_host_connect(). It accepts the address of a foreign host to connect to, and the number of channels that should be allocated for communication. If N channels are allocated for use, their channel ids will be numbered 0 through N-1. A peer representing the connection attempt is returned, or NULL if there were no available peers over which to initiate the connection. When the connection attempt succeeds, an event of type ENET_EVENT_TYPE_CONNECT will be generated. If the connection attempt times out or otherwise fails, an event of type ENET_EVENT_TYPE_DISCONNECT will be generated.ENetAddress address; ENetEvent event; ENetPeer *peer; /* Connect to some.server.net:1234. */ enet_address_set_host (& address, "some.server.net"); address.port = 1234; /* Initiate the connection, allocating the two channels 0 and 1. */ peer = enet_host_connect (client, & address, 2, 0); if (peer == NULL) { fprintf (stderr, "No available peers for initiating an ENet connection.\n"); exit (EXIT_FAILURE); } /* Wait up to 5 seconds for the connection attempt to succeed. */ if (enet_host_service (client, & event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { puts ("Connection to some.server.net:1234 succeeded."); ... ... ... } else { /* Either the 5 seconds are up or a disconnect event was */ /* received. Reset the peer in the event the 5 seconds */ /* had run out without any significant event. */ enet_peer_reset (peer); puts ("Connection to some.server.net:1234 failed."); } ... ... ...