Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged the network branche to the trunk

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