Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/network/src/lib/network/network_socket.cc @ 5804

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

network: changed the synchronizeable interface, since the data synchronizeables, more debug output

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