-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnetudp.h
More file actions
365 lines (296 loc) · 11.8 KB
/
netudp.h
File metadata and controls
365 lines (296 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/*
Reference:
Simple Network Library from "Networking for Game Programmers"
http://www.gaffer.org/networking-for-game-programmers
Author: Glenn Fiedler <gaffer@gaffer.org>
*/
#ifndef _NETUDP_H_
#define _NETUDP_H_yy
// platform detection
#define PLATFORM_WINDOWS 1
#define PLATFORM_MAC 2
#define PLATFORM_UNIX 3
#if defined(_WIN32)
#define PLATFORM PLATFORM_WINDOWS
#elif defined(__APPLE__)
#define PLATFORM PLATFORM_MAC
#else
#define PLATFORM PLATFORM_UNIX
#endif
#if PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#pragma comment( lib, "wsock32.lib" )
#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#else
#error unknown platform!
#endif
#include <string.h>
#include <assert.h>
#include <vector>
#include <map>
#include <stack>
#include <list>
#include <algorithm>
#include <functional>
namespace dragonfighting {
// internet address
class Address
{
public:
Address()
{
address = 0;
port = 0;
}
Address( unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned short port )
{
this->address = ( a << 24 ) | ( b << 16 ) | ( c << 8 ) | d;
this->port = port;
}
Address( unsigned int address, unsigned short port )
{
this->address = address;
this->port = port;
}
unsigned int GetAddress() const
{
return address;
}
unsigned char GetA() const
{
return ( unsigned char ) ( address >> 24 );
}
unsigned char GetB() const
{
return ( unsigned char ) ( address >> 16 );
}
unsigned char GetC() const
{
return ( unsigned char ) ( address >> 8 );
}
unsigned char GetD() const
{
return ( unsigned char ) ( address );
}
unsigned short GetPort() const
{
return port;
}
bool operator == ( const Address & other ) const
{
return address == other.address && port == other.port;
}
bool operator != ( const Address & other ) const
{
return ! ( *this == other );
}
bool operator < ( const Address & other ) const
{
// note: this is so we can use address as a key in std::map
if ( address < other.address )
return true;
if ( address > other.address )
return false;
else
return port < other.port;
}
private:
unsigned int address;
unsigned short port;
};
// sockets
inline bool InitializeSockets()
{
#if PLATFORM == PLATFORM_WINDOWS
WSADATA WsaData;
return WSAStartup( MAKEWORD(2,2), &WsaData ) != NO_ERROR;
#else
return true;
#endif
}
inline void ShutdownSockets()
{
#if PLATFORM == PLATFORM_WINDOWS
WSACleanup();
#endif
}
class Socket
{
private:
int socket;
public:
Socket();
~Socket();
void Open(unsigned short port);
void Close();
bool IsOpen() const;
bool Send(const Address &destination, const void *data, int size);
int Receive(Address &sender, void *data, int size);
};
class Connection
{
public:
enum Mode {
None,
Client,
Server
};
Connection(unsigned int protocolId, float timeout);
virtual ~Connection();
bool Start(int port);
void Stop();
void Listen();
void Connect(const Address &address);
bool IsRunning() const { return running; }
bool IsConnecting() const { return state == Connecting; }
bool IsConnectFailed() const { return state == ConnectFail; }
bool IsConnected() const { return state == Connected; }
bool IsListening() const { return state == Listening; }
Mode GetMode() const { return mode; }
int GetHeaderSize() const { return 4; }
virtual void Update(float dt);
virtual bool SendPacket(const void *data, int size);
virtual int ReceivePacket(void *data, int size);
protected:
virtual void OnStart() {}
virtual void OnStop() {}
virtual void OnConnect() {}
virtual void OnDisconnect() {}
private:
enum State
{
Disconnected,
Listening,
Connecting,
ConnectFail,
Connected
};
unsigned int protocolId;
float timeout;
bool running;
Mode mode;
State state;
Socket socket;
float timeoutAccumulator;
Address address;
void ClearData()
{
state = Disconnected;
timeoutAccumulator = 0.0f;
address = Address();
}
};
// packet queue to store information about sent and received packets sorted in sequence order
// + we define ordering using the "sequence_more_recent" function, this works provided there is a large gap when sequence wrap occurs
struct PacketData
{
unsigned int sequence; // packet sequence number
float time; // time offset since packet was sent or received (depending on context)
int size; // packet size in bytes
};
inline bool sequence_more_recent( unsigned int s1, unsigned int s2, unsigned int max_sequence )
{
return (( s1 > s2 ) && ( s1 - s2 <= max_sequence/2 )) || (( s2 > s1 ) && ( s2 - s1 > max_sequence/2 ));
}
class PacketQueue : public std::list<PacketData>
{
public:
bool exists( unsigned int sequence );
void insert_sorted( const PacketData & p, unsigned int max_sequence );
void verify_sorted( unsigned int max_sequence );
};
class ReliabilitySystem
{
private:
unsigned int max_sequence; // maximum sequence value before wrap around (used to test sequence wrap at low # values)
unsigned int local_sequence; // local sequence number for most recently sent packet
unsigned int remote_sequence; // remote sequence number for most recently received packet
unsigned int sent_packets; // total number of packets sent
unsigned int recv_packets; // total number of packets received
unsigned int lost_packets; // total number of packets lost
unsigned int acked_packets; // total number of packets acked
float sent_bandwidth; // approximate sent bandwidth over the last second
float acked_bandwidth; // approximate acked bandwidth over the last second
float rtt; // estimated round trip time
float rtt_maximum; // maximum expected round trip time (hard coded to one second for the moment)
std::vector<unsigned int> acks; // acked packets from last set of packet receives. cleared each update!
PacketQueue sentQueue; // sent packets used to calculate sent bandwidth (kept until rtt_maximum)
PacketQueue pendingAckQueue; // sent packets which have not been acked yet (kept until rtt_maximum * 2 )
PacketQueue receivedQueue; // received packets for determining acks to send (kept up to most recent recv sequence - 32)
PacketQueue ackedQueue; // acked packets (kept until rtt_maximum * 2)
// add by shiningdracon
unsigned int last_lost_packet_seq;
public:
ReliabilitySystem(unsigned int max_sequence = 0xFFFFFFFF);
void Reset();
void PacketSent(int size);
void PacketReceived(unsigned int sequence, int size);
unsigned int GenerateAckBits();
void ProcessAck(unsigned int ack, unsigned int ack_bits);
void Update(float deltaTime);
//static bool sequence_more_recent( unsigned int s1, unsigned int s2, unsigned int max_sequence );
static int bit_index_for_sequence( unsigned int sequence, unsigned int ack, unsigned int max_sequence );
static unsigned int generate_ack_bits( unsigned int ack, const PacketQueue & received_queue, unsigned int max_sequence );
void process_ack( unsigned int ack, unsigned int ack_bits,
PacketQueue & pending_ack_queue, PacketQueue & acked_queue,
std::vector<unsigned int> & acks, unsigned int & acked_packets,
float & rtt, unsigned int max_sequence );
unsigned int GetLocalSequence() const { return local_sequence; }
unsigned int GetRemoteSequence() const { return remote_sequence; }
unsigned int GetMaxSequence() const { return max_sequence; }
void GetAcks( unsigned int ** acks, int & count ) {
*acks = &this->acks[0];
count = (int) this->acks.size();
}
unsigned int GetSentPackets() const { return sent_packets; }
unsigned int GetReceivedPackets() const { return recv_packets; }
unsigned int GetLostPackets() const { return lost_packets; }
unsigned int GetAckedPackets() const { return acked_packets; }
float GetSentBandwidth() const { return sent_bandwidth; }
float GetAckedBandwidth() const { return acked_bandwidth; }
float GetRoundTripTime() const { return rtt; }
int GetHeaderSize() const { return 12; }
unsigned int GetLastLostPacket() const { return last_lost_packet_seq; }
protected:
void AdvanceQueueTime(float deltaTime);
void UpdateQueues();
void UpdateStats();
};
// connection with reliability (seq/ack)
class ReliableConnection : public Connection
{
private:
void ClearData() { reliabilitySystem.Reset(); }
#ifdef NET_UNIT_TEST
unsigned int packet_loss_mask; // mask sequence number, if non-zero, drop packet - for unit test only
#endif
ReliabilitySystem reliabilitySystem; // reliability system: manages sequence numbers and acks, tracks network stats etc.
public:
ReliableConnection( unsigned int protocolId, float timeout, unsigned int max_sequence = 0xFFFFFFFF );
~ReliableConnection();
bool SendPacket( const void *data, int size );
int ReceivePacket( void *data, int size );
void Update( float deltaTime );
int GetHeaderSize() const { return Connection::GetHeaderSize() + reliabilitySystem.GetHeaderSize(); }
ReliabilitySystem & GetReliabilitySystem() { return reliabilitySystem; }
// unit test controls
#ifdef NET_UNIT_TEST
void SetPacketLossMask( unsigned int mask )
{
packet_loss_mask = mask;
}
#endif
protected:
void WriteInteger( unsigned char * data, unsigned int value );
void WriteHeader( unsigned char * header, unsigned int sequence, unsigned int ack, unsigned int ack_bits );
void ReadInteger( const unsigned char * data, unsigned int & value );
void ReadHeader( const unsigned char * header, unsigned int & sequence, unsigned int & ack, unsigned int & ack_bits );
virtual void OnStop();
virtual void OnDisconnect();
};
}
#endif