EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
TCPConnection.cpp
Go to the documentation of this file.
1 /*
2  ------------------------------------------------------------------------------------
3  LICENSE:
4  ------------------------------------------------------------------------------------
5  This file is part of EVEmu: EVE Online Server Emulator
6  Copyright 2006 - 2021 The EVEmu Team
7  For the latest information visit https://evemu.dev
8  ------------------------------------------------------------------------------------
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU Lesser General Public License as published by the Free Software
11  Foundation; either version 2 of the License, or (at your option) any later
12  version.
13 
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public License along with
19  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20  Place - Suite 330, Boston, MA 02111-1307, USA, or go to
21  http://www.gnu.org/copyleft/lesser.txt.
22  ------------------------------------------------------------------------------------
23  Author: Zhur
24  Rewrite: Allan
25 */
26 
27 #include "eve-core.h"
28 
29 #include "log/logsys.h"
30 #include "log/LogNew.h"
31 #include "network/TCPConnection.h"
32 #include "network/NetUtils.h"
33 #include "threading/Threading.h"
34 #include "utils/timer.h"
35 
37 const uint32 TCPCONN_LOOP_GRANULARITY = 5; /* 5ms */
38 
40 : mSock( nullptr ),
41  mSockState( STATE_DISCONNECTED ),
42  mrIP( 0 ),
43  mrPort( 0 ),
44  mRecvBuf( nullptr )
45 {
46 }
47 
49 : mSock( socket ),
50  mSockState( STATE_CONNECTED ),
51  mrIP( mrIP ),
52  mrPort( mrPort ),
53  mRecvBuf( nullptr )
54 {
55  // Start worker thread
56  StartLoop();
57 }
58 
60 {
61  _log(THREAD__WARNING, "Destroying TCPConnection for thread 0x%X", pthread_self());
62  // Make sure we are disconnected
63  Disconnect();
64  // Wait for loop to stop
65  WaitLoop();
66  // Clear buffers
67  ClearBuffers();
68 }
69 
71 {
72  /* "The Matrix is a system, Neo. That system is our enemy. But when you're inside, you look around, what do you see?" */
73  in_addr addr;
74  addr.s_addr = GetrIP();
75 
76  char address[22];
77  int len = snprintf( address, 22, "%s:%u", inet_ntoa( addr ), GetrPort() );
78 
79  /* snprintf will return < 0 when a error occurs so return empty string */
80  if( len < 0 )
81  return std::string();
82 
83  return address;
84 }
85 
86 bool TCPConnection::Connect( uint32 rIP, uint16 rPort, char* errbuf )
87 {
88  if (errbuf)
89  errbuf[0] = 0;
90 
91  MutexLock lock( mMSock );
92 
93  if (GetState() == STATE_DISCONNECTED) {
94  mMSock.Unlock();
95  // Wait for working thread to stop.
96  WaitLoop();
97  mMSock.Lock();
98  }
99 
100  state_t oldState = GetState();
101  if (oldState != STATE_DISCONNECTED && oldState != STATE_CONNECTING)
102  return false;
103 
104  mSock = new Socket( AF_INET, SOCK_STREAM, 0 );
105 
106  sockaddr_in server_sin;
107  server_sin.sin_family = AF_INET;
108  server_sin.sin_addr.s_addr = rIP;
109  server_sin.sin_port = htons( rPort );
110 
111  // Establish a connection to the server socket.
112  if( mSock->connect( (sockaddr*)&server_sin, sizeof( server_sin ) ) == SOCKET_ERROR )
113  {
114  if( errbuf )
115  snprintf( errbuf, TCPCONN_ERRBUF_SIZE, "%s", strerror( errno ) );
116 
117  SafeDelete( mSock );
118  return false;
119  }
120 
121  int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k
122  mSock->setopt( SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof( bufsize ) );
123  mSock->fcntl( F_SETFL, O_NONBLOCK );
124  mrIP = rIP;
125  mrPort = rPort;
127  // Start processing thread if necessary
128  if( oldState == STATE_DISCONNECTED )
129  StartLoop();
130 
131  return true;
132 }
133 
135 {
136  // Changing state; acquire mutex
137  MutexLock lock( mMSock );
138 
139  if (GetState() == STATE_DISCONNECTED) {
140  mMSock.Unlock();
141  // Wait for working thread to stop.
142  WaitLoop();
143  mMSock.Lock();
144  }
145 
146  if (GetState() != STATE_DISCONNECTED)
147  return;
148 
149  mrIP = rIP;
150  mrPort = rPort;
152  // Start processing thread
153  StartLoop();
154 }
155 
157 {
158  MutexLock lock( mMSock );
159 
160  state_t state = GetState();
161  if( state != STATE_CONNECTING && state != STATE_CONNECTED )
162  return;
163 
164  // Change state
166 }
167 
169 {
170  // Invalidate pointer
171  Buffer* buf = *data;
172  *data = nullptr;
173 
174  // Check we are in STATE_CONNECTED
175  MutexLock sockLock( mMSock );
176 
177  if (GetState() != STATE_CONNECTED) {
178  SafeDelete( buf );
179  return false;
180  }
181 
182  // Push buffer to the send queue
183  MutexLock queueLock( mMSendQueue );
184 
185  mSendQueue.push_back( buf );
186  buf = nullptr;
187 
188  return true;
189 }
190 
192 {
197  sThread.CreateThread(TCPConnectionLoop, this);
198  /* ORIGINAL CODE HERE
199  // Spawn new thread
200  pthread_t thread;
201  pthread_create( &thread, nullptr, TCPConnectionLoop, this );
202  _log(THREAD__WARNING, "StartLoop() - Created thread ID 0x%X for TCPConnectionLoop", thread);
203  sThread.AddThread(thread);*/
204 }
205 
207 {
208  // Block calling thread until work thread terminates
211 }
212 
213 /* This is always called from an IO thread. Either the server socket's thread, or a
214  * special thread we create when we make an outbound connection. */
216  char errbuf[ TCPCONN_ERRBUF_SIZE ];
217  MutexLock lock( mMSock );
218  switch (GetState()) {
219  case STATE_CONNECTING: {
220  if (!Connect( GetrIP(), GetrPort(), errbuf)) {
221  _log(TCP_CLIENT__TRACE, "Process() - Connecting Failed at %s: %s", GetAddress().c_str(), errbuf );
222  return false;
223  }
224  _log(TCP_CLIENT__INFO, "Process() - TCP connectection from %s", GetAddress().c_str() );
225  return true;
226  }
227  case STATE_CONNECTED: {
228  if (!RecvData(errbuf)) {
229  _log(TCP_CLIENT__TRACE, "Process() - Connected RecvData() Failed at %s: %s", GetAddress().c_str(), errbuf );
230  return false;
231  }
232  if (!SendData(errbuf)) {
233  _log(TCP_CLIENT__TRACE, "Process() - Connected SendData() Failed at", "%s: %s", GetAddress().c_str(), errbuf );
234  return false;
235  }
236  return true;
237  }
238  case STATE_DISCONNECTING: {
239  if (!SendData(errbuf)) {
240  _log(TCP_CLIENT__TRACE, "Process() - Disconnecting SendData() Failed at", "%s: %s", GetAddress().c_str(), errbuf );
241  return false;
242  }
243  DoDisconnect();
244  return true;
245  }
246  case STATE_DISCONNECTED:
247  default: {
248  return false;
249  }
250  }
251 }
252 
253 bool TCPConnection::SendData( char* errbuf )
254 {
255  if( errbuf )
256  errbuf[0] = 0;
257 
258  MutexLock lock( mMSock );
259 
260  state_t state = GetState();
261  if( state != STATE_CONNECTED && state != STATE_DISCONNECTING )
262  return false;
263 
264  mMSendQueue.Lock();
265  Buffer* buf(nullptr);
266  int status(0);
267  while (!mSendQueue.empty()) {
268  buf = mSendQueue.front();
269  mSendQueue.pop_front();
271  if (mSendQueue.empty())
272  status = mSock->send( &(*buf)[ 0 ], (uint)buf->size(), MSG_NOSIGNAL);
273  else
274  status = mSock->send( &(*buf)[ 0 ], (uint)buf->size(), (MSG_NOSIGNAL | MSG_MORE) );
275  if (status == SOCKET_ERROR) {
276  if (errno == EWOULDBLOCK) {
277  status = 0;
278  } else {
279  if( errbuf )
280  snprintf( errbuf, TCPCONN_ERRBUF_SIZE, "%s", strerror( errno ) );
281  SafeDelete( buf );
282  return false;
283  }
284  }
285 
286  if ((size_t)status > buf->size()) {
287  if (errbuf)
288  snprintf( errbuf, TCPCONN_ERRBUF_SIZE, "WTF?!? status > size." );
289 
290  SafeDelete( buf );
291  return false;
292  } else if ((size_t)status < buf->size()) {
293  if (status > 0)
294  buf->AssignSeq( buf->begin<uint8>() + status, buf->end<uint8>() );
295  MutexLock queueLock( mMSendQueue );
296  mSendQueue.push_front( buf );
297  buf = nullptr;
298  } else {
299  SafeDelete( buf );
300  }
301  mMSendQueue.Lock();
302  }
304  return true;
305 }
306 
307 bool TCPConnection::RecvData( char* errbuf )
308 {
309  if (errbuf)
310  errbuf[0] = 0;
311 
312  MutexLock lock( mMSock );
313 
314  state_t state = GetState();
315  if ((state != STATE_CONNECTED) && (state != STATE_DISCONNECTING))
316  return false;
317 
318  int status = 0;
319  while (true) {
320  if (!mRecvBuf)
322  else if( mRecvBuf->size() < TCPCONN_RECVBUF_SIZE )
324 
325  status = mSock->recv( &(*mRecvBuf)[ 0 ], (uint)mRecvBuf->size(), MSG_DONTWAIT);
326  if (status == SOCKET_ERROR) {
327  if (errno == EWOULDBLOCK)
328  return true;
329  if (errbuf)
330  snprintf( errbuf, TCPCONN_ERRBUF_SIZE, "%s", strerror(errno));
331  return false;
332  } else if (status == 0) {
333  if (errbuf)
334  snprintf( errbuf, TCPCONN_ERRBUF_SIZE, "No Data Received.");
335  return false;
336  } else if (status) {
337  mRecvBuf->Resize<uint8>(status);
338  if (!ProcessReceivedData(errbuf))
339  return false;
340  } else {
341  if (errbuf)
342  snprintf( errbuf, TCPCONN_ERRBUF_SIZE, "recv() returned unknown status");
343  _log(TCP_CLIENT__ERROR, "TCPConnection::RecvData(): Error: recv() returned unknown status");
344  return false;
345  }
346  }
347  return true;
348 }
349 
351 {
352  MutexLock lock( mMSock );
353 
354  state_t state = GetState();
355  if ((state != STATE_CONNECTED) && (state != STATE_DISCONNECTING))
356  return;
357 
358  ClearBuffers();
359  mrIP = mrPort = 0;
360  SafeDelete( mSock );
361 
363 }
364 
366 {
367  MutexLock lock( mMSendQueue );
368 
369  Buffer* buf = nullptr;
370  while (!mSendQueue.empty()) {
371  buf = mSendQueue.front();
372  mSendQueue.pop_front();
373  SafeDelete(buf);
374  }
376 }
377 
379 {
380  TCPConnection* tcpc = reinterpret_cast< TCPConnection* >( arg );
381  assert( tcpc != nullptr );
382 
383  tcpc->TCPConnectionLoop();
384  sThread.RemoveThread(pthread_self());
385 
386  return nullptr;
387 }
388 
390 {
392  uint32 start = GetTickCount();
393  while (Process()) {
394  // do the stuff for thread sleeping
395  start = GetTickCount() - start;
396  if (TCPCONN_LOOP_GRANULARITY > start)
398  start = GetTickCount();
399  }
400  DoDisconnect();
402 }
uint16 GetrPort() const
Definition: TCPConnection.h:69
Simple wrapper for sockets.
Definition: Socket.h:34
unsigned __int8 uint8
Definition: eve-compat.h:46
virtual ~TCPConnection()
Cleans connection up.
const uint32 TCPCONN_LOOP_GRANULARITY
#define _log(type, fmt,...)
Definition: logsys.h:124
A lock for a Lockable object.
Definition: Lock.h:70
state_t mSockState
void WaitLoop()
Blocks calling thread until working thread terminates.
virtual bool RecvData(char *errbuf=0)
Receives data and puts them into receive queue.
virtual bool SendData(char *errbuf=0)
Sends data in send queue.
void Lock()
Locks the mutex.
Definition: Mutex.cpp:57
virtual void ClearBuffers()
Clears send and receive buffers.
bool Send(Buffer **data)
Enqueues data to be sent.
const uint32 TCPCONN_RECVBUF_SIZE
void SafeDelete(T *&p)
Deletes and nullifies a pointer.
Definition: SafeMem.h:83
TCPConnection()
Creates new connection in STATE_DISCONNECTED.
#define sThread
Definition: Threading.h:50
static const uint32 TCPCONN_ERRBUF_SIZE
Definition: TCPConnection.h:34
iterator< T > begin()
Definition: Buffer.h:381
Generic class for buffers.
Definition: Buffer.h:40
void StartLoop()
Starts working thread.
Socket * mSock
Buffer * mRecvBuf
unsigned int recv(void *buf, unsigned int len, int flags)
Definition: Socket.cpp:59
#define snprintf
Definition: eve-compat.h:184
void Resize(size_type requiredCount, const uint8 &fill=0)
Resizes buffer.
Definition: Buffer.h:670
uint32 GetTickCount()
Definition: eve-compat.cpp:39
std::string GetAddress()
int setopt(int level, int optname, const void *optval, unsigned int optlen)
Definition: Socket.cpp:99
#define MSG_NOSIGNAL
Definition: eve-compat.h:137
void Unlock()
Unlocks the mutex.
Definition: Mutex.cpp:75
virtual bool Process()
Does all stuff that needs to be periodically done to keep connection alive.
Generic class for TCP connections.
Definition: TCPConnection.h:45
unsigned __int32 uint32
Definition: eve-compat.h:50
static void * TCPConnectionLoop(void *arg)
Loop for worker threads.
std::deque< Buffer * > mSendQueue
void AsyncConnect(uint32 rIP, uint16 rPort)
Schedules asynchronous connect to specified address.
unsigned int send(const void *buf, unsigned int len, int flags)
Definition: Socket.cpp:69
bool Connect(uint32 rIP, uint16 rPort, char *errbuf=0)
Connects to specified address.
void Sleep(uint32 x)
Definition: eve-compat.cpp:32
size_type size() const
Definition: Buffer.h:610
#define SOCKET_ERROR
Definition: eve-compat.h:130
void Disconnect()
Schedules disconnect of current connection.
int fcntl(int cmd, long arg)
Definition: Socket.cpp:104
void AssignSeq(Iter first, Iter last)
Assigns a sequence of elements to buffer.
Definition: Buffer.h:504
uint32 GetrIP() const
Definition: TCPConnection.h:67
void DoDisconnect()
Disconnects socket.
int connect(const sockaddr *name, unsigned int namelen)
Definition: Socket.cpp:54
unsigned __int16 uint16
Definition: eve-compat.h:48
entityID heal the character with the entityID note giving you detailed ship status information gives a list of all dynamic entities and players and their destinyState in this bubble shows some current destiny variables save all kick all and halt server immediate command list all items in current location s gives list of cargo contents and volumes in all holds list current session values show current ship DNA show current objects in their destiny state
state_t GetState() const
Definition: TCPConnection.h:77
void TCPConnectionLoop()
Loop for worker threads.
iterator< T > end()
Definition: Buffer.h:387
virtual bool ProcessReceivedData(char *errbuf=0)=0
Processes received data.