|
| 1 | +# Polymorph Network Library (UDP part) |
| 2 | +> Here is the documentation for the UDP part. If you want to implement an TCP connection check [this](TCP.md) out. |
| 3 | +
|
| 4 | +As mentioned in the [README](/readme.md), the library has 2 major class that you will have to instantiate. For UDP those class are `polymorph::network::udp::Server` and `polymorph::network::udp::Client`. There is also `polymorph::network::udp::Connector` which is really important as it handles send and receive operations |
| 5 | + |
| 6 | +For readability’s sake, namespaces `polymorph::network::` and `polymorph::network::udp::` will be omitted but keep in mind you need to mention them or use the following directive : |
| 7 | +```c++ |
| 8 | +using namespace polymorph::networ; |
| 9 | +using namespace polymorph::network::udp; |
| 10 | +``` |
| 11 | +
|
| 12 | +## UDP specifics |
| 13 | +The implemented protocol is based on top of UDP so there is no builtin confirmation that a packet has been received. Fortunately, the library provides a builtin resend timeout and acknowledgement of packets. |
| 14 | +This is why you have to create a "safeties mapping", which is a map of `OpId` to `bool`. This map will be used to know if a packet is "important" or not and needs to be resent until acknowledgement. |
| 15 | +
|
| 16 | +
|
| 17 | +## Server |
| 18 | +
|
| 19 | +### 1) Creating a server |
| 20 | +The `Server` have a constructor that takes a port and a `SessionStore` as parameters. You can create a new Session for the server sake or use one that is already used by **ONE UDP server**. |
| 21 | +You will also have to create a connector with a reference of the created server. Then, you will have to reference the connector in the server with the `Server::setConnector(std::shared_ptr<Connector>)` method. |
| 22 | +```cpp |
| 23 | +SessionStore sessionStoreServer; // Session store of the server (store any client connection) |
| 24 | +// Then, you can create the server |
| 25 | +// Simply pass the session store, the mapping and the port you want the server to use |
| 26 | +
|
| 27 | +std::map<OpId, bool> safetiesMapping = { |
| 28 | + { SampleDto::opId, true }, // SampleDto is an important packet, it will be resent if we do not receive a confirmation of its reception. It will also send an ACK packet if the server receives a packet with this OpId |
| 29 | + { AnotherDto::opId, false } // This packet is not important, it will be sent once |
| 30 | +}; |
| 31 | +
|
| 32 | +Server server(4242, safetiesMapping, sessionStoreServer); |
| 33 | +// Then, you have to create a connector |
| 34 | +auto connector = std::make_shared<Connector>(server); |
| 35 | +// And finally, you have to reference the connector in the server |
| 36 | +server.setConnector(connector); |
| 37 | +``` |
| 38 | +It is recommended to put a public static variable in all your DTOs to always have their associated opId. This is why ```SampleDto::opId``` is used in the snippet. |
| 39 | + |
| 40 | + |
| 41 | +### 2) Register packet handlers |
| 42 | +You can now register callbacks to handle received packet. Here is an example of registering a callback for a packet with a payload of type `SampleDto` : |
| 43 | +```c++ |
| 44 | +server->registerReceiveHandler<SampleDto>(SampleDto::opId, [](const PacketHeader &header, const APyaloadType &payload) { |
| 45 | + std::cout << "Server received \"" << payload.value << "\" from client with session id " << header.sId << std::endl; |
| 46 | + // Note that before calling the callback, the server will check if the packet is important or not. If it is, it will send an ACK packet |
| 47 | +}); |
| 48 | +``` |
| 49 | + |
| 50 | +🎉 Congratulations, you have created your server and registered your first callback ! |
| 51 | + |
| 52 | +### 3) Start the server |
| 53 | +Now you will have to start it in order to accept incoming connections. To do so, call the `Connector::start()` method. |
| 54 | +> __Warning__ |
| 55 | +> Event if there is a `Server::start()` method, you have to call the `Connector::start()` method. This is because the server is not the one that will handle the incoming connections, it is the connector. The server will only handle the packets received by the connector. |
| 56 | + |
| 57 | +```c++ |
| 58 | +connector->start(); |
| 59 | +``` |
| 60 | + |
| 61 | +### 4) Send packets |
| 62 | +You can now send packets to your clients. To do so, you will have to get the `SessionId` of the client you want to send a packet to. You can find it in received packet callbacks in the header. |
| 63 | +```cpp |
| 64 | +server.sendTo<SampleDto>(SampleDto::opId, payload, clientSessionId, [](const PacketHEader &header, const SampleDto &payload) { |
| 65 | + std::cout << "Server sent packet to client" << std::endl; |
| 66 | +}); |
| 67 | +// or, to send to all clients |
| 68 | +server.send(SampleDto::opId, payload); |
| 69 | +``` |
| 70 | + |
| 71 | +## Client |
| 72 | + |
| 73 | +### 1) Creating a client |
| 74 | +The `Client` have a constructor that takes a host string, a port and a `std::map<OpId, bool>` as parameters . You have to pass the address and the port of a running (or soon) server. |
| 75 | +The safety mapping should be the same as the one used by the server. You will also have to create a connector with a reference of the created client. Then, you will have to reference the connector in the client with the `Client::setConnector(std::shared_ptr<Connector>)` method. |
| 76 | +```cpp |
| 77 | +std::map<OpId, bool> safetiesMapping = { |
| 78 | + { SampleDto::opId, true }, // SampleDto is an important packet, it will be resent if we do not receive a confirmation of its reception. It will also send an ACK packet if the client receives a packet with this OpId |
| 79 | + { AnotherDto::opId, false } // This packet is not important, it will be sent once |
| 80 | +}; |
| 81 | + |
| 82 | +Client client("127.0.0.1", 4242, safetiesMapping); |
| 83 | +``` |
| 84 | +
|
| 85 | +### 2) Registering callbacks |
| 86 | +You can now register callbacks to handle received packet. Here is an example of registering a callback for a packet with a payload of type `SampleDto` : |
| 87 | +```c++ |
| 88 | +client.registerReceiveHandler<SampleDto>(SampleDto::opId, [](const PacketHeader &header, const APyaloadType &payload) { |
| 89 | + std::cout << "Client received : " << payload.value << std::endl; |
| 90 | + // Note that before calling the callback, the client will check if the packet is important or not. If it is, it will send an ACK packet |
| 91 | +}); |
| 92 | +``` |
| 93 | + |
| 94 | +### 3) Connecting to the server |
| 95 | +You can now call the `Client::connect()` to initiate the connection with the server. You pass a callback which will be called when the server has accepted/refused the connection. |
| 96 | +```c++ |
| 97 | +client.connect([](bool authorized, SessionId id) { |
| 98 | + if (authorized) { |
| 99 | + std::cout << "Connected with session id " << id << std::endl; |
| 100 | + } else { |
| 101 | + std::cout << "Connection failed" << std::endl; |
| 102 | + } |
| 103 | +}); |
| 104 | +``` |
| 105 | + |
| 106 | +### 4) Sending packets |
| 107 | +You can now send packets to the server. Here is an example of sending a packet with a payload of type `SampleDto` : |
| 108 | +```c++ |
| 109 | +SampleDto payload; |
| 110 | +payload.value = 42; |
| 111 | +client.send(SampleDto::opId, payload, [](const PacketHeader &header, const SampleDto &payload) { |
| 112 | + std::cout << "Client sent packet to server"; |
| 113 | +}); |
| 114 | +``` |
| 115 | + |
| 116 | + |
| 117 | +## Server and Client |
| 118 | +You can see a complete example of echo UDP server and client in the [example](/examples/udpEcho) folder. |
0 commit comments