Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/network/tcp_socket.cc @ 9879

Last change on this file since 9879 was 9869, checked in by bensch, 18 years ago

orxonox/trunk: merged the new_class_id branche back to the trunk.
merged with command:
svn merge https://svn.orxonox.net/orxonox/branches/new_class_id trunk -r9683:HEAD
no conflicts… puh..

File size: 10.5 KB
RevLine 
[7541]1
[7540]2/*
3   orxonox - the future of 3D-vertical-scrollers
4
5   Copyright (C) 2004 orx
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
12### File Specific:
[7541]13   main-programmer: Christoph Renner, David Hasenfratz
[7540]14   co-programmer:
15*/
16
[7541]17
18
19/* this is for debug output. It just says, that all calls to PRINT() belong to the DEBUG_MODULE_NETWORK module
20   For more information refere to https://www.orxonox.net/cgi-bin/trac.cgi/wiki/DebugOutput
21*/
22#define DEBUG_MODULE_NETWORK
23
24#include "converter.h"
25
26/* include your own header */
[7540]27#include "tcp_socket.h"
28
[7541]29/* header for debug output */
30#include "debug.h"
31
[9869]32ObjectListDefinition(TcpSocket);
[7540]33/**
[7541]34 * Default constructor
[7540]35 */
[7541]36TcpSocket::TcpSocket()
[7540]37{
[7541]38  this->init();
39}
[7540]40
41/**
[7541]42 * Constructor to connect directly
[7540]43 */
[7541]44TcpSocket::TcpSocket( std::string host, int port )
[7540]45{
[7541]46  this->init();
47  connectToServer( host, port);
[7540]48}
49
[7541]50
51TcpSocket::TcpSocket( TCPsocket sock )
52{
53  this->init();
54  this->tcpSocket = sock;
55
56  readThread = SDL_CreateThread(thread_read, (void*)this);
57  writeThread = SDL_CreateThread(thread_write, (void*)this);
58}
59
60void TcpSocket::init()
61{
62  /* set the class id for the base object */
[9869]63  this->registerObject(this, TcpSocket::_objectList);
[7541]64
65  tcpSocket = NULL;
66  incomingBufferLength = 0;
67  outgoingBufferLength = 0;
68
69  readThread = NULL;
70  writeThread = NULL;
71
72
73  thread_write_running = false;
74  thread_read_running = false;
75
76  incomingBufferMutex = SDL_CreateMutex();
77  outgoingBufferMutex = SDL_CreateMutex();
78
79
80  socketMutex = SDL_CreateMutex();
81  terminateThread = false;
82
83  /* Init SDL_net */
84  //NOTE: do we need to call SDLNet_Init for all instances?
85  if(SDLNet_Init()==-1)
86  {
87    PRINTF(1)("SDLNet_Init: %s\n", SDLNet_GetError());
88    return;
89  }
90  else
91    PRINTF(5)("SDL_net initialized\n");
92
93  PRINTF(0)("TcpSocket created\n");
94
95}
96
97
98
[7540]99/**
[7541]100 * Default destructor
101 * dont use this from outside: use destroy() instead!!
[7540]102 */
103TcpSocket::~TcpSocket( )
104{
[7541]105  this->terminateThread = true;
106  /* Quit SDL_net */
107  // NOTE: what if other instances of TcpSocket running?
108  SDLNet_Quit();
109  PRINTF(5)("SDL_net shutdown\n");
110
111  SDL_DestroyMutex(incomingBufferMutex);
112  SDL_DestroyMutex(outgoingBufferMutex);
113  SDL_DestroyMutex(socketMutex);
114  SDL_DestroyMutex(threadTerminationMutex);
[7540]115}
[7541]116
[7540]117/**
[7541]118 * This function establishes a TCP/UDP connection to a given server (function argument).
119 * It is called by the NetworkStream. It creates a TCP/UDP socket for the connection.
120 * @param ip
[7540]121 */
[7541]122void TcpSocket::connectToServer( std::string host, int port)
[7540]123{
[7541]124  IPaddress ip;
[9869]125
126  bOk = true;
127
[7541]128  if ( SDLNet_ResolveHost( &ip, host.c_str(), port ) != 0 )
129  {
130    PRINTF(1)("could not resolve host %s\n", host.c_str() );
131    bOk = false;
132    return;
133  }
[9869]134
[7541]135  //check if not already connected or listening
136  if (tcpSocket)
137  {
138    PRINTF(1)("TcpSocket::listen: tcpSocket!=NULL! maybe you already called listen or connectToServer or did not call disconnectServer()!");
139  }
140
141  /* Connect to the host and port contained in ip using a TCP connection. */
142  tcpSocket = SDLNet_TCP_Open(&ip);
143  if(!tcpSocket)
144  {
145    PRINTF(1)("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
146    return;
147  }
148
149  readThread = SDL_CreateThread(thread_read, (void*)this);
150  writeThread = SDL_CreateThread(thread_write, (void*)this);
[7540]151}
152
[7541]153
[7540]154/**
[7541]155 * DTears down a TCP/UDP connection.
[7540]156 */
[7541]157void TcpSocket::disconnectServer( )
[7540]158{
[7541]159  terminateThread = true;
160  /* Close the connection */
161
162  SDL_mutexP(socketMutex);
163  SDLNet_TCP_Close(tcpSocket);
164  tcpSocket = NULL;
165  SDL_mutexV(socketMutex);
[7540]166}
167
[7541]168
[7540]169/**
[7541]170 * This function writes some bytes (data) to the network connection (if the connection is already
171 * estabilhed) otherwise it just does nothing (silently discarding the data). And writes some
172 * warnings
173 * @param data: pointer to the data to send
174 * @param length: n bytes to send
175 * @return the number successfully written bytes
[7540]176 */
[7541]177int TcpSocket::writeBytes(byte * data, int length)
[7540]178{
[7541]179  PRINTF(5)("TcpSocket::writeBytes()\n");
180#ifdef _USE_OUTGOING_BUFFER
181
182#define min(a,b) (a<b)?a:b
183  int nbytes = min(_OUTGOING_BUFFER_SIZE - outgoingBufferLength, length);
184#undef min
185
186  if (!tcpSocket || data==NULL || nbytes<=0)
187{
188  assert(_OUTGOING_BUFFER_SIZE - outgoingBufferLength > 0);
189  return 0;
[7540]190}
191
[7541]192  SDL_mutexP(outgoingBufferMutex);
193
194  memcpy(outgoingBuffer + outgoingBufferLength, data, nbytes);
195  outgoingBufferLength += nbytes;
196
197  SDL_mutexV(outgoingBufferMutex);
198
199
200  return nbytes;
201#else
202  SDL_mutexP(socketMutex);
203
204  if (!tcpSocket || data==NULL)
205    return 0;
206
207  int res = SDLNet_TCP_Send(tcpSocket, data, length);
208
209  SDL_mutexV(socketMutex);
210
211  if (res<length)
212    PRINTF(1)("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
213
214  return res;
215#endif
216}
217
[7540]218/**
[7541]219 * Reads in the bytes from the network interface and passes it to the NetworkStream.
220 * This function must internaly be implemented/connected as a thread, since the read
221 * functions of many network libraries are blocking an would therefore block the whole
222 * program.
223 * From outside, the thread shouldn't be accessible at all.
224 * @param data: pointer to memory, big enough to store length bytes
225 * @param length: n bytes to read
226 * @return the number successfully read bytes. -1 on error. may be less than length!
[7540]227 */
[7541]228int TcpSocket::readBytes(byte * data, int length)
[7540]229{
[7541]230  PRINTF(5)("TcpSocket::readBytes()\n");
231  if (data==NULL)
232    return 0;
233
234  int nbytes = (length<incomingBufferLength) ? length : incomingBufferLength;
235
236
237  //printf("readBytes: nbytes = %d; length=%d; incomingBufferLength=%d\n", nbytes, length, incomingBufferLength);
238
239  // just in case ...
240  if (nbytes<0)
241    return -1;
242
243  if (nbytes==0)
244    return 0;
245
246  SDL_mutexP(incomingBufferMutex);
247
248  memcpy(data, incomingBuffer, nbytes);
249
250  //important: use memmove because the memory areas may overlap
251  memmove(incomingBuffer, incomingBuffer+nbytes, incomingBufferLength-nbytes);
252  incomingBufferLength -= nbytes;
253
254  SDL_mutexV(incomingBufferMutex);
255
256  return nbytes;
[7540]257}
258
[7541]259/**
260 * Reads in the bytes form the network interface and passes it to the NetworkStream.
261 * It only reads the bytes if there are enough bytes in our buffer.
262 * @param data: pointer to memory, big enough to store length bytes
263 * @param length: n bytes to read
264 * @return the number successfully read bytes. -1 on error. 0 if there are not enough bytes in our buffer.
265 */
266int TcpSocket::readBlock(byte * data, int length)
267{
268  printf("TcpSocket: got %i bytes, NetworkStream requested %i bytes\n", this->incomingBufferLength, length);
269  if (incomingBufferLength >= length)
270    return readBytes(data, length);
271  else return 0;
272}
273
274
275/**
276 * used to create a thread to read from socket
277 * @param data: pointer to TcpSocket
278 */
279int TcpSocket::thread_read( void * data )
280{
281  int nbytesread = 0;
282  int nbytestoread = 0;
283  char buffer[_LOCAL_BUFFER_SIZE];
284  TcpSocket * self = (TcpSocket*)data;
285
286  self->thread_read_running = true;
287
288  while (!self->terminateThread)
289  {
290#define min(a,b) (a<b)?a:b
291    nbytestoread = min(_INCOMING_BUFFER_SIZE - self->incomingBufferLength, _LOCAL_BUFFER_SIZE);
292#undef min
293
294    //if buffer is full
295    if (nbytestoread<=0 || !self->tcpSocket)
296{
297  SDL_Delay(_MSECONDS_SLEEP_FULL_BUFFER);
298  continue;
299}
300
301    nbytesread = SDLNet_TCP_Recv(self->tcpSocket, buffer, nbytestoread);
302
303    SDL_mutexP(self->incomingBufferMutex);
304
305    if (nbytesread<=0)
306{
307  if (nbytesread<0)
308    printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
309
310  SDL_mutexP(self->socketMutex);
311
312  SDLNet_TCP_Close(self->tcpSocket);
313  self->tcpSocket = NULL;
314
315  SDL_mutexV(self->socketMutex);
316  SDL_mutexV(self->incomingBufferMutex);
317  continue;
318}
319
320    //printf("thread_read: nbytesread=%d\n", nbytesread);
321
322    memcpy(self->incomingBuffer+self->incomingBufferLength, buffer, nbytesread);
323    self->incomingBufferLength += nbytesread;
324
325    SDL_mutexV(self->incomingBufferMutex);
326  }
327
328  SDL_mutexP(self->threadTerminationMutex);
329  self->thread_read_running = false;
330
331  if ( !self->thread_write_running )
332{
333    //delete self;
334  SDL_mutexV(self->threadTerminationMutex);
335}
336  else
337{
338  SDL_mutexV(self->threadTerminationMutex);
339}
340
341
342#ifdef DONTEXITTHREADS
343  while ( true )
344{
345  SDL_Delay(1000);
346}
347#endif
[9869]348
[7541]349  PRINTF(0)("QUIT READ THREAD\n");
[9869]350
[7541]351  return 0;
352}
353
354int TcpSocket::thread_write( void * data )
355{
356  int nbyteswrite = 0;
357  int nbytestowrite = 0;
358  char buffer[_LOCAL_BUFFER_SIZE];
359  TcpSocket * self = (TcpSocket*)data;
360
361  self->thread_write_running = true;
362
363  while (!self->terminateThread)
364  {
365#define min(a,b) (a<b)?a:b
366    nbytestowrite = min(self->outgoingBufferLength, _LOCAL_BUFFER_SIZE);
367#undef min
368
369//     printf("thread_write nbytes=%d listening=%d\n", nbytestowrite, (int)self->_isListening);
370
371    //if buffer is full
372    if (nbytestowrite<=0 || !self->tcpSocket)
373{
374  SDL_Delay(_MSECONDS_SLEEP_EMPTY_BUFFER);
375  continue;
376}
377
378    SDL_mutexP(self->outgoingBufferMutex);
379
380    //printf("a\n");
381
382    memcpy(buffer, self->outgoingBuffer, nbytestowrite);
383    self->outgoingBufferLength -= nbytestowrite;
384    memmove(self->outgoingBuffer, self->outgoingBuffer+nbytestowrite, self->outgoingBufferLength);
385
386    SDL_mutexV(self->outgoingBufferMutex);
387
388    nbyteswrite = SDLNet_TCP_Send(self->tcpSocket, buffer, nbytestowrite);
389
390    if (nbyteswrite<=0)
391{
392  printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
393
394  SDL_mutexP(self->socketMutex);
395
396  SDLNet_TCP_Close(self->tcpSocket);
397  self->tcpSocket = NULL;
398
399  SDL_mutexV(self->socketMutex);
400  continue;
401}
402
403  }
404
405  SDL_mutexP(self->threadTerminationMutex);
406  self->thread_write_running = false;
407
408  if ( !self->thread_read_running )
409{
410    //delete self;
411  SDL_mutexV(self->threadTerminationMutex);
412}
413  else
414{
415  SDL_mutexV(self->threadTerminationMutex);
416}
417
418#ifdef DONTEXITTHREADS
419  while ( true )
420{
421  SDL_Delay(1000);
422}
423#endif
424
425  PRINTF(0)("QUIT WRITE THREAD\n");
426
427  return 0;
[9869]428
[7541]429}
430
431bool TcpSocket::writePacket( byte * data, int length )
432{
433  PRINTF(5)("TcpSocket::writePacket() size=%d\n", length);
434
435  if ( length > 1024 )
436    PRINTF(2)("WARNING SENDING BIG PACKET SIZE = %d\n", length);
437
438  byte blen[INTSIZE];
439
440  Converter::intToByteArray( length, blen, INTSIZE );
441
442  writeBytes(blen, INTSIZE);
443  writeBytes(data, length);
444}
445
446int TcpSocket::readPacket( byte * data, int maxLength )
447{
448  PRINTF(5)("TcpSocket::readPacket()\n");
449  if (incomingBufferLength<INTSIZE)
450  {
451    return 0;
452  }
453
454  int blen;
455  Converter::byteArrayToInt( incomingBuffer, &blen );
456
457  if (blen>maxLength)
458  {
459    PRINTF(1)("Buffersize is too small (%d) for packet (%d).\n", maxLength, blen);
460    assert(false);
461    return 0;
462  }
463
464  if (blen>incomingBufferLength)
465  {
466    return 0;
467  }
468
469  byte t[INTSIZE];
470  readBytes(t, INTSIZE);
471  int res = readBytes(data, blen);
472
473  if (res!=blen)
474    return -1;
475  else
476    return blen;
477
478}
479
480
Note: See TracBrowser for help on using the repository browser.