Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/terrain/src/lib/network/tcp_socket.cc @ 9822

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

trunk: merged the network branche back to trunk.

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