| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2013-2018, The OpenClonk Team and contributors |
| 6 | * Copyright (c) 2017-2020, The LegacyClonk Team and contributors |
| 7 | * |
| 8 | * Distributed under the terms of the ISC license; see accompanying file |
| 9 | * "COPYING" for details. |
| 10 | * |
| 11 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 12 | * See accompanying file "TRADEMARK" for details. |
| 13 | * |
| 14 | * To redistribute this file separately, substitute the full license texts |
| 15 | * for the above references. |
| 16 | */ |
| 17 | |
| 18 | /* network i/o, featuring tcp, udp and multicast */ |
| 19 | |
| 20 | #pragma once |
| 21 | |
| 22 | #include "C4Network2Address.h" |
| 23 | #include "Standard.h" |
| 24 | #include "StdSync.h" |
| 25 | #include "StdBuf.h" |
| 26 | #include "StdCompiler.h" |
| 27 | #include "StdScheduler.h" |
| 28 | |
| 29 | #include <memory> |
| 30 | #include <vector> |
| 31 | |
| 32 | |
| 33 | #include <cstring> |
| 34 | #include <vector> |
| 35 | |
| 36 | class C4Network2Address; |
| 37 | |
| 38 | // net i/o base class |
| 39 | class C4NetIO : public StdSchedulerProc |
| 40 | { |
| 41 | public: |
| 42 | C4NetIO(); |
| 43 | virtual ~C4NetIO(); |
| 44 | |
| 45 | using addr_t = C4Network2EndpointAddress; |
| 46 | |
| 47 | static std::vector<C4Network2HostAddress> GetLocalAddresses(bool unsorted = false); |
| 48 | static void SortAddresses(std::vector<C4Network2Address> &addresses); |
| 49 | |
| 50 | // callback class |
| 51 | class CBClass |
| 52 | { |
| 53 | public: |
| 54 | virtual bool OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO) { return true; } |
| 55 | virtual void OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason) {} |
| 56 | virtual void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO) = 0; |
| 57 | virtual ~CBClass() {} |
| 58 | }; |
| 59 | |
| 60 | // used to explicitly callback to a specific class |
| 61 | template <class T> |
| 62 | class CBProxy : public CBClass |
| 63 | { |
| 64 | T *pTarget; |
| 65 | |
| 66 | public: |
| 67 | CBProxy *operator()(T *pnTarget) { pTarget = pnTarget; return this; } |
| 68 | |
| 69 | virtual bool OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO) override |
| 70 | { |
| 71 | return pTarget->T::OnConn(AddrPeer, AddrConnect, pOwnAddr, pNetIO); |
| 72 | } |
| 73 | |
| 74 | virtual void OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason) override |
| 75 | { |
| 76 | pTarget->T::OnDisconn(AddrPeer, pNetIO, szReason); |
| 77 | } |
| 78 | |
| 79 | virtual void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO) override |
| 80 | { |
| 81 | pTarget->T::OnPacket(rPacket, pNetIO); |
| 82 | } |
| 83 | }; |
| 84 | |
| 85 | #define NETIO_CREATE_CALLBACK_PROXY(ForClass, ProxyName) \ |
| 86 | friend class C4NetIO::CBProxy<ForClass>; \ |
| 87 | C4NetIO::CBProxy<ForClass> ProxyName |
| 88 | |
| 89 | // *** interface |
| 90 | |
| 91 | // * not multithreading safe |
| 92 | virtual bool Init(uint16_t iPort = addr_t::IPPORT_NONE) = 0; |
| 93 | virtual bool Close() = 0; |
| 94 | |
| 95 | virtual bool Execute(int iTimeout = -1) override = 0; // (for StdSchedulerProc) |
| 96 | |
| 97 | // * multithreading safe |
| 98 | virtual bool Connect(const addr_t &addr) = 0; // async! |
| 99 | virtual bool Close(const addr_t &addr) = 0; |
| 100 | |
| 101 | virtual bool Send(const class C4NetIOPacket &rPacket) = 0; |
| 102 | virtual bool SetBroadcast(const addr_t &addr, bool fSet = true) = 0; |
| 103 | virtual bool Broadcast(const class C4NetIOPacket &rPacket) = 0; |
| 104 | |
| 105 | // statistics |
| 106 | virtual bool GetStatistic(int *pBroadcastRate) = 0; |
| 107 | virtual bool GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss) = 0; |
| 108 | virtual void ClearStatistic() = 0; |
| 109 | |
| 110 | protected: |
| 111 | // Makes IPv4 connections from an IPv6 socket work. |
| 112 | bool InitIPv6Socket(SOCKET socket); |
| 113 | |
| 114 | // *** errors |
| 115 | protected: |
| 116 | StdStrBuf Error; |
| 117 | void SetError(const char *strnError, bool fSockErr = false); |
| 118 | |
| 119 | public: |
| 120 | virtual const char *GetError() const { return Error.getData(); } |
| 121 | void ResetError() { Error.Clear(); } |
| 122 | |
| 123 | // *** callbacks |
| 124 | virtual void SetCallback(CBClass *pnCallback) = 0; |
| 125 | }; |
| 126 | |
| 127 | // packet class |
| 128 | class C4NetIOPacket : public StdBuf |
| 129 | { |
| 130 | public: |
| 131 | C4NetIOPacket(); |
| 132 | |
| 133 | // construct from memory (copies / references data) |
| 134 | C4NetIOPacket(const void *pnData, size_t inSize, bool fCopy = false, const C4NetIO::addr_t &naddr = C4NetIO::addr_t()); |
| 135 | // construct from buffer (takes data, if possible) |
| 136 | explicit C4NetIOPacket(const StdBuf &Buf, const C4NetIO::addr_t &naddr = C4NetIO::addr_t()); |
| 137 | |
| 138 | ~C4NetIOPacket(); |
| 139 | |
| 140 | protected: |
| 141 | // address |
| 142 | C4NetIO::addr_t addr; |
| 143 | |
| 144 | public: |
| 145 | const C4NetIO::addr_t &getAddr() const { return addr; } |
| 146 | |
| 147 | uint8_t getStatus() const { return getSize() ? *getPtr<char>() : 0; } |
| 148 | StdBuf getPBuf() const { return getSize() ? getPart(iStart: 1, inSize: getSize() - 1) : getRef(); } |
| 149 | const char *getPData() const { return getSize() ? getPtr<char>(pos: 1) : nullptr; } |
| 150 | std::size_t getPSize() const { return getSize() ? getSize() - 1 : 0; } |
| 151 | |
| 152 | // Some overloads |
| 153 | C4NetIOPacket getRef() const { return C4NetIOPacket(StdBuf::getRef(), addr); } |
| 154 | C4NetIOPacket Duplicate() const { return C4NetIOPacket(StdBuf::Duplicate(), addr); } |
| 155 | // change addr |
| 156 | void SetAddr(const C4NetIO::addr_t &naddr) { addr = naddr; } |
| 157 | |
| 158 | // delete contents |
| 159 | void Clear(); |
| 160 | }; |
| 161 | |
| 162 | // tcp network i/o |
| 163 | class C4NetIOTCP : public C4NetIO, protected CStdCSecExCallback |
| 164 | { |
| 165 | public: |
| 166 | C4NetIOTCP(); |
| 167 | virtual ~C4NetIOTCP(); |
| 168 | |
| 169 | // Socket is an unconnected, but bound socket. |
| 170 | class Socket |
| 171 | { |
| 172 | SOCKET sock; |
| 173 | Socket(const SOCKET s) : sock{s} {} |
| 174 | friend class C4NetIOTCP; |
| 175 | |
| 176 | public: |
| 177 | ~Socket(); |
| 178 | // GetAddress returns the address the socket is bound to. |
| 179 | C4NetIO::addr_t GetAddress() const; |
| 180 | }; |
| 181 | |
| 182 | // *** interface |
| 183 | |
| 184 | // * not multithreading safe |
| 185 | virtual bool Init(uint16_t iPort = addr_t::IPPORT_NONE) override; |
| 186 | virtual bool InitBroadcast(addr_t *pBroadcastAddr); |
| 187 | virtual bool Close() override; |
| 188 | virtual bool CloseBroadcast(); |
| 189 | |
| 190 | virtual bool Execute(int iMaxTime = StdSync::Infinite) override; |
| 191 | |
| 192 | // * multithreading safe |
| 193 | std::unique_ptr<Socket> Bind(const addr_t &addr); |
| 194 | bool Connect(const addr_t &addr, std::unique_ptr<Socket> socket); |
| 195 | virtual bool Connect(const addr_t &addr) override; |
| 196 | virtual bool Close(const addr_t &addr) override; |
| 197 | |
| 198 | virtual bool Send(const C4NetIOPacket &rPacket) override; |
| 199 | virtual bool Broadcast(const C4NetIOPacket &rPacket) override; |
| 200 | virtual bool SetBroadcast(const addr_t &addr, bool fSet = true) override; |
| 201 | |
| 202 | virtual void UnBlock(); |
| 203 | #ifdef _WIN32 |
| 204 | virtual HANDLE GetEvent() override; |
| 205 | #else |
| 206 | virtual void GetFDs(std::vector<pollfd> &fds) override; |
| 207 | #endif |
| 208 | |
| 209 | // statistics |
| 210 | virtual bool GetStatistic(int *pBroadcastRate) override; |
| 211 | virtual bool GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss) override; |
| 212 | virtual void ClearStatistic() override; |
| 213 | |
| 214 | protected: |
| 215 | // * overridables (packet format) |
| 216 | |
| 217 | // Append packet data to output buffer |
| 218 | virtual void PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf); |
| 219 | |
| 220 | // Extract a packet from the start of the input buffer (if possible) and call OnPacket. |
| 221 | // Should return the numer of bytes used. |
| 222 | virtual size_t UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &Addr); |
| 223 | |
| 224 | // *** data |
| 225 | |
| 226 | // peer class |
| 227 | class Peer |
| 228 | { |
| 229 | public: |
| 230 | Peer(const C4NetIO::addr_t &naddr, SOCKET nsock, C4NetIOTCP *pnParent); |
| 231 | ~Peer(); |
| 232 | |
| 233 | protected: |
| 234 | // constants |
| 235 | static const unsigned int ; // = 28 + 24; // (bytes) |
| 236 | static const unsigned int iMinIBufSize; // = 8192; // (bytes) |
| 237 | // parent |
| 238 | C4NetIOTCP *const pParent; |
| 239 | // addr |
| 240 | C4NetIO::addr_t addr; |
| 241 | // socket connected |
| 242 | SOCKET sock; |
| 243 | // incoming & outgoing buffer |
| 244 | StdBuf IBuf, OBuf; |
| 245 | int iIBufUsage; |
| 246 | // statistics |
| 247 | int iIRate, iORate; |
| 248 | // status (1 = open, 0 = closed) |
| 249 | bool fOpen; |
| 250 | // selected for broadcast? |
| 251 | bool fDoBroadcast; |
| 252 | // IO critical sections |
| 253 | CStdCSec ICSec; CStdCSec OCSec; |
| 254 | |
| 255 | public: |
| 256 | // data access |
| 257 | const C4NetIO::addr_t &GetAddr() const { return addr; } |
| 258 | SOCKET GetSocket() const { return sock; } |
| 259 | int GetIRate() const { return iIRate; } |
| 260 | int GetORate() const { return iORate; } |
| 261 | // send a packet to this peer |
| 262 | bool Send(const C4NetIOPacket &rPacket); |
| 263 | // send as much data of the interal outgoing buffer as possible |
| 264 | bool Send(); |
| 265 | // request buffer space for new input. Must call OnRecv or NoRecv afterwards! |
| 266 | void *GetRecvBuf(int iSize); |
| 267 | // called after the buffer returned by GetRecvBuf has been filled with fresh data |
| 268 | void OnRecv(int iSize); |
| 269 | // close socket |
| 270 | void Close(); |
| 271 | // test: open? |
| 272 | bool Open() const { return fOpen; } |
| 273 | // selected for broadcast? |
| 274 | bool doBroadcast() const { return fDoBroadcast; } |
| 275 | // outgoing data waiting? |
| 276 | bool hasWaitingData() const { return !OBuf.isNull(); } |
| 277 | // select/unselect peer |
| 278 | void SetBroadcast(bool fSet) { fDoBroadcast = fSet; } |
| 279 | // statistics |
| 280 | void ClearStatistics(); |
| 281 | |
| 282 | public: |
| 283 | // next peer |
| 284 | Peer *Next; |
| 285 | }; |
| 286 | |
| 287 | friend class Peer; |
| 288 | |
| 289 | // peer list |
| 290 | Peer *pPeerList; |
| 291 | |
| 292 | // small list for waited-for connections |
| 293 | struct ConnectWait |
| 294 | { |
| 295 | SOCKET sock; |
| 296 | addr_t addr; |
| 297 | |
| 298 | ConnectWait *Next; |
| 299 | } |
| 300 | *pConnectWaits; |
| 301 | |
| 302 | CStdCSecEx PeerListCSec; |
| 303 | CStdCSec PeerListAddCSec; |
| 304 | |
| 305 | // initialized? |
| 306 | bool fInit; |
| 307 | |
| 308 | // listen socket |
| 309 | uint16_t iListenPort; |
| 310 | SOCKET lsock{INVALID_SOCKET}; |
| 311 | |
| 312 | #ifdef _WIN32 |
| 313 | // event indicating network activity |
| 314 | HANDLE Event; |
| 315 | #else |
| 316 | // Pipe used for cancelling select |
| 317 | int Pipe[2]; |
| 318 | #endif |
| 319 | |
| 320 | // *** implementation |
| 321 | |
| 322 | bool Listen(uint16_t inListenPort); |
| 323 | |
| 324 | SOCKET CreateSocket(addr_t::AddressFamily family); |
| 325 | bool Connect(const addr_t &addr, SOCKET nsock); |
| 326 | |
| 327 | Peer *Accept(SOCKET nsock = INVALID_SOCKET, const addr_t &ConnectAddr = addr_t()); |
| 328 | Peer *GetPeer(const addr_t &addr); |
| 329 | void OnShareFree(CStdCSecEx *pCSec) override; |
| 330 | |
| 331 | void AddConnectWait(SOCKET sock, const addr_t &addr); |
| 332 | ConnectWait *GetConnectWait(const addr_t &addr); |
| 333 | void ClearConnectWaits(); |
| 334 | |
| 335 | // *** callbacks |
| 336 | |
| 337 | public: |
| 338 | virtual void SetCallback(CBClass *pnCallback) override { pCB = pnCallback; } |
| 339 | |
| 340 | private: |
| 341 | CBClass *pCB; |
| 342 | }; |
| 343 | |
| 344 | // simple udp network i/o |
| 345 | // - No connections |
| 346 | // - Delivery not garantueed |
| 347 | // - Broadcast will multicast the packet to all clients with the same broadcast address. |
| 348 | class C4NetIOSimpleUDP : public C4NetIO |
| 349 | { |
| 350 | public: |
| 351 | C4NetIOSimpleUDP(); |
| 352 | virtual ~C4NetIOSimpleUDP(); |
| 353 | |
| 354 | virtual bool Init(uint16_t iPort = addr_t::IPPORT_NONE) override; |
| 355 | virtual bool InitBroadcast(addr_t *pBroadcastAddr); |
| 356 | virtual bool Close() override; |
| 357 | virtual bool CloseBroadcast(); |
| 358 | |
| 359 | virtual bool Execute(int iMaxTime = StdSync::Infinite) override; |
| 360 | |
| 361 | virtual bool Send(const C4NetIOPacket &rPacket) override; |
| 362 | virtual bool Broadcast(const C4NetIOPacket &rPacket) override; |
| 363 | |
| 364 | virtual void UnBlock(); |
| 365 | #ifdef _WIN32 |
| 366 | virtual HANDLE GetEvent() override; |
| 367 | #else |
| 368 | virtual void GetFDs(std::vector<pollfd> &fds) override; |
| 369 | #endif |
| 370 | |
| 371 | // not implemented |
| 372 | virtual bool Connect(const addr_t &addr) override { assert(false); return false; } |
| 373 | virtual bool Close(const addr_t &addr) override { assert(false); return false; } |
| 374 | |
| 375 | virtual bool SetBroadcast(const addr_t &addr, bool fSet = true) override { assert(false); return false; } |
| 376 | |
| 377 | virtual bool GetStatistic(int *pBroadcastRate) override { assert(false); return false; } |
| 378 | |
| 379 | virtual bool GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss) override |
| 380 | { |
| 381 | assert(false); return false; |
| 382 | } |
| 383 | |
| 384 | virtual void ClearStatistic() override { assert(false); } |
| 385 | |
| 386 | private: |
| 387 | // status |
| 388 | bool fInit; |
| 389 | bool fMultiCast; |
| 390 | uint16_t iPort; |
| 391 | |
| 392 | // the socket and the associated event |
| 393 | SOCKET sock{INVALID_SOCKET}; |
| 394 | #ifdef _WIN32 |
| 395 | HANDLE hEvent; |
| 396 | #else |
| 397 | int Pipe[2]; |
| 398 | #endif |
| 399 | |
| 400 | // multicast |
| 401 | addr_t MCAddr; ipv6_mreq MCGrpInfo; |
| 402 | bool fMCLoopback; |
| 403 | |
| 404 | // multibind |
| 405 | int fAllowReUse; |
| 406 | |
| 407 | protected: |
| 408 | // multicast address |
| 409 | const addr_t &getMCAddr() const { return MCAddr; } |
| 410 | |
| 411 | // (try to) control loopback |
| 412 | bool SetMCLoopback(int fLoopback); |
| 413 | bool getMCLoopback() const { return fMCLoopback; } |
| 414 | |
| 415 | // enable multi-bind (call before Init!) |
| 416 | void SetReUseAddress(bool fAllow); |
| 417 | |
| 418 | private: |
| 419 | // socket wait (check for readability) |
| 420 | enum WaitResult { WR_Timeout, WR_Readable, WR_Cancelled, WR_Error = -1, }; |
| 421 | WaitResult WaitForSocket(int iTimeout); |
| 422 | |
| 423 | // *** callbacks |
| 424 | public: |
| 425 | virtual void SetCallback(CBClass *pnCallback) override { pCB = pnCallback; } |
| 426 | |
| 427 | private: |
| 428 | CBClass *pCB; |
| 429 | }; |
| 430 | |
| 431 | // udp network i/o |
| 432 | // - Connection are emulated |
| 433 | // - Delivery garantueed |
| 434 | // - Broadcast will automatically be activated on one side if it's active on the other side. |
| 435 | // If the peer can't be reached through broadcasting, packets will be sent directly. |
| 436 | class C4NetIOUDP : public C4NetIOSimpleUDP, protected CStdCSecExCallback |
| 437 | { |
| 438 | public: |
| 439 | C4NetIOUDP(); |
| 440 | virtual ~C4NetIOUDP(); |
| 441 | |
| 442 | // *** interface |
| 443 | |
| 444 | virtual bool Init(uint16_t iPort = addr_t::IPPORT_NONE) override; |
| 445 | virtual bool InitBroadcast(addr_t *pBroadcastAddr) override; |
| 446 | virtual bool Close() override; |
| 447 | virtual bool CloseBroadcast() override; |
| 448 | |
| 449 | virtual bool Execute(int iMaxTime = StdSync::Infinite) override; |
| 450 | |
| 451 | virtual bool Connect(const addr_t &addr) override; |
| 452 | virtual bool Close(const addr_t &addr) override; |
| 453 | |
| 454 | virtual bool Send(const C4NetIOPacket &rPacket) override; |
| 455 | bool SendDirect(C4NetIOPacket &&packet); // (mt-safe) |
| 456 | virtual bool Broadcast(const C4NetIOPacket &rPacket) override; |
| 457 | virtual bool SetBroadcast(const addr_t &addr, bool fSet = true) override; |
| 458 | |
| 459 | virtual int GetTimeout() override; |
| 460 | |
| 461 | virtual bool GetStatistic(int *pBroadcastRate) override; |
| 462 | virtual bool GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss) override; |
| 463 | virtual void ClearStatistic() override; |
| 464 | |
| 465 | protected: |
| 466 | // *** data |
| 467 | |
| 468 | // internal packet type ids |
| 469 | enum IPTypeID : uint8_t |
| 470 | { |
| 471 | IPID_Ping = 0, |
| 472 | IPID_Test = 1, |
| 473 | IPID_Conn = 2, |
| 474 | IPID_ConnOK = 3, |
| 475 | IPID_AddAddr = 7, |
| 476 | IPID_Data = 4, |
| 477 | IPID_Check = 5, |
| 478 | IPID_Close = 6, |
| 479 | }; |
| 480 | |
| 481 | // packet structures |
| 482 | struct BinAddr; |
| 483 | struct PacketHdr; struct TestPacket; struct ConnPacket; struct ConnOKPacket; struct AddAddrPacket; |
| 484 | struct DataPacketHdr; struct CheckPacketHdr; struct ClosePacket; |
| 485 | |
| 486 | // constants |
| 487 | static const unsigned int iVersion; // = 2; |
| 488 | |
| 489 | static const unsigned int iStdTimeout, // = 1000, // (ms) |
| 490 | iCheckInterval; // = 1000 // (ms) |
| 491 | |
| 492 | static const unsigned int iMaxOPacketBacklog; // = 100; |
| 493 | |
| 494 | static const unsigned int ; // = 8 + 24; // (bytes) |
| 495 | |
| 496 | // packet class |
| 497 | class PacketList; |
| 498 | class Packet |
| 499 | { |
| 500 | friend class PacketList; |
| 501 | |
| 502 | public: |
| 503 | // constants / structures |
| 504 | static const size_t MaxSize; // = 1024; |
| 505 | static const size_t MaxDataSize; // = MaxSize - sizeof(Header); |
| 506 | |
| 507 | // types used for packing |
| 508 | typedef uint32_t nr_t; |
| 509 | |
| 510 | // construction / destruction |
| 511 | Packet(); |
| 512 | Packet(C4NetIOPacket &&rnData, nr_t inNr); |
| 513 | ~Packet(); |
| 514 | |
| 515 | protected: |
| 516 | // data |
| 517 | nr_t iNr; |
| 518 | C4NetIOPacket Data; |
| 519 | bool *pFragmentGot; |
| 520 | |
| 521 | public: |
| 522 | // data access |
| 523 | C4NetIOPacket &GetData() { return Data; } |
| 524 | const C4NetIOPacket &GetData() const { return Data; } |
| 525 | nr_t GetNr() const { return iNr; } |
| 526 | bool Empty() const { return Data.isNull(); } |
| 527 | |
| 528 | // fragmention |
| 529 | nr_t FragmentCnt() const; |
| 530 | C4NetIOPacket GetFragment(nr_t iFNr, bool fBroadcastFlag = false) const; |
| 531 | bool Complete() const; |
| 532 | bool FragmentPresent(nr_t iFNr) const; |
| 533 | bool AddFragment(const C4NetIOPacket &Packet, const C4NetIO::addr_t &addr); |
| 534 | |
| 535 | protected: |
| 536 | ::size_t FragmentSize(nr_t iFNr) const; |
| 537 | // list |
| 538 | Packet *Next, *Prev; |
| 539 | }; |
| 540 | |
| 541 | friend class Packet; |
| 542 | |
| 543 | class PacketList |
| 544 | { |
| 545 | public: |
| 546 | PacketList(unsigned int iMaxPacketCnt = ~0); |
| 547 | ~PacketList(); |
| 548 | |
| 549 | protected: |
| 550 | // packet list |
| 551 | Packet *pFront, *pBack; |
| 552 | // packet counts |
| 553 | unsigned int iPacketCnt, iMaxPacketCnt; |
| 554 | // critical section |
| 555 | CStdCSecEx ListCSec; |
| 556 | |
| 557 | public: |
| 558 | Packet *GetPacket(unsigned int iNr); |
| 559 | Packet *GetPacketFrgm(unsigned int iNr); |
| 560 | Packet *GetFirstPacketComplete(); |
| 561 | bool FragmentPresent(unsigned int iNr); |
| 562 | |
| 563 | bool AddPacket(Packet *pPacket); |
| 564 | bool DeletePacket(Packet *pPacket); |
| 565 | void ClearPackets(unsigned int iUntil); |
| 566 | void Clear(); |
| 567 | }; |
| 568 | |
| 569 | friend class PacketList; |
| 570 | |
| 571 | // peer class |
| 572 | class Peer |
| 573 | { |
| 574 | public: |
| 575 | // construction / destruction |
| 576 | Peer(const C4NetIO::addr_t &naddr, C4NetIOUDP *pnParent); |
| 577 | ~Peer(); |
| 578 | |
| 579 | protected: |
| 580 | // constants |
| 581 | static const unsigned int iConnectRetries; // = 5 |
| 582 | static const unsigned int iReCheckInterval; // = 1000 (ms) |
| 583 | |
| 584 | // parent class |
| 585 | C4NetIOUDP *const pParent; |
| 586 | |
| 587 | // peer address |
| 588 | C4NetIO::addr_t addr; |
| 589 | // alternate peer address |
| 590 | C4NetIO::addr_t addr2; |
| 591 | // the address used by the peer |
| 592 | addr_t PeerAddr; |
| 593 | // connection status |
| 594 | enum ConnStatus |
| 595 | { |
| 596 | CS_None, CS_Conn, CS_Works, CS_Closed |
| 597 | } |
| 598 | eStatus; |
| 599 | // does multicast work? |
| 600 | bool fMultiCast; |
| 601 | // selected for broadcast? |
| 602 | bool fDoBroadcast; |
| 603 | // do callback on connection timeout? |
| 604 | bool fConnFailCallback; |
| 605 | |
| 606 | // packet lists (outgoing, incoming, incoming multicast) |
| 607 | PacketList OPackets; |
| 608 | PacketList IPackets, IMCPackets; |
| 609 | |
| 610 | // packet counters |
| 611 | unsigned int iOPacketCounter; |
| 612 | unsigned int iIPacketCounter, iRIPacketCounter; |
| 613 | unsigned int iIMCPacketCounter, iRIMCPacketCounter; |
| 614 | |
| 615 | unsigned int iMCAckPacketCounter; |
| 616 | |
| 617 | // output critical section |
| 618 | CStdCSec OutCSec; |
| 619 | |
| 620 | // connection check time limit |
| 621 | unsigned int iNextReCheck; |
| 622 | unsigned int iLastPacketAsked, iLastMCPacketAsked; |
| 623 | |
| 624 | // timeout |
| 625 | unsigned int iTimeout; |
| 626 | unsigned int iRetries; |
| 627 | |
| 628 | // statistics |
| 629 | int iIRate, iORate, iLoss; |
| 630 | CStdCSec StatCSec; |
| 631 | |
| 632 | public: |
| 633 | // data access |
| 634 | const C4NetIO::addr_t &GetAddr() const { return addr; } |
| 635 | const C4NetIO::addr_t &GetAltAddr() const { return addr2; } |
| 636 | |
| 637 | // initiate connection |
| 638 | bool Connect(bool fFailCallback); |
| 639 | |
| 640 | // send something to this computer |
| 641 | bool Send(const C4NetIOPacket &rPacket); |
| 642 | // check for lost packets |
| 643 | bool Check(bool fForceCheck = true); |
| 644 | |
| 645 | // called if something from this peer was received |
| 646 | void OnRecv(const C4NetIOPacket &Packet); |
| 647 | |
| 648 | // close connection |
| 649 | void Close(const char *szReason); |
| 650 | // open? |
| 651 | bool Open() const { return eStatus == CS_Works; } |
| 652 | // closed? |
| 653 | bool Closed() const { return eStatus == CS_Closed; } |
| 654 | // multicast support? |
| 655 | bool MultiCast() const { return fMultiCast; } |
| 656 | |
| 657 | // acknowledgment check |
| 658 | unsigned int GetMCAckPacketCounter() const { return iMCAckPacketCounter; } |
| 659 | |
| 660 | // timeout checking |
| 661 | int GetTimeout() { return iTimeout; } |
| 662 | void CheckTimeout(); |
| 663 | |
| 664 | // selected for broadcast? |
| 665 | bool doBroadcast() const { return fDoBroadcast; } |
| 666 | // select/unselect peer |
| 667 | void SetBroadcast(bool fSet) { fDoBroadcast = fSet; } |
| 668 | |
| 669 | // alternate address |
| 670 | void SetAltAddr(const C4NetIO::addr_t &naddr2) { addr2 = naddr2; } |
| 671 | |
| 672 | // statistics |
| 673 | int GetIRate() const { return iIRate; } |
| 674 | int GetORate() const { return iORate; } |
| 675 | void ClearStatistics(); |
| 676 | |
| 677 | protected: |
| 678 | // * helpers |
| 679 | bool DoConn(bool fMC); |
| 680 | bool DoCheck(int iAskCnt = 0, int iMCAskCnt = 0, unsigned int *pAskList = nullptr); |
| 681 | |
| 682 | // sending |
| 683 | bool SendDirect(const Packet &rPacket, unsigned int iNr = ~0); |
| 684 | bool SendDirect(C4NetIOPacket &&rPacket); |
| 685 | |
| 686 | // events |
| 687 | void OnConn(); |
| 688 | void OnClose(const char *szReason); |
| 689 | |
| 690 | // incoming packet list |
| 691 | void CheckCompleteIPackets(); |
| 692 | |
| 693 | // timeout |
| 694 | void SetTimeout(int iLength = iStdTimeout, int iRetryCnt = 0); |
| 695 | void OnTimeout(); |
| 696 | |
| 697 | public: |
| 698 | // next peer |
| 699 | Peer *Next; |
| 700 | }; |
| 701 | |
| 702 | friend class Peer; |
| 703 | |
| 704 | // critical sections |
| 705 | CStdCSecEx PeerListCSec; |
| 706 | CStdCSec PeerListAddCSec; |
| 707 | CStdCSec OutCSec; |
| 708 | |
| 709 | // status |
| 710 | bool fInit; |
| 711 | bool fMultiCast; |
| 712 | uint16_t iPort; |
| 713 | |
| 714 | // peer list |
| 715 | Peer *pPeerList; |
| 716 | |
| 717 | // currently initializing - do not process packets, save them back instead |
| 718 | bool fSavePacket; |
| 719 | C4NetIOPacket LastPacket; |
| 720 | |
| 721 | // multicast support data |
| 722 | addr_t MCLoopbackAddr; |
| 723 | bool fDelayedLoopbackTest; |
| 724 | |
| 725 | // check timing |
| 726 | unsigned int iNextCheck; |
| 727 | |
| 728 | // outgoing packet list (for multicast) |
| 729 | PacketList OPackets; |
| 730 | unsigned int iOPacketCounter; |
| 731 | |
| 732 | // statistics |
| 733 | int iBroadcastRate; |
| 734 | CStdCSec StatCSec; |
| 735 | |
| 736 | // callback proxy |
| 737 | NETIO_CREATE_CALLBACK_PROXY(C4NetIOUDP, CBProxy); |
| 738 | |
| 739 | // helpers |
| 740 | |
| 741 | // sending |
| 742 | bool BroadcastDirect(const Packet &rPacket, unsigned int iNr = ~0u); // (mt-safe) |
| 743 | |
| 744 | // multicast related |
| 745 | bool DoLoopbackTest(); |
| 746 | void ClearMCPackets(); |
| 747 | |
| 748 | // peer list |
| 749 | void AddPeer(Peer *pPeer); |
| 750 | Peer *GetPeer(const addr_t &addr); |
| 751 | Peer *ConnectPeer(const addr_t &PeerAddr, bool fFailCallback); |
| 752 | void OnShareFree(CStdCSecEx *pCSec) override; |
| 753 | |
| 754 | // connection check |
| 755 | void DoCheck(); |
| 756 | |
| 757 | // critical section: only one execute at a time |
| 758 | CStdCSec ExecuteCSec; |
| 759 | |
| 760 | // debug |
| 761 | #ifdef C4NETIO_DEBUG |
| 762 | int hDebugLog; |
| 763 | void OpenDebugLog(); |
| 764 | void CloseDebugLog(); |
| 765 | void DebugLogPkt(bool fOut, const C4NetIOPacket &Pkt); |
| 766 | #endif |
| 767 | |
| 768 | // *** callbacks |
| 769 | public: |
| 770 | virtual void SetCallback(CBClass *pnCallback) override { pCB = pnCallback; } |
| 771 | |
| 772 | private: |
| 773 | CBClass *pCB; |
| 774 | |
| 775 | // callback interface for C4NetIO |
| 776 | virtual bool OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO); |
| 777 | virtual void OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason); |
| 778 | virtual void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO); |
| 779 | |
| 780 | void OnAddAddress(const addr_t &FromAddr, const AddAddrPacket &Packet); |
| 781 | }; |
| 782 | |
| 783 | #ifdef _WIN32 |
| 784 | bool AcquireWinSock(); |
| 785 | void ReleaseWinSock(); |
| 786 | #endif |
| 787 | |