aboutsummaryrefslogtreecommitdiff
path: root/dep/g3dlite/include/G3D/NetworkDevice.h
blob: e60878cb8a24db751cd1c0ec0148c00a4eddb3c1 (plain)
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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
/**
 @file NetworkDevice.h
 These classes abstract networking from the socket level to a
 serialized messaging style that is more appropriate for games.  The
 performance has been tuned for sending many small messages.  The
 message protocol contains a header that prevents them from being used
 with raw UDP/TCP (e.g. connecting to an HTTP server). 

 LightweightConduit and ReliableConduits have different interfaces
 because they have different semantics.  You would never want to
 interchange them without rewriting the surrounding code.

 NetworkDevice creates conduits because they need access to a global
 log pointer and because I don't want non-reference counted conduits
 being created.

 Be careful with threads and reference counting.  The reference
 counters are not threadsafe, and are also not updated correctly if a
 thread is explicitly killed.  Since the conduits will be passed by
 const XConduitRef& most of the time this doesn't appear as a major
 problem.  With non-blocking conduits, you should need few threads
 anyway.

 LightweightConduits preceed each message with a 4-byte host order
 unsigned integer that is the message type.  This does not appear in
 the message serialization/deserialization.

 ReliableConduits preceed each message with two 4-byte host order
 unsigned integers.  The first is the message type and the second
 indicates the length of the rest of the data.  The size does not
 include the size of the header itself.  The minimum message is 9
 bytes:a 4-byte type, a 4-byte header equal to "1", and one byte of data.

 @maintainer Morgan McGuire, http://graphics.cs.williams.edu
 @created 2002-11-22
 @edited  2006-11-25
 */

#ifndef G3D_NETWORKDEVICE_H
#define G3D_NETWORKDEVICE_H

#include "G3D/platform.h"
#include "G3D/NetAddress.h"

#include <string>
#include <iostream>
#include "G3D/g3dmath.h"

#include "G3D/ReferenceCount.h"
#include "G3D/Array.h"
#include "G3D/BinaryOutput.h"

namespace G3D {

class TextOutput;
/**  \deprecated */
class Conduit : public ReferenceCountedObject {
protected:
    friend class NetworkDevice;
    friend class NetListener;

    uint64                          mSent;
    uint64                          mReceived;
    uint64                          bSent;
    uint64                          bReceived;

    SOCKET                          sock;

    /**
     Used for serialization.  One per socket
     to make this threadsafe.
     */
    BinaryOutput                    binaryOutput;

    Conduit();

public:

    virtual ~Conduit();
    uint64 bytesSent() const;
    uint64 messagesSent() const;
    uint64 bytesReceived() const;
    uint64 messagesReceived() const;

    /**
     If true, receive will return true.
     */
    virtual bool messageWaiting();

    /**
     Returns the type of the waiting message (i.e. the type supplied
     with send).  The return value is zero when there is no message
     waiting.

     One way to use this is to have a Table mapping message types to
     pre-allocated subclasses so receiving looks like:

     <PRE>
         // My base class for messages.
         class Message {
             virtual void serialize(BinaryOutput&) const;
             virtual void deserialize(BinaryInput&);
             virtual void process() = 0;
         };

         Message* m = table[conduit->waitingMessageType()];
         conduit->receive(m);
         m->process();
     </PRE>

      Another is to simply switch on the message type:

      <pre>
         switch (conduit->waitingMessageType()) {
         case 0:
            // No message
            break;

         case ENTITY_SPAWN_MSG:
            {
               EntitySpawnMsg m;
               condiut->receive(m);
               spawnEntity(m.id, m.position, m.modelID);
            }
            break;
            ...
         }
      </pre>
     */
    virtual uint32 waitingMessageType() = 0;

    /** Returns true if the connection is ok. */
    bool ok() const;
};

typedef shared_ptr<class ReliableConduit> ReliableConduitRef;

#ifdef __GNUC__
// Workaround for a known bug in gcc 4.x where htonl produces 
// a spurrious warning.
// http://gcc.gnu.org/ml/gcc-bugs/2005-10/msg03270.html
uint32 gcchtonl(uint32);
#endif

// Messaging and stream APIs must be supported on a single class because
// sometimes an application will switch modes on a single socket.  For
// example, when transferring 3D level geometry during handshaking with
// a game server.
/**
 A conduit that guarantees messages will arrive, intact and in order.
 Create on the client using NetworkDevice::createReliableConduit and
 on the server using NetListener::waitForConnection.  Set the reference
 counted pointer to NULL to disconnect.

 To construct a ReliableConduit:
 <OL>
   <LI> Create a G3D::NetworkDevice (if you are using G3D::GApp, it creates 
        one for you) on the client and on the server.
   <LI> On the server, create a G3D::NetListener using 
        G3D::NetworkDevice::createListener
   <LI> On the server, invoke G3D::NetListener::waitForConnection.
   <LI> On the client, call G3D::NetworkDevice::createReliableConduit.  
        You will need the server's G3D::NetAddress.  Consider using
        G3D::Discovery::Client to find it via broadcasting.
 </OL>
 \deprecated
 */
class ReliableConduit : public Conduit {
private:
    friend class NetworkDevice;
    friend class NetListener;

    enum State {RECEIVING, HOLDING, NO_MESSAGE} state;

    NetAddress                      addr;
    
    /**
     Type of the incoming message.
     */
    uint32                          messageType;

    /** 
     Total size of the incoming message (read from the header).
     */
    uint32                          messageSize;

    /** Shared buffer for receiving messages. */
    void*                           receiveBuffer;

    /** Total size of the receiveBuffer. */
    size_t                          receiveBufferTotalSize;

    /** Size occupied by the current message... so far.  This will be
        equal to messageSize when the whole message has arrived. 
      */
    size_t                          receiveBufferUsedSize;

    ReliableConduit(const NetAddress& addr);

    ReliableConduit(const SOCKET& sock, 
                    const NetAddress& addr);

    template<typename T> static void serializeMessage
              (uint32 t, const T& m, BinaryOutput& b) {

        b.writeUInt32(t);

        // Reserve space for the 4 byte size header
        b.writeUInt32(0);

        size_t L = (size_t)b.length();
        m.serialize(b);
        if ((size_t)b.length() == L) {
            // No data was created by serialization.
            // We need to send at least one byte because receive assumes that
            // a zero length message is an error.
            b.writeUInt8(0xFF);
        }
    
        uint32 len = (uint32)b.size() - 8;
    
        // We send the length first to tell recv how much data to read.
        // Here we abuse BinaryOutput a bit and write directly into
        // its buffer, violating the abstraction.
        // Note that we write to the second set of 4 bytes, which is
        // the size field.
        uint32* lenPtr = ((uint32*)b.getCArray()) + 1;
        #if defined(__GNUC__)
            *lenPtr = gcchtonl(len);
        #else
            *lenPtr = htonl(len);
        #endif
    }


    void sendBuffer(const BinaryOutput& b);

    /** Accumulates whatever part of the message (not the header) is
        still waiting on the socket into the receiveBuffer during
        state = RECEIVING mode.  Closes the socket if anything goes
        wrong.  When receiveBufferUsedSize == messageSize, the entire
        message has arrived. */
    void receiveIntoBuffer();

    /** Receives the messageType and messageSize from the socket. */
    void receiveHeader();

public:

    /**
     Client invokes this to connect to a server.  The call blocks until the 
     conduit is opened.  The conduit will not be ok() if it fails.
     */
    static ReliableConduitRef create(const NetAddress& address);

    /** Closes the socket. */
    ~ReliableConduit();


    /** The message is actually copied from the socket to an internal buffer during
     this call.  Receive only deserializes.*/
    virtual bool messageWaiting();

    /**
     Serializes the message and schedules it to be sent as soon as possible,
     and then returns immediately.  The message can be any <B>class</B> with
     a serialize and deserialize method.  On the receiving side,
     use G3D::ReliableConduit::waitingMessageType() to detect the incoming
     message and then invoke G3D::ReliableConduit::receive(msg) where msg
     is of the same class as the message that was sent.

     The actual data sent across the network is preceeded by the
     message type and the size of the serialized message as a 32-bit
     integer.  The size is sent because TCP is a stream protocol and
     doesn't have a concept of discrete messages.
     */
    template<typename T> inline void send(uint32 type, const T& message) {
        binaryOutput.reset();
        serializeMessage(type, message, binaryOutput);
        sendBuffer(binaryOutput);
    }
    
    /** Sends an empty message with the given type.  Useful for sending 
        commands that have no parameters. */
    void send(uint32 type);

    /** Send the same message to a number of conduits.  Useful for sending
        data from a server to many clients (only serializes once). */
    template<typename T>
    inline static void multisend(
        const Array<ReliableConduitRef>& array, 
        uint32                          type,
        const T&                        m) {
        
        if (array.size() > 0) {
            array[0]->binaryOutput.reset();
            serializeMessage(type, m, array[0]->binaryOutput);

            for (int i = 0; i < array.size(); ++i) {
                array[i]->sendBuffer(array[0]->binaryOutput);
            }
        }
    }

    virtual uint32 waitingMessageType();

    /** 
        If a message is waiting, deserializes the waiting message into
        message and returns true, otherwise returns false.  You can
        determine the type of the message (and therefore, the class
        of message) using G3D::ReliableConduit::waitingMessageType().
     */
    template<typename T> inline bool receive(T& message) {
        if (! messageWaiting()) {
            return false;
        }

        debugAssert(state == HOLDING);
        // Deserialize
        BinaryInput b((uint8*)receiveBuffer, receiveBufferUsedSize, G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY);
        message.deserialize(b);
        
        // Don't let anyone read this message again.  We leave the buffer
        // allocated for the next caller, however.
        receiveBufferUsedSize = 0;
        state = NO_MESSAGE;
        messageType = 0;
        messageSize = 0;

        // Potentially read the next message.
        messageWaiting();

        return true;
    }

    /** Removes the current message from the queue. */
    inline void receive() {
        if (! messageWaiting()) {
            return;
        }
        receiveBufferUsedSize = 0;
        state = NO_MESSAGE;
        messageType = 0;
        messageSize = 0;

        // Potentially read the next message.
        messageWaiting();
    }

    /** The address of the other end of the conduit */
    NetAddress address() const;
};


typedef shared_ptr<class LightweightConduit> LightweightConduitRef;

/**
 Provides fast but unreliable transfer of messages.  On a LAN,
 LightweightConduit will probably never drop messages but you
 <I>might</I> get your messages out of order.  On an internet
 connection it might drop messages altogether.  Messages are never
 corrupted, however.  LightweightConduit requires a little less setup
 and overhead than ReliableConduit.  ReliableConduit guarantees
 message delivery and order but requires a persistent connection.
 
 To set up a LightweightConduit (assuming you have already made
 subclasses of G3D::NetMessage based on your application's
 pcommunication protocol):

[Server Side]
<OL>
<LI> Call LightweightConduit::create(port, true, false), 
where port is the port on which you will receive messages.

<LI> Poll LightweightConduit::messageWaiting from your main loop.  When 
it is true (or, equivalently, when LightweightConduit::waitingMessageType
is non-zero) there is an incoming message.

<LI> To read the incoming message, call LightweightConduit::receive with 
the appropriate class type, which mist have a deserialize method.
LightweightConduit::waitingMessageType tells you what class is 
needed (you make up your own message constants for your program; numbers 
under 1000 are reserved for G3D's internal use).

<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let 
it go out of scope and the conduit cleans itself up automatically.
</OL>

[Client Side]
<OL>
<LI> Call G3D::LightweightConduit::create().  If you will 
broadcast to all servers on a LAN, set the third optional argument to 
true (the default is false for no broadcast).  You can also set up the
receive port as if it was a server to send and receive from a single 
LightweightConduit.

<LI> To send, call G3D::LightweightConduit::send with the target address 
and a pointer to an instance of the message you want to send.

<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let 
it go out of scope and the conduit cleans itself up automatically.

</OL>

\deprecated
 */
class LightweightConduit : public Conduit {
private:
    friend class NetworkDevice;

    /**
     True when waitingForMessageType has read the message
     from the network into messageType/messageStream.
     */
    bool                    alreadyReadMessage;

    /**
     Origin of the received message.
     */
    NetAddress              messageSender;

    /**
     The type of the last message received.
     */
    uint32                  messageType;

    /**
     The message received (the type has already been read off).
     */
    Array<uint8>            messageBuffer;

    LightweightConduit(uint16 receivePort, bool enableReceive, bool enableBroadcast);
    
    void sendBuffer(const NetAddress& a, BinaryOutput& b);

    /** Maximum transmission unit (packet size in bytes) for this socket.
        May vary between sockets. */
    int                    MTU;


    template<typename T> 
    void serializeMessage(
        uint32 type, 
        const T& m, 
        BinaryOutput& b) const {

        debugAssert(type != 0);
        b.writeUInt32(type);
        m.serialize(b);
        b.writeUInt32(1);
    
        debugAssertM(b.size() < MTU, 
                    format("This LightweightConduit is limited to messages of "
                           "%d bytes (Ethernet hardware limit; this is the "
                           "'UDP MTU')", maxMessageSize()));

        if (b.size() >= MTU) {
            throw LightweightConduit::PacketSizeException(
                    format("This LightweightConduit is limited to messages of "
                           "%d bytes (Ethernet hardware limit; this is the "
                           "'UDP MTU')", maxMessageSize()),
                           (int)b.size() - 4, // Don't count the type header
                           maxMessageSize());
        }
    }

public:

    static LightweightConduitRef create(uint16 receivePort, bool enableReceive, bool enableBroadcast);

    class PacketSizeException {
    public:
        std::string            message;
        int                    serializedPacketSize;
        int                    maxMessageSize;

        inline PacketSizeException(const std::string& m, int s, int b) :
            message(m),
            serializedPacketSize(s),
            maxMessageSize(b) {}
    };

    /** Closes the socket. */
    ~LightweightConduit();

    /** The maximum length of a message that can be sent
        (G3D places a small header at the front of each UDP packet;
        this is already taken into account by the value returned).
     */
    inline int maxMessageSize() const {
        return MTU - 4;
    }


    template<typename T> inline void send(const NetAddress& a, uint32 type, const T& msg) {
        binaryOutput.reset();
        serializeMessage(type, msg, binaryOutput);
        sendBuffer(a, binaryOutput);
    }

    /** Send the same message to multiple addresses (only serializes once).
        Useful when server needs to send to a known list of addresses
        (unlike direct UDP broadcast to all addresses on the subnet) */
    template<typename T> inline void send(const Array<NetAddress>& a, uint32 type, const T& m) {
        binaryOutput.reset();
        serializeMessage(type, m, binaryOutput);

        for (int i = 0; i < a.size(); ++i) {
            sendBuffer(a[i], binaryOutput);
        }
    }

    bool receive(NetAddress& sender);

    template<typename T> inline bool receive(NetAddress& sender, T& message) {
        bool r = receive(sender);
        if (r) {
            BinaryInput b((messageBuffer.getCArray() + 4), 
                          messageBuffer.size() - 4, 
                          G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY);
            message.deserialize(b);
        }

        return r;
    }

    inline bool receive() {
        static NetAddress ignore;
        return receive(ignore);
    }

    virtual uint32 waitingMessageType();


    virtual bool messageWaiting();
};

///////////////////////////////////////////////////////////////////////////////

typedef shared_ptr<class NetListener> NetListenerRef;

/**
 Runs on the server listening for clients trying to make reliable connections.

 \deprecated
 */
class NetListener : public ReferenceCountedObject {
private:

    friend class NetworkDevice;

    SOCKET                          sock;

    /** Port is in host byte order. */
    NetListener(uint16 port);

public:

    static NetListenerRef create(const uint16 port);

    ~NetListener();

    /** Block until a connection is received.  Returns NULL if 
        something went wrong. */
    ReliableConduitRef waitForConnection();

    /** True if a client is waiting (i.e. waitForConnection will
        return immediately). */
    bool clientWaiting() const;

    bool ok() const;
};


///////////////////////////////////////////////////////////////////////////////

/**
   @brief Abstraction of network (socket) functionality.

   An abstraction over sockets that provides a message-based network
   infrastructure optimized for sending many small (~500 bytes) messages.
   All functions always return immediately.
   
   Create only one NetworkDevice per process (a WinSock restriction).
   
   NetworkDevice is technically not thread safe.  However, as long as
   you use different conduits on different threads (or lock conduits
   before sending), you will encounter no problems sharing the single
   NetworkDevice across multiple threads.  That is, do not invoke the same
   Conduit's send or receive method on two threads at once.
   
   This assumes that the underlying WinSock/BSD sockets implementation
   is thread safe.  That is not guaranteed, but in practice seems
   to always be true (see
   http://tangentsoft.net/wskfaq/intermediate.html#threadsafety)

   <hr> 

   IP networks use "network byte order" (big-endian) for
   communicating integers.  "Host byte order" is the endian-ness of
   the local machine (typically little-endian; see
   System::endian). The C functions htonl() and ntohl() convert 32-bit
   values between these formats. G3D only ever exposes host byte order,
   so programmers rarely need to be aware of the distinction.

   \deprecated
 */
class NetworkDevice {
public:

    /** @brief Description of an ethernet or wireless ethernet adapter.*/
    class EthernetAdapter {
    public:
        /** Reverse-DNS of the ip address.*/
        std::string     hostname;

        /** Name of the adapter */
        std::string     name;

        /** IP address in host byte order.*/
        uint32          ip;

        /** Subnet mask in host byte order.*/
        uint32          subnet;

        /** UDP broadcast address in host byte order.*/
        uint32          broadcast;

        /** MAC (hardware) address, if known */
        uint8           mac[6];

        EthernetAdapter();

        /** Produces a text description of this adapter */
        void describe(TextOutput& t) const;
    };

private:

    friend class Conduit;
    friend class LightweightConduit;
    friend class ReliableConduit;
    friend class NetListener;

    bool                        initialized;

    Array<EthernetAdapter>      m_adapterArray;

    /** Broadcast addresses available on this machine,
     extracted from m_adapterArray.*/
    Array<uint32>               m_broadcastAddresses;

    /** Utility method. */
    void closesocket(SOCKET& sock) const;

    /** Utility method. Returns true on success.*/
    bool bind(SOCKET sock, const NetAddress& addr) const;

    /** The global instance */
    static NetworkDevice* s_instance;

    NetworkDevice();

    bool init();

    void _cleanup();

    /** Called from init to update m_adapterArray and
        m_broadcastAddresses. */
    void addAdapter(const EthernetAdapter& a);

public:

    /** Prints an IP address to a string.
        @param ip In host byte order.*/
    static std::string formatIP(uint32 ip);

    /** Prints a MAC address to a string. */
    static std::string formatMAC(const uint8 mac[6]);

    ~NetworkDevice();

    /** Returns the available ethernet adapters for the current
        machine that are online. Does not include the loopback adapter
        for localhost.*/
    inline const Array<EthernetAdapter>& adapterArray() const {
        return m_adapterArray;
    }

    /** Returns the (unique) IP addresses for UDP broadcasting
        extracted from adapterArray(). All are in host byte order. */
    inline const Array<uint32>& broadcastAddressArray() const {
        return m_broadcastAddresses;
    }

    /**
     Returns NULL if there was a problem initializing the network.
     */
    static NetworkDevice* instance();

    /**
     Shuts down the network device (destroying the global instance).
     */
    static void cleanup();

    /**
     Prints a human-readable description of this machine
     to the text output stream.
     */
    void describeSystem(
        TextOutput& t);

    void describeSystem(
        std::string&        s);

    /** Returns the name (or one of the names) of this computer */
    std::string localHostName() const;

    /** There is often more than one address for the local host. This
        returns all of them. 
        @deprecated Use adapterArray()
    */
    void localHostAddresses(Array<NetAddress>& array) const;
};


#ifdef __GNUC__
inline uint32 gcchtonl(uint32 x) {
    // This pragma fools gcc into surpressing all error messages,
    // including the bogus one that it creates for htonl
#   pragma GCC system_header
    return htonl(x);
}
#endif

} // G3D namespace

#ifndef _WIN32
#undef SOCKADDR_IN
#undef SOCKET
#endif

#endif