Ocean
Loading...
Searching...
No Matches
PackagedSocket.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef FACEBOOK_NETWORK_PACKAGED_SOCKET_H
9#define FACEBOOK_NETWORK_PACKAGED_SOCKET_H
10
13
14#include "ocean/base/Memory.h"
15
16#include "ocean/io/Bitstream.h"
17
18#include <queue>
19
20namespace Ocean
21{
22
23namespace Network
24{
25
26/**
27 * This class is the base class for all packaged sockets.
28 * Packaged sockets add a framing layer on top of the raw socket, so that a message survives being split or merged in transit.
29 * Two independent and mutually incompatible formats exist, both defined here:
30 * - the stream oriented PackageHeader, used by PackagedTCPClient and PackagedTCPServer
31 * - the datagram oriented package management header, used by PackagedConnectionlessClient and PackagedConnectionlessServer, see packageManagmentHeaderSize()
32 * @ingroup network
33 */
34class OCEAN_NETWORK_EXPORT PackagedSocket : virtual public Socket
35{
36 protected:
37
38 /**
39 * This class implements a memory block.
40 */
41 class OCEAN_NETWORK_EXPORT MemoryBlock
42 {
43 public:
44
45 /**
46 * Default constructor creating an empty memory block.
47 */
48 MemoryBlock() = default;
49
50 /**
51 * Creates a new memory block.
52 * @param size The number of bytes the memory bock will contain, with range [1, infinity)
53 */
54 explicit MemoryBlock(const size_t size);
55
56 /**
57 * Creates a new memory block and copied memory from source data.
58 * @param data The source data to be copied, must be valid
59 * @param size The number of bytes in the source data, with range [1, infinity)
60 */
61 MemoryBlock(const void* data, const size_t size);
62
63 /**
64 * Returns the pointer to the start of the memory block.
65 * @return Pointer to the start of the memory
66 */
67 inline const void* data() const;
68
69 /**
70 * Returns the pointer to the start of the memory block.
71 * @return Pointer to the start of the memory
72 */
73 inline void* data();
74
75 /**
76 * Returns the pointer to the offset location within the memory block.
77 * @return Pointer to offset location within the memory
78 */
79 inline const void* offsetData() const;
80
81 /**
82 * Returns the pointer to the offset location within the memory block.
83 * @return Pointer to offset location within the memory
84 */
85 inline void* offsetData();
86
87 /**
88 * Returns the overall number of bytes in the memory block.
89 * @return The memory block's size in bytes (the capacity may still be larger), with range [0, infinity)
90 */
91 inline size_t size() const;
92
93 /**
94 * Returns the number of remaining bytes.
95 * @return The memory's remaining bytes, with range [0, infinity)
96 */
97 inline size_t remainingBytes() const;
98
99 /**
100 * Resets the memory offset location back to the start.
101 */
102 inline void resetOffset();
103
104 /**
105 * Moves the offset within the memory block.
106 * @param delta The number of bytes the offset will be moved, with range [0, remainingBytes()]
107 */
108 inline void moveOffset(const size_t delta);
109
110 /**
111 * Resizes the memory block.
112 * @param size The new size in bytes, with range [0, infinity)
113 */
114 void resize(const size_t size);
115
116 /**
117 * Returns whether this memory block holds memory.
118 * @return True, if so
119 */
120 inline bool isValid() const;
121
122 protected:
123
124 /// The actual memory, can be larger than 'size_'.
126
127 /// The actual size of the usable memory, in bytes.
128 size_t size_ = 0;
129
130 /// The offset within the memory.
131 size_t offset_ = 0;
132 };
133
134 /**
135 * Definition of a queue holding memory blocks.
136 */
137 using MemoryBlockQueue = std::queue<MemoryBlock>;
138
139 /**
140 * This class implements the header of a packaged stream message.
141 * The header is written as a raw struct in host byte order, directly followed by the payload:
142 * @code
143 * |<---------------------- 24 byte header ----------------------->|< payload >|
144 * +-------------------+-----------------+-------------------------+-----------+
145 * | tag | version | size | payload |
146 * | uint64_t | uint64_t | uint64_t | size byte |
147 * | host byte order | host byte order | host byte order | |
148 * | always "_OCNPKG_" | always 0 | number of payload bytes | |
149 * +-------------------+-----------------+-------------------------+-----------+
150 * @endcode
151 * `tag` is the constant "_OCNPKG_" and is identical in every package.
152 * The receiver only accepts a header carrying it.
153 * This is how a desynchronized stream, or a peer which is not speaking this protocol, is detected instead of arbitrary bytes being interpreted as a length.
154 * `version` is the version of this format and is currently always 0.
155 * isValid() rejects any other value, so a future change of the layout is detected by an old receiver rather than silently misparsed.
156 * `size` is the number of payload bytes which follow the header, with range [1, maximalPackagedMessageSize()], and does not include the 24 header bytes.
157 * The payload of one message is never split by the sender.
158 * The receiver still has to reassemble it, as TCP may deliver the header and the payload across any number of segments.
159 * Beware: the three fields are in host byte order, so this format does not survive between hosts of different endianness.
160 */
162 {
163 public:
164
165 /**
166 * Creates an invalid header.
167 */
168 PackageHeader() = default;
169
170 /**
171 * Creates a valid header with specified size field.
172 * @param size The number of bytes the payload will have, with range [1, infinity)
173 */
174 explicit inline PackageHeader(const size_t size);
175
176 /**
177 * Returns whether this header is valid.
178 * @return True, if so
179 */
180 inline bool isValid() const;
181
182 /**
183 * Returns the number of bytes the payload will have.
184 * @return The payload size in bytes, with range [1, infinity)
185 */
186 inline size_t size() const;
187
188 protected:
189
190 /**
191 * Returns the unique tag for this header.
192 * @return The header's unique tag
193 */
194 static constexpr uint64_t oceanTag();
195
196 protected:
197
198 /// The header's tag.
199 uint64_t tag_ = 0ull;
200
201 /// The header's version.
202 uint64_t version_ = 0ull;
203
204 /// The number of bytes the payload will have.
205 uint64_t size_ = 0ull;
206 };
207
208 static_assert(sizeof(PackageHeader) == sizeof(uint64_t) * 3, "Invalid header!");
209
210 public:
211
212 /**
213 * Definition of a message id.
214 */
215 using MessageId = uint32_t;
216
217 /**
218 * Returns an invalid message id.
219 * @return Invalid message id
220 */
221 static constexpr MessageId invalidMessageId();
222
223 /**
224 * Returns the size of the package management header in bytes.
225 * Unlike the stream oriented PackageHeader, this header describes one fragment of a message which the sender has split across several datagrams:
226 * @code
227 * |<-------------------- 20 byte package management header -------------------->|<-------- payload --------->|
228 * +------------+-------------+-------------------+--------------+---------------+----------------------------+
229 * | messageId | messageSize | dataStartPosition | packageIndex | totalPackages | payload |
230 * | uint32_t | uint32_t | uint32_t | uint32_t | uint32_t | at most maximalPackageSize |
231 * | big endian | big endian | big endian | big endian | big endian | minus 20 byte |
232 * +------------+-------------+-------------------+--------------+---------------+----------------------------+
233 * @endcode
234 * `messageId` is a counter which the sender increments for every message it sends, and it is what ties the fragments of one message together.
235 * It is unique per sender only, so a message is identified by (sender address, sender port, `messageId`).
236 * `messageSize` is the size of the whole message rather than of the fragment carrying it.
237 * Every fragment repeats it, which lets the receiver allocate the reassembly buffer from whichever fragment happens to arrive first.
238 * `dataStartPosition` is the byte offset of this fragment's payload within the whole message.
239 * It allows the receiver to place a fragment without knowing which ones came before it.
240 * `packageIndex` is the index of this fragment, with range [0, `totalPackages`).
241 * It identifies the fragment itself, which is what allows a duplicate to be recognized as one.
242 * `totalPackages` is the number of fragments the message was split into, and the message is complete once every index has been seen.
243 * The last three fields are what makes the format order independent, as UDP may deliver fragments out of order, duplicated, or not at all.
244 * @return The header's size in bytes
245 */
246 static constexpr size_t packageManagmentHeaderSize();
247
248 /**
249 * Returns the maximal size of a packaged message.
250 * @return The maximal size of a packaged message in bytes
251 */
252 static constexpr size_t maximalPackagedMessageSize();
253
254 protected:
255
256 /**
257 * Creates a new packaged socket.
258 */
260
261 /**
262 * Extracts a memory block from a queue with memory block.
263 * @param sourceQueue The queue from which the memory will be extracted
264 * @param targetMemory The target memory block to be filled, must be valid, must have remaining bytes
265 * @return True, if the target memory block has been filled and no byte is remaining anymore
266 */
267 static bool extractNextPackage(MemoryBlockQueue& sourceQueue, MemoryBlock& targetMemory);
268};
269
270inline const void* PackagedSocket::MemoryBlock::data() const
271{
272 ocean_assert(size_ < memory_.size());
273
274 return memory_.constdata<uint8_t>();
275}
276
278{
279 ocean_assert(size_ <= memory_.size());
280
281 return memory_.data<uint8_t>();
282}
283
285{
286 ocean_assert(size_ < memory_.size());
287 ocean_assert(offset_ < size_);
288
289 return memory_.constdata<uint8_t>() + offset_;
290}
291
293{
294 ocean_assert(size_ <= memory_.size());
295 ocean_assert(offset_ < size_);
296
297 return memory_.data<uint8_t>() + offset_;
298}
299
301{
302 return size_;
303}
304
306{
307 offset_ = 0;
308}
309
310inline void PackagedSocket::MemoryBlock::moveOffset(const size_t delta)
311{
312 ocean_assert(delta <= remainingBytes());
313
314 offset_ += delta;
315
316 ocean_assert(offset_ <= size_);
317}
318
320{
321 ocean_assert(offset_ <= size_);
322 return size_ - offset_;
323}
324
326{
327 return bool(memory_);
328}
329
331 tag_(oceanTag()),
332 size_(size)
333{
334 // nothing to do here
335}
336
338{
339 return tag_ == oceanTag() && version_ == 0ull && size_ != 0ull;
340}
341
343{
344 return size_t(size_);
345}
346
348{
349 return IO::Tag::string2tag("_OCNPKG_");
350}
351
356
358{
359 return sizeof(MessageId) + sizeof(unsigned int) + sizeof(unsigned int) + sizeof(unsigned int) + sizeof(unsigned int);
360}
361
363{
364 return 1024 * 1024 * 1024; // 1GB
365}
366
367}
368
369}
370
371#endif // FACEBOOK_NETWORK_PACKAGED_SOCKET_H
static constexpr unsigned long long string2tag(const char tagString[8])
Converts a string with exactly eight characters to a unique tag value.
Definition Bitstream.h:455
This class implements an object able to allocate memory.
Definition base/Memory.h:22
size_t size() const
Returns the size of the memory in bytes.
Definition base/Memory.h:386
const void * constdata() const
Returns the pointer to the read-only memory which is allocated by this object.
Definition base/Memory.h:298
This class implements a memory block.
Definition PackagedSocket.h:42
MemoryBlock(const size_t size)
Creates a new memory block.
void resetOffset()
Resets the memory offset location back to the start.
Definition PackagedSocket.h:305
size_t size() const
Returns the overall number of bytes in the memory block.
Definition PackagedSocket.h:300
MemoryBlock(const void *data, const size_t size)
Creates a new memory block and copied memory from source data.
Memory memory_
The actual memory, can be larger than 'size_'.
Definition PackagedSocket.h:125
void moveOffset(const size_t delta)
Moves the offset within the memory block.
Definition PackagedSocket.h:310
const void * offsetData() const
Returns the pointer to the offset location within the memory block.
Definition PackagedSocket.h:284
MemoryBlock()=default
Default constructor creating an empty memory block.
void resize(const size_t size)
Resizes the memory block.
bool isValid() const
Returns whether this memory block holds memory.
Definition PackagedSocket.h:325
size_t size_
The actual size of the usable memory, in bytes.
Definition PackagedSocket.h:128
const void * data() const
Returns the pointer to the start of the memory block.
Definition PackagedSocket.h:270
size_t remainingBytes() const
Returns the number of remaining bytes.
Definition PackagedSocket.h:319
This class implements the header of a packaged stream message.
Definition PackagedSocket.h:162
size_t size() const
Returns the number of bytes the payload will have.
Definition PackagedSocket.h:342
PackageHeader()=default
Creates an invalid header.
bool isValid() const
Returns whether this header is valid.
Definition PackagedSocket.h:337
static constexpr uint64_t oceanTag()
Returns the unique tag for this header.
Definition PackagedSocket.h:347
This class is the base class for all packaged sockets.
Definition PackagedSocket.h:35
static bool extractNextPackage(MemoryBlockQueue &sourceQueue, MemoryBlock &targetMemory)
Extracts a memory block from a queue with memory block.
PackagedSocket()
Creates a new packaged socket.
std::queue< MemoryBlock > MemoryBlockQueue
Definition of a queue holding memory blocks.
Definition PackagedSocket.h:137
static constexpr size_t packageManagmentHeaderSize()
Returns the size of the package management header in bytes.
Definition PackagedSocket.h:357
static constexpr MessageId invalidMessageId()
Returns an invalid message id.
Definition PackagedSocket.h:352
uint32_t MessageId
Definition of a message id.
Definition PackagedSocket.h:215
static constexpr size_t maximalPackagedMessageSize()
Returns the maximal size of a packaged message.
Definition PackagedSocket.h:362
This class is the base class for all sockets.
Definition Socket.h:31
The namespace covering the entire Ocean framework.
Definition Accessor.h:15