Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5732 was 5732, checked in by rennerc, 19 years ago

network_socket: added constructor which connects directly

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