Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/network/network_socket.cc @ 6959

Last change on this file since 6959 was 6959, checked in by patrick, 18 years ago

trunk: merged network branche into trunk

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