zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDLnetUDP.c
Go to the documentation of this file.
1 /*
2  SDL_net: An example cross-platform network library for use with SDL
3  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 
22 /* $Id$ */
23 
24 #include "SDLnetsys.h"
25 #include "SDL_net.h"
26 
27 #ifdef __WIN32__
28 #define srandom srand
29 #define random rand
30 #endif
31 
32 struct UDP_channel {
33  int numbound;
35 };
36 
37 struct _UDPsocket {
38  int ready;
39  SOCKET channel;
41 
42  struct UDP_channel binding[SDLNET_MAX_UDPCHANNELS];
43 
44  /* For debugging purposes */
45  int packetloss;
46 };
47 
48 /* Allocate/free a single UDP packet 'size' bytes long.
49  The new packet is returned, or NULL if the function ran out of memory.
50  */
52 {
53  UDPpacket *packet;
54  int error;
55 
56 
57  error = 1;
58  packet = (UDPpacket *)malloc(sizeof(*packet));
59  if ( packet != NULL ) {
60  packet->maxlen = size;
61  packet->data = (Uint8 *)malloc(size);
62  if ( packet->data != NULL ) {
63  error = 0;
64  }
65  }
66  if ( error ) {
67  SDLNet_SetError("Out of memory");
68  SDLNet_FreePacket(packet);
69  packet = NULL;
70  }
71  return(packet);
72 }
73 int SDLNet_ResizePacket(UDPpacket *packet, int newsize)
74 {
75  Uint8 *newdata;
76 
77  newdata = (Uint8 *)malloc(newsize);
78  if ( newdata != NULL ) {
79  free(packet->data);
80  packet->data = newdata;
81  packet->maxlen = newsize;
82  }
83  return(packet->maxlen);
84 }
85 extern void SDLNet_FreePacket(UDPpacket *packet)
86 {
87  if ( packet ) {
88  free(packet->data);
89  free(packet);
90  }
91 }
92 
93 /* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets,
94  each 'size' bytes long.
95  A pointer to the packet array is returned, or NULL if the function ran out
96  of memory.
97  */
98 UDPpacket **SDLNet_AllocPacketV(int howmany, int size)
99 {
100  UDPpacket **packetV;
101 
102  packetV = (UDPpacket **)malloc((howmany+1)*sizeof(*packetV));
103  if ( packetV != NULL ) {
104  int i;
105  for ( i=0; i<howmany; ++i ) {
106  packetV[i] = SDLNet_AllocPacket(size);
107  if ( packetV[i] == NULL ) {
108  break;
109  }
110  }
111  packetV[i] = NULL;
112 
113  if ( i != howmany ) {
114  SDLNet_SetError("Out of memory");
115  SDLNet_FreePacketV(packetV);
116  packetV = NULL;
117  }
118  }
119  return(packetV);
120 }
122 {
123  if ( packetV ) {
124  int i;
125  for ( i=0; packetV[i]; ++i ) {
126  SDLNet_FreePacket(packetV[i]);
127  }
128  free(packetV);
129  }
130 }
131 
132 /* Since the UNIX/Win32/BeOS code is so different from MacOS,
133  we'll just have two completely different sections here.
134 */
135 
136 /* Open a UDP network socket
137  If 'port' is non-zero, the UDP socket is bound to a fixed local port.
138 */
140 {
141  UDPsocket sock;
142  struct sockaddr_in sock_addr;
143  socklen_t sock_len;
144 
145  /* Allocate a UDP socket structure */
146  sock = (UDPsocket)malloc(sizeof(*sock));
147  if ( sock == NULL ) {
148  SDLNet_SetError("Out of memory");
149  goto error_return;
150  }
151  memset(sock, 0, sizeof(*sock));
152  memset(&sock_addr, 0, sizeof(sock_addr));
153 
154  /* Open the socket */
155  sock->channel = socket(AF_INET, SOCK_DGRAM, 0);
156  if ( sock->channel == INVALID_SOCKET )
157  {
158  SDLNet_SetError("Couldn't create socket");
159  goto error_return;
160  }
161 
162  /* Bind locally, if appropriate */
163  sock_addr.sin_family = AF_INET;
164  sock_addr.sin_addr.s_addr = INADDR_ANY;
165  sock_addr.sin_port = SDLNet_Read16(&port);
166 
167  /* Bind the socket for listening */
168  if ( bind(sock->channel, (struct sockaddr *)&sock_addr,
169  sizeof(sock_addr)) == SOCKET_ERROR ) {
170  SDLNet_SetError("Couldn't bind to local port");
171  goto error_return;
172  }
173 
174  /* Get the bound address and port */
175  sock_len = sizeof(sock_addr);
176  if ( getsockname(sock->channel, (struct sockaddr *)&sock_addr, &sock_len) < 0 ) {
177  SDLNet_SetError("Couldn't get socket address");
178  goto error_return;
179  }
180 
181  /* Fill in the channel host address */
182  sock->address.host = sock_addr.sin_addr.s_addr;
183  sock->address.port = sock_addr.sin_port;
184 
185 #ifdef SO_BROADCAST
186  /* Allow LAN broadcasts with the socket */
187  { int yes = 1;
188  setsockopt(sock->channel, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));
189  }
190 #endif
191 #ifdef IP_ADD_MEMBERSHIP
192  /* Receive LAN multicast packets on 224.0.0.1
193  This automatically works on Mac OS X, Linux and BSD, but needs
194  this code on Windows.
195  */
196  /* A good description of multicast can be found here:
197  http://www.docs.hp.com/en/B2355-90136/ch05s05.html
198  */
199  /* FIXME: Add support for joining arbitrary groups to the API */
200  {
201  struct ip_mreq g;
202 
203  g.imr_multiaddr.s_addr = inet_addr("224.0.0.1");
204  g.imr_interface.s_addr = INADDR_ANY;
205  setsockopt(sock->channel, IPPROTO_IP, IP_ADD_MEMBERSHIP,
206  (char*)&g, sizeof(g));
207  }
208 #endif
209 
210  /* The socket is ready */
211 
212  return(sock);
213 
214 error_return:
215  SDLNet_UDP_Close(sock);
216 
217  return(NULL);
218 }
219 
221 {
222  /* FIXME: We may want this behavior to be reproducible
223  but there isn't a portable reentrant random
224  number generator with good randomness.
225  */
226  srandom(time(NULL));
227 
228  if (percent < 0) {
229  percent = 0;
230  } else if (percent > 100) {
231  percent = 100;
232  }
233  sock->packetloss = percent;
234 }
235 
236 /* Verify that the channel is in the valid range */
237 static int ValidChannel(int channel)
238 {
239  if ( (channel < 0) || (channel >= SDLNET_MAX_UDPCHANNELS) ) {
240  SDLNet_SetError("Invalid channel");
241  return(0);
242  }
243  return(1);
244 }
245 
246 /* Bind the address 'address' to the requested channel on the UDP socket.
247  If the channel is -1, then the first unbound channel that has not yet
248  been bound to the maximum number of addresses will be bound with
249  the given address as it's primary address.
250  If the channel is already bound, this new address will be added to the
251  list of valid source addresses for packets arriving on the channel.
252  If the channel is not already bound, then the address becomes the primary
253  address, to which all outbound packets on the channel are sent.
254  This function returns the channel which was bound, or -1 on error.
255 */
256 int SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address)
257 {
258  struct UDP_channel *binding;
259 
260  if ( sock == NULL ) {
261  SDLNet_SetError("Passed a NULL socket");
262  return(-1);
263  }
264 
265  if ( channel == -1 ) {
266  for ( channel=0; channel < SDLNET_MAX_UDPCHANNELS; ++channel ) {
267  binding = &sock->binding[channel];
268  if ( binding->numbound < SDLNET_MAX_UDPADDRESSES ) {
269  break;
270  }
271  }
272  } else {
273  if ( ! ValidChannel(channel) ) {
274  return(-1);
275  }
276  binding = &sock->binding[channel];
277  }
278  if ( binding->numbound == SDLNET_MAX_UDPADDRESSES ) {
279  SDLNet_SetError("No room for new addresses");
280  return(-1);
281  }
282  binding->address[binding->numbound++] = *address;
283  return(channel);
284 }
285 
286 /* Unbind all addresses from the given channel */
287 void SDLNet_UDP_Unbind(UDPsocket sock, int channel)
288 {
289  if ( (channel >= 0) && (channel < SDLNET_MAX_UDPCHANNELS) ) {
290  sock->binding[channel].numbound = 0;
291  }
292 }
293 
294 /* Get the primary IP address of the remote system associated with the
295  socket and channel.
296  If the channel is not bound, this function returns NULL.
297  */
299 {
301 
302  address = NULL;
303  switch (channel) {
304  case -1:
305  /* Return the actual address of the socket */
306  address = &sock->address;
307  break;
308  default:
309  /* Return the address of the bound channel */
310  if ( ValidChannel(channel) &&
311  (sock->binding[channel].numbound > 0) ) {
312  address = &sock->binding[channel].address[0];
313  }
314  break;
315  }
316  return(address);
317 }
318 
319 /* Send a vector of packets to the the channels specified within the packet.
320  If the channel specified in the packet is -1, the packet will be sent to
321  the address in the 'src' member of the packet.
322  Each packet will be updated with the status of the packet after it has
323  been sent, -1 if the packet send failed.
324  This function returns the number of packets sent.
325 */
327 {
328  int numsent, i, j;
329  struct UDP_channel *binding;
330  int status;
331  int sock_len;
332  struct sockaddr_in sock_addr;
333 
334  if ( sock == NULL ) {
335  SDLNet_SetError("Passed a NULL socket");
336  return(0);
337  }
338 
339  /* Set up the variables to send packets */
340  sock_len = sizeof(sock_addr);
341 
342  numsent = 0;
343  for ( i=0; i<npackets; ++i )
344  {
345  /* Simulate packet loss, if desired */
346  if (sock->packetloss) {
347  if ((random()%100) <= sock->packetloss) {
348  packets[i]->status = packets[i]->len;
349  ++numsent;
350  continue;
351  }
352  }
353 
354  /* if channel is < 0, then use channel specified in sock */
355 
356  if ( packets[i]->channel < 0 )
357  {
358  sock_addr.sin_addr.s_addr = packets[i]->address.host;
359  sock_addr.sin_port = packets[i]->address.port;
360  sock_addr.sin_family = AF_INET;
361  status = sendto(sock->channel,
362  packets[i]->data, packets[i]->len, 0,
363  (struct sockaddr *)&sock_addr,sock_len);
364  if ( status >= 0 )
365  {
366  packets[i]->status = status;
367  ++numsent;
368  }
369  }
370  else
371  {
372  /* Send to each of the bound addresses on the channel */
373 #ifdef DEBUG_NET
374  printf("SDLNet_UDP_SendV sending packet to channel = %d\n", packets[i]->channel );
375 #endif
376 
377  binding = &sock->binding[packets[i]->channel];
378 
379  for ( j=binding->numbound-1; j>=0; --j )
380  {
381  sock_addr.sin_addr.s_addr = binding->address[j].host;
382  sock_addr.sin_port = binding->address[j].port;
383  sock_addr.sin_family = AF_INET;
384  status = sendto(sock->channel,
385  packets[i]->data, packets[i]->len, 0,
386  (struct sockaddr *)&sock_addr,sock_len);
387  if ( status >= 0 )
388  {
389  packets[i]->status = status;
390  ++numsent;
391  }
392  }
393  }
394  }
395 
396  return(numsent);
397 }
398 
399 int SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet)
400 {
401  /* This is silly, but... */
402  packet->channel = channel;
403  return(SDLNet_UDP_SendV(sock, &packet, 1));
404 }
405 
406 /* Returns true if a socket is has data available for reading right now */
408 {
409  int retval = 0;
410  struct timeval tv;
411  fd_set mask;
412 
413  /* Check the file descriptors for available data */
414  do {
416 
417  /* Set up the mask of file descriptors */
418  FD_ZERO(&mask);
419  FD_SET(sock, &mask);
420 
421  /* Set up the timeout */
422  tv.tv_sec = 0;
423  tv.tv_usec = 0;
424 
425  /* Look! */
426  retval = select(sock+1, &mask, NULL, NULL, &tv);
427  } while ( SDLNet_GetLastError() == EINTR );
428 
429  return(retval == 1);
430 }
431 
432 /* Receive a vector of pending packets from the UDP socket.
433  The returned packets contain the source address and the channel they arrived
434  on. If they did not arrive on a bound channel, the the channel will be set
435  to -1.
436  This function returns the number of packets read from the network, or -1
437  on error. This function does not block, so can return 0 packets pending.
438 */
440 {
441  int numrecv, i, j;
442  struct UDP_channel *binding;
443  socklen_t sock_len;
444  struct sockaddr_in sock_addr;
445 
446  if ( sock == NULL ) {
447  return(0);
448  }
449 
450  numrecv = 0;
451  while ( packets[numrecv] && SocketReady(sock->channel) )
452  {
453  UDPpacket *packet;
454 
455  packet = packets[numrecv];
456 
457  sock_len = sizeof(sock_addr);
458  packet->status = recvfrom(sock->channel,
459  packet->data, packet->maxlen, 0,
460  (struct sockaddr *)&sock_addr,
461  &sock_len);
462  if ( packet->status >= 0 ) {
463  packet->len = packet->status;
464  packet->address.host = sock_addr.sin_addr.s_addr;
465  packet->address.port = sock_addr.sin_port;
466  packet->channel = -1;
467 
468  for (i=(SDLNET_MAX_UDPCHANNELS-1); i>=0; --i )
469  {
470  binding = &sock->binding[i];
471 
472  for ( j=binding->numbound-1; j>=0; --j )
473  {
474  if ( (packet->address.host == binding->address[j].host) &&
475  (packet->address.port == binding->address[j].port) )
476  {
477  packet->channel = i;
478  goto foundit; /* break twice */
479  }
480  }
481  }
482 foundit:
483  ++numrecv;
484  }
485 
486  else
487  {
488  packet->len = 0;
489  }
490  }
491 
492  sock->ready = 0;
493 
494  return(numrecv);
495 }
496 
497 /* Receive a single packet from the UDP socket.
498  The returned packet contains the source address and the channel it arrived
499  on. If it did not arrive on a bound channel, the the channel will be set
500  to -1.
501  This function returns the number of packets read from the network, or -1
502  on error. This function does not block, so can return 0 packets pending.
503 */
505 {
506  UDPpacket *packets[2];
507 
508  /* Receive a packet array of 1 */
509  packets[0] = packet;
510  packets[1] = NULL;
511  return(SDLNet_UDP_RecvV(sock, packets));
512 }
513 
514 /* Close a UDP network socket */
516 {
517  if ( sock != NULL )
518  {
519  if ( sock->channel != INVALID_SOCKET )
520  {
521  closesocket(sock->channel);
522  }
523 
524  free(sock);
525  }
526 }
527 
DECLSPEC void SDLCALL SDLNet_SetError(const char *fmt,...)
Definition: SDLnet.c:65
static int SocketReady(SOCKET sock)
Definition: SDLnetUDP.c:407
#define SOCKET
Definition: SDLnetsys.h:78
Uint32 host
Definition: SDL_net.h:91
static int ValidChannel(int channel)
Definition: SDLnetUDP.c:237
struct _UDPsocket * UDPsocket
Definition: SDL_net.h:182
DECLSPEC UDPsocket SDLCALL SDLNet_UDP_Open(Uint16 port)
Definition: SDLnetUDP.c:139
#define closesocket
Definition: SDLnetsys.h:76
#define NULL
Definition: ftobjs.h:61
SDL_EventEntry * free
Definition: SDL_events.c:80
int32_t j
Definition: e_log.c:102
#define memset
Definition: SDL_malloc.c:633
int SDLNet_GetLastError(void)
Definition: SDLnet.c:51
DECLSPEC UDPpacket *SDLCALL SDLNet_AllocPacket(int size)
Definition: SDLnetUDP.c:51
int len
Definition: SDL_net.h:186
IPaddress address
Definition: SDL_net.h:189
DECLSPEC void SDLCALL SDLNet_UDP_Unbind(UDPsocket sock, int channel)
Definition: SDLnetUDP.c:287
static UDPpacket ** packets
Definition: chat.cpp:46
#define SDLNET_MAX_UDPADDRESSES
Definition: SDL_net.h:180
#define INVALID_SOCKET
Definition: SDLnetsys.h:79
int channel
Definition: SDL_net.h:184
FT_Error error
Definition: cffdrivr.c:407
DECLSPEC int SDLCALL SDLNet_UDP_SendV(UDPsocket sock, UDPpacket **packets, int npackets)
Definition: SDLnetUDP.c:326
DECLSPEC void SDLCALL SDLNet_FreePacketV(UDPpacket **packetV)
Definition: SDLnetUDP.c:121
DECLSPEC UDPpacket **SDLCALL SDLNet_AllocPacketV(int howmany, int size)
Definition: SDLnetUDP.c:98
#define SDLNET_MAX_UDPCHANNELS
Definition: SDL_net.h:178
GLuint GLuint64EXT address
Definition: glew.h:13266
Uint16 port
Definition: SDL_net.h:92
DECLSPEC int SDLCALL SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet)
Definition: SDLnetUDP.c:399
#define malloc
Definition: SDL_malloc.c:635
DECLSPEC void SDLCALL SDLNet_UDP_SetPacketLoss(UDPsocket sock, int percent)
Definition: SDLnetUDP.c:220
DECLSPEC void SDLCALL SDLNet_FreePacket(UDPpacket *packet)
Definition: SDLnetUDP.c:85
TCPsocket sock
Definition: chatd.c:41
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:129
#define INADDR_ANY
Definition: SDL_net.h:102
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
Definition: gl2ext.h:961
int status
Definition: SDL_net.h:188
DECLSPEC void SDLCALL SDLNet_UDP_Close(UDPsocket sock)
Definition: SDLnetUDP.c:515
DECLSPEC int SDLCALL SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address)
Definition: SDLnetUDP.c:256
DECLSPEC IPaddress *SDLCALL SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel)
Definition: SDLnetUDP.c:298
#define SOCKET_ERROR
Definition: SDLnetsys.h:80
uint16_t Uint16
An unsigned 16-bit integer type.
Definition: SDL_stdinc.h:137
int maxlen
Definition: SDL_net.h:187
void SDLNet_SetLastError(int err)
Definition: SDLnet.c:56
#define SDLNet_Read16(areap)
Definition: SDL_net.h:380
int i
Definition: pngrutil.c:1377
Uint8 * data
Definition: SDL_net.h:185
DECLSPEC int SDLCALL SDLNet_ResizePacket(UDPpacket *packet, int newsize)
Definition: SDLnetUDP.c:73
DECLSPEC int SDLCALL SDLNet_UDP_Recv(UDPsocket sock, UDPpacket *packet)
Definition: SDLnetUDP.c:504
DECLSPEC int SDLCALL SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets)
Definition: SDLnetUDP.c:439
GLsizei size
Definition: gl2ext.h:1467