Planeshift

netbase.h

Go to the documentation of this file.
00001 /*
00002  * netbase.h by Matze Braun <[email protected]>
00003  *
00004  * Copyright (C) 2001 Atomic Blue ([email protected], http://www.atomicblue.org)
00005  *
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation (version 2 of the License)
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017  *
00018  * This file defines some inline functions which are used in client and server
00019  * network functions, the client/server interface classes should be derived from
00020  * this base class
00021  */
00022 #ifndef __NETBASE_H__
00023 #define __NETBASE_H__
00024 
00025 #include "net/pstypes.h"
00026 #include "net/netinfos.h"
00027 #include "net/netpacket.h"
00028 #include "util/genrefqueue.h"
00029 #include <csutil/ref.h>
00030 #include <csutil/weakref.h>
00031 #include <csutil/weakreferenced.h>
00032 #include <csutil/refcount.h>
00033 #include <csutil/strset.h>
00034 #include <csutil/array.h>
00035 #include "netprofile.h"
00036 
00041 #define NUM_BROADCAST        0xffffffff
00042 #define MAXQUEUESIZE        20000
00043 #define MAXCLIENTQUEUESIZE  5000
00044 #define MAXPACKETHISTORY    1009 // Should be a prime to improve performance of hash
00045                                  // This must be set carefully and ideally should be at least the size
00046                                  // of the input queue
00047 #define NETAVGCOUNT 400
00048 #define RESENDAVGCOUNT 200
00049 
00050 const unsigned int WINDOW_MAX_SIZE = 65536; // The size of the maximum reliable window in bytes.
00051 
00052 // The number of times the SendTo function will retry on a EAGAIN or EWOULDBLOCK
00053 #define SENDTO_MAX_RETRIES   200
00054 // The whole seconds that the SendTo function will block each cycle waiting for the write state to change on the socket
00055 #define SENDTO_SELECT_TIMEOUT_SEC 0
00056 // The microseconds (1/10^6 s) that the SendTo function will block each cycle waiting for the write state to change on the socket
00057 #define SENDTO_SELECT_TIMEOUT_USEC 10000
00058 
00059 /* Include platform specific socket settings */
00060 #ifdef USE_WINSOCK
00061 #   include "net/sockwin.h"
00062 #endif
00063 #ifdef USE_UNISOCK
00064 #   include "net/sockuni.h"
00065 #endif
00066 
00067 // Jorrit: hack for mingw.
00068 #ifdef SendMessage
00069 #undef SendMessage
00070 #endif
00071 #ifdef SetJob
00072 #undef SetJob
00073 #endif
00074 #ifdef GetJob
00075 #undef GetJob
00076 #endif
00077 
00078 class MsgEntry;
00079 class NetPacketQueueRefCount;
00080 class csRandomGen;
00081 class csStringHashReversible;
00082 struct iEngine;
00083 typedef GenericRefQueue <MsgEntry> MsgQueue;
00084 typedef GenericRefQueue <psNetPacketEntry> NetPacketQueue;
00085 
00086 /*
00087  * Microsoft sockets do not use Berkeley error codes or functions in all cases.
00088  * This is the workaround.
00089  * http://msdn.microsoft.com/en-us/library/ms737828(VS.85).aspx
00090  *      Ravna 19/3/2016: This breaks msvc2015, since various internal libraries like String use errno, and can't assign to it if it is not a value, 
00091  *      so I changed the 3 values the macro originally replaced. Left this for reference.
00092  */
00093 
00094 #ifndef CS_PLATFORM_WIN32
00095 #define WSAEWOULDBLOCK EAGAIN
00096 #endif
00097 
00098 
00099 struct PublishDestination
00100 {
00101     uint32_t client;
00102     void*    object;
00103     float    dist;
00104     float    min_dist;
00105 
00106     PublishDestination(int client, void* object, float dist, float min_dist) : client(client), object(object), dist(dist), min_dist(min_dist) {}
00107 };
00108 
00109 //-----------------------------------------------------------------------------
00110 
00116 class NetBase
00117 {
00118 public:
00119 
00125     struct AccessPointers
00126     {
00127         // msgstrings are loaded by CacheManager and mantained in CacheManager.msg_strings
00128         // then sent from server to client at login with psMsgStringsMessage
00129         // and stored in <appdata>/cache/commonstrings client side
00130         csStringSet* msgstrings;
00131         csStringHashReversible* msgstringshash;
00132         iEngine *engine;
00133 
00139         const char* Request(csStringID id) const;
00140 
00146         csStringID Request(const char* string) const;
00147     };
00148 
00149     
00155     NetBase(int outqueuelen=100);
00156     virtual ~NetBase();
00157 
00163     bool AddMsgQueue (MsgQueue *,objID minID=0,objID maxID=0xffffffff);
00164 
00166     void RemoveMsgQueue(MsgQueue *);
00167 
00171     virtual bool SendMessage (MsgEntry* me);
00172     virtual bool SendMessage (MsgEntry* me,NetPacketQueueRefCount *queue);
00173 
00177     virtual void Broadcast (MsgEntry* me, int scope, int guildID) = 0;
00178 
00195     virtual void Multicast (MsgEntry* me, const csArray<PublishDestination>& multi, uint32_t except, float range) = 0;
00196 
00200     bool IsReady()  { return ready; }
00201 
00205     bool QueueMessage(MsgEntry *me);
00206 
00214     void ProcessNetwork (csTicks timeout);
00215 
00217     bool SendOut(void);
00218 
00220     bool CheckIn(void);
00221 
00228     bool Flush(MsgQueue * queue);
00229 
00231     bool Bind(const char* addr, int port);
00232     bool Bind(const IN_ADDR &addr, int port);
00233 
00239     class Connection;
00240 
00241     enum broadcasttype
00242     {
00243     BC_EVERYONEBUTSELF = 1,
00244     BC_GROUP = 2,
00245     BC_GUILD = 3,
00246     BC_SECTOR = 4,
00247     BC_EVERYONE = 5,
00248     BC_FINALPACKET = 6
00249     };
00250 
00251     csTicks GetPing(void) { return netInfos.GetAveragePingTicks();}
00252 
00253     psNetMsgProfiles * GetProfs() { return profs; }
00254 
00256     struct timeval timeout;
00257 
00259     void SetMsgStrings(csStringSet* msgstrings, csStringHashReversible* msgstringshash)
00260     {
00261         accessPointers.msgstrings = msgstrings;
00262         accessPointers.msgstringshash = msgstringshash;
00263     }
00264 
00266     void SetEngine(iEngine* engine) { accessPointers.engine = engine; }
00267 
00269     iEngine* GetEngine() { return accessPointers.engine; }
00270 
00272     static AccessPointers* GetAccessPointers() { return &accessPointers; }
00273     
00280     void LogMessages(char dir, MsgEntry* me);
00281 
00287     csString LogMessageFilter(const char *arg);
00288 
00294     void AddFilterLogMessage(int type);
00295 
00301     void RemoveFilterLogMessage(int type);
00302 
00307     void LogMessageFilterClear();
00308 
00312     void InvertLogMessageFilter() { logmsgfiltersetting.invert = !logmsgfiltersetting.invert; }
00313 
00317     void ToggleReceiveMessageFilter() { logmsgfiltersetting.receive = !logmsgfiltersetting.receive; }
00318 
00322     void ToggleSendMessageFilter() { logmsgfiltersetting.send = !logmsgfiltersetting.send; }
00323 
00327     void SetLogMessageFilterHex(bool filterhex) { logmsgfiltersetting.filterhex = filterhex; }
00328 
00332     void LogMessageFilterDump();
00333 
00335     uint32_t GetRandomID();
00336 
00337 protected:
00344     bool FilterLogMessage(int type,char dir);
00345 
00346     /* the following protected stuff is thought of being used in the inherited
00347      * network classes
00348      */
00349 
00354     int SendTo (LPSOCKADDR_IN addr, const void *data, unsigned int size)
00355     {
00356         struct timeval timeout;
00357         int sentbytes;
00358         int retries=0;
00359         fd_set wfds;
00360 
00361         #ifdef DEBUG
00362             if (!addr || !data)
00363             Error1("wrong args");
00364         #endif
00365 
00366         // #define ENDIAN_DEBUG
00367 
00368         #ifdef ENDIAN_DEBUG
00369             FILE *f = fopen("hexdump.txt","a");
00370             int i;
00371             int len;
00372             char c;
00373             const char *ptr;
00374             ptr = (const char *)data;
00375             len = ((40<size) ? 40 : size);
00376             fprintf(f,"\n------------\nSending %d bytes:\n",len);
00377             for ( i = 0 ; i < len ; i++ ) /* Print the hex dump for len chars. */
00378                 fprintf(f,"%02x " ,(*(ptr+i)&0xff));
00379             for ( i = len ; i < 16 ; i++ ) /* Pad out the dump field to the ASCII */
00380                 fprintf(f, "   " ); /* field. */
00381             fprintf(f, " " );
00382             for ( i = 0 ; i < len ; i++ ) /* Now print the ASCII field. */
00383             {
00384                  c=0x7f&(*(ptr+i)); /* mask out bit 7 */
00385                  if (!(isprint(c))) /* If not printable */
00386                      fprintf(f, "." ); /* print a dot */
00387                  else {
00388                      fprintf(f, "%c" ,c);
00389                  } /* else display it */
00390             }
00391             fprintf(f,"\n-----------------\n");
00392             fclose(f);
00393         #endif
00394 
00395         // Try and send the data, if we fail we wait for the status to change
00396         sentbytes=SOCK_SENDTO(mysocket, data, size, 0, (LPSOCKADDR) addr, sizeof (SOCKADDR_IN) );
00397 
00398 
00399         /* Call select() to wait until precisely the time of the change, but have a timeout.
00400          *  It's possible that the buffer will free between the sendto call and the select
00401          *  leaving the select hanging forever if not for the timeout value.
00402          */
00403 #ifdef CS_PLATFORM_WIN32 
00404         while (retries++ < SENDTO_MAX_RETRIES && sentbytes == -1 && (WSAGetLastError() == EAGAIN || WSAGetLastError() == WSAEWOULDBLOCK))
00405 #else
00406                 while (retries++ < SENDTO_MAX_RETRIES && sentbytes == -1 && (errno == EAGAIN || errno == WSAEWOULDBLOCK))
00407 #endif
00408         {
00409             printf("In while loop on EAGAIN... retry #%d.\n", retries);
00410 
00411             // Clear the file descriptor set
00412             FD_ZERO(&wfds);
00413             // Set the socket's FD in this set
00414             FD_SET(mysocket,&wfds);
00415 
00416             // Zero out the timeout value to start
00417             memset(&timeout,0,sizeof(struct timeval));
00418             timeout.tv_sec=SENDTO_SELECT_TIMEOUT_SEC;
00419             timeout.tv_usec=SENDTO_SELECT_TIMEOUT_USEC;
00420 
00421             /* Wait for the descriptor to change.  Note that it's possible that the status
00422              * has already cleared, so we'll try to send again after this anyway.
00423              */
00424             SOCK_SELECT(mysocket+1,NULL,&wfds,NULL,&timeout);
00425 
00426             // Try and send again.
00427             sentbytes=SOCK_SENDTO(mysocket, data, size, 0, (LPSOCKADDR) addr, sizeof (SOCKADDR_IN) );
00428         }
00429 
00430         if (sentbytes>0)
00431         {
00432             totaltransferout += size;
00433             totalcountout++;
00434         }
00435         else
00436         {
00437 #ifdef CS_PLATFORM_WIN32
00438             Error2("NetBase::SendTo() gave up trying to send a packet with errno=%d.", WSAGetLastError());
00439 #else
00440                         Error2("NetBase::SendTo() gave up trying to send a packet with errno=%d.", errno);
00441 #endif
00442         }
00443 
00444         return sentbytes;
00445     }
00446 
00451     int RecvFrom (LPSOCKADDR_IN addr, socklen_t *socklen, void *buf,
00452         unsigned int maxsize)
00453     {
00454         #ifdef DEBUG
00455         if (!addr || !buf)
00456             Error1("wrong args");
00457         #endif
00458         fd_set set;
00459 
00460         /* Initialize the file descriptor set. */
00461         FD_ZERO (&set);
00462         FD_SET (mysocket, &set);
00463 #ifndef CS_PLATFORM_WIN32
00464         if(pipe_fd[0] > 0)
00465             FD_SET (pipe_fd[0], &set);
00466 #endif
00467 
00468         // Backup the timeval struct in case select changes it as on Linux
00469         struct timeval prevTimeout = timeout;
00470 
00471         /* select returns 0 if timeout, 1 if input available, -1 if error. */
00472         if (SOCK_SELECT(csMax(mysocket, pipe_fd[0]) + 1, &set, NULL, NULL, &timeout) < 1)
00473         {
00474             timeout = prevTimeout;
00475             return 0;
00476         }
00477 
00478 #ifndef CS_PLATFORM_WIN32
00479         if(FD_ISSET(pipe_fd[0], &set))
00480         {
00481             char throwaway[32];
00482             if(read(pipe_fd[0], throwaway, 32) == -1)
00483             {
00484                Error1("Read failed!");
00485             }
00486         }
00487 #endif
00488 
00489         timeout = prevTimeout;
00490 
00491         if(!FD_ISSET(mysocket, &set))
00492             return 0;
00493 
00494         int err = SOCK_RECVFROM (mysocket, buf, maxsize, 0,
00495             (LPSOCKADDR) addr, socklen);
00496         if (err>=0)
00497         {
00498             totaltransferin += err;
00499             totalcountin++;
00500         }
00501         return err;
00502     }
00503 
00504 
00509     int GetIPByName (LPSOCKADDR_IN addr, const char *name);
00510 
00511     virtual Connection *GetConnByIP (LPSOCKADDR_IN addr) = 0;
00512 
00513     virtual Connection *GetConnByNum (uint32_t clientnum) = 0;
00514 
00519     virtual bool HandleUnknownClient (LPSOCKADDR_IN addr, MsgEntry *data) = 0;
00520 
00526     bool Init(bool autobind = true);
00527 
00528     void Close(bool force = true);
00529 
00534     bool HandleAck(psNetPacketEntry* pkt, Connection* connection, LPSOCKADDR_IN addr);
00535 
00541     void CheckResendPkts(void);
00542 
00547     bool BuildMessage(psNetPacketEntry* pkt,Connection* &connection,LPSOCKADDR_IN addr);
00548 
00556     void CheckFragmentTimeouts(void);
00557 
00562     csPtr<MsgEntry> CheckCompleteMessage(uint32_t client,uint32_t id);
00563 
00569     void HandleCompletedMessage(MsgEntry *me,
00570                 Connection* &connection,
00571                 LPSOCKADDR_IN addr,
00572                 psNetPacketEntry* pkt);
00573 
00577     bool CheckDoublePackets (Connection* connection, psNetPacketEntry* pkt);
00578 
00579 
00584     bool SendMergedPackets(NetPacketQueue *q);
00585 
00589     bool SendSinglePacket(psNetPacketEntry* pkt);
00590 
00594     bool SendFinalPacket(psNetPacketEntry* pkt);
00595 
00599     bool SendFinalPacket(psNetPacketEntry* pkt, LPSOCKADDR_IN addr);
00600 
00602     csRef<NetPacketQueueRefCount> NetworkQueue;
00603 
00605     GenericRefQueue<NetPacketQueueRefCount, csWeakRef > senders;
00606 
00608     csArray<MsgQueue*> inqueues;
00609 
00611     csHash<csRef<psNetPacketEntry>, PacketKey> awaitingack;
00612 
00614     static int socklibrefcount;
00615 
00617     bool ready;
00618 
00620     long totaltransferin, totaltransferout;
00622     long totalcountin, totalcountout;
00623 
00625     typedef struct {
00626         unsigned int senders;
00627         unsigned int messages;
00628         csTicks time;
00629     } SendQueueStats_t;
00630 
00631     SendQueueStats_t sendStats[NETAVGCOUNT];
00632     csTicks lastSendReport;
00633     unsigned int avgIndex;
00634 
00635     size_t resends[RESENDAVGCOUNT];
00636     unsigned int resendIndex;
00637 
00638     psNetMsgProfiles * profs;
00639 
00640 private:
00642     SOCKET mysocket;
00643 
00645     SOCKET pipe_fd[2];
00646 
00648     csHash<csRef<psNetPacketEntry> , PacketKey> packets;
00649 
00653     csRandomGen* randomgen;
00654 
00656     CS::Threading::Mutex mutex;
00657 
00659     psNetInfos netInfos;
00660 
00662     static AccessPointers accessPointers;
00663 
00665     csArray<int> logmessagefilter;
00666 
00667     typedef struct {
00668         bool invert;
00669         bool filterhex;
00670         bool receive;
00671         bool send;
00672     } LogMsgFilterSetting_t;
00673 
00674     LogMsgFilterSetting_t logmsgfiltersetting;
00675 
00676     char* input_buffer;
00677 };
00678 
00679 
00680 //-----------------------------------------------------------------------------
00681 
00682 
00684 class NetBase::Connection
00685 {
00686     uint32_t sequence;
00687 public:
00688     csHash<uint32_t> packethistoryhash;
00690     void *buf;
00692     SOCKADDR_IN addr;
00694     csString nameAddr;
00696     int pcknumin;
00698     int pcknumout;
00700     bool valid;
00702     uint32_t clientnum;
00704     csTicks lastRecvPacketTime;
00706     int heartbeat;
00708     float estRTT;
00710     float devRTT;
00712     csTicks RTO;
00714     uint32_t sends;
00716     uint32_t resends;
00717     
00718     // Reliable transmission window size
00719     uint32_t window;
00720 
00722     uint32_t packethistoryid[MAXPACKETHISTORY];
00723     uint32_t packethistoryoffset[MAXPACKETHISTORY];
00724     int historypos;
00725 
00726     Connection(uint32_t num=0);
00727     ~Connection();
00728     bool isValid() const
00729     { return valid; }
00730 
00731     uint32_t GetNextPacketID() {return sequence++;}
00732     
00734     bool IsWindowFull() {return window > WINDOW_MAX_SIZE; }
00736     void AddToWindow(uint32_t bytes) {window += bytes; }
00738     void RemoveFromWindow(uint32_t bytes) { if(bytes > window) abort(); window -= bytes;}
00739 };
00740 
00741 
00742 //-----------------------------------------------------------------------------
00743 
00744 
00745 class NetPacketQueueRefCount : public NetPacketQueue, public csSyncRefCount, public CS::Utility::WeakReferenced
00746 {
00747 private:
00748     bool pending;
00749 
00750 public:
00751     NetPacketQueueRefCount(int qlen)
00752     : NetPacketQueue(qlen)
00753     { pending=false; }
00754     virtual ~NetPacketQueueRefCount()
00755     {}
00756 
00759     void SetPending(bool flag)
00760     {
00761         pending = flag;
00762     }
00763     bool GetPending()
00764     {
00765         return pending;
00766     }
00767 };
00768 
00771 #endif
00772