Planeshift
|
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