1 /*
2    gnauralnet_socket.c
3    low-level network code to access/create GnauralNet
4    Depends on:
5    gnauralnet.h
6    gnauralnet_clocksync.c
7    gnauralnet_lists.c
8    gnauralnet_main.c
9    gnauralnet_socket.c
10 
11    Copyright (C) 2008  Bret Logan
12 
13    This program is free software; you can redistribute it and/or modify
14    it under the terms of the GNU General Public License as published by
15    the Free Software Foundation; either version 2 of the License, or
16    (at your option) any later version.
17 
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21    GNU General Public License for more details.
22 
23    You should have received a copy of the GNU General Public License
24    along with this program; if not, write to the Free Software
25    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "gnauralnet.h"
29 
30 /////////////////////////////////////////////////////////////
31 //IMPORTANT: IP and Port must already be in network order
32 //(i.e. had htons or htonl done to them by caller), since
33 //GN_Friend stores them that way
GN_Socket_SendMessage(SOCKET sok,char * MsgBuf,int sizeof_MsgBuf,unsigned int IP,unsigned short port)34 int GN_Socket_SendMessage (SOCKET sok,  //
35                            char *MsgBuf,        //question
36                            int sizeof_MsgBuf,   //in bytes
37                            unsigned int IP,     //IN NET ORDER htons
38                            unsigned short port) //IN NET ORDER htonl
39 {
40  //put the address in:
41  struct sockaddr_in saddr_remote;       //holds temp addresses for send and recv
42 
43  saddr_remote.sin_family = PF_INET;
44  saddr_remote.sin_port = port;  //it is already in net order
45  saddr_remote.sin_addr.s_addr = IP;     //it is already in net order
46 
47  //send the buffer:
48  // GN_DBGOUT_STR ("Sending to IP:", inet_ntoa (saddr_remote.sin_addr));
49  // GN_DBGOUT_INT ("         Port:", ntohs (port));
50  if (GNAURAL_IS_SOCKET_ERROR
51      (sendto
52       (sok, MsgBuf, sizeof_MsgBuf, 0, (struct sockaddr *) &saddr_remote,
53        sizeof (saddr_remote))))
54  {
55   perror ("sendto() error");
56  }
57  return GNAURALNET_SUCCESS;
58 }
59 
60 ///////////////////////////////////////
61 //NOTE: I NEVER DEBUGGED THE ODD TIME REPORTING HERE; uncomment the dbgout lines to see
62 //This spends usecs time receiving messages; if successful
63 //it guarantees secs + usecs period before returning unless SIGALRM
64 //gets sent across the system (see select())
65 //NOTE: usecs is always a fractional amount under 1e6
GN_Socket_RecvMessage(SOCKET sok,char * MsgBuf,int sizeof_MsgBuf,unsigned int secs,unsigned int usecs)66 int GN_Socket_RecvMessage (SOCKET sok,  //normally GN_My.Socket
67                            char *MsgBuf,        //what to receive into
68                            int sizeof_MsgBuf, unsigned int secs,
69                            unsigned int usecs)
70 {
71  int count = 0;                 //for debugging only
72  int recvsize = 0;
73  struct sockaddr_in saddr_remote;
74 
75 #ifdef GNAURAL_WIN32
76  int iAddrLen = sizeof (saddr_remote);
77 #else
78  unsigned int iAddrLen = sizeof (saddr_remote);
79 #endif
80 
81  //see if socket is ready to be read:
82  GTimeVal time_started;
83  GTimeVal time_now;
84  unsigned int target_usecs = (secs * 1e6) + usecs;
85  unsigned int elapsed_usecs = 0;
86  int interval_secs = secs;
87  int interval_usecs = usecs;
88 
89  //this is one way of returning at a fixed interval:
90  g_get_current_time (&time_started);
91  do
92  {
93   if (0 !=
94       GN_Socket_IsReadyForRead (GN_My.Socket, interval_secs, interval_usecs))
95   {
96    GN_DBGOUT_INT ("Recv packet", ++count);
97    if (0 ==
98        GNAURAL_IS_SOCKET_ERROR ((recvsize =
99                                  recvfrom (sok, MsgBuf, sizeof_MsgBuf, 0,
100                                            (struct sockaddr *) &saddr_remote,
101                                            &iAddrLen))))
102    {    //was a good receive:
103     GN_ProcessIncomingData (MsgBuf, sizeof_MsgBuf, &saddr_remote);
104    }
105    else
106    {
107     GN_DBGOUT ("Socket error");
108     //return GNAURALNET_FAILURE;
109    }
110   }
111   //see if it has taken long enough:
112   g_get_current_time (&time_now);
113 
114   elapsed_usecs += (((time_now.tv_sec - time_started.tv_sec) * 1e6) +
115                     (time_now.tv_usec - time_started.tv_usec));
116 
117   //first get total interval in usecs:
118   interval_usecs = target_usecs - elapsed_usecs;
119   //now separate it in to fractional sec and usec components:
120   if (interval_usecs < 1e6)
121   {
122    interval_secs = 0;
123   }
124   else
125   {
126    interval_secs = interval_usecs / 1e6;
127    interval_usecs = interval_usecs % 1000000;
128   }
129 
130   //  GN_DBGOUT_INT ("Time elapsed_usecs:", elapsed_usecs);
131   //  GN_DBGOUT_INT ("Time to go, secs:", interval_secs);
132   //  GN_DBGOUT_INT ("Time to go, usecs:", interval_usecs);
133  }
134  while (target_usecs > elapsed_usecs);
135  return GNAURALNET_SUCCESS;
136 }
137 
138 /////////////////////////////////////////////////////////////////
139 //Makes a Datagram (UDP) socket
140 /////////////////////////////////////////////////////////////////
GN_Socket_MakeUDP(int port)141 SOCKET GN_Socket_MakeUDP (int port)
142 {
143  SOCKET sok = socket (PF_INET, SOCK_DGRAM, 0);
144 
145  if (GNAURAL_IS_INVALID_SOCKET (sok))
146  {
147   // error occurred on socket call
148   //Windows:
149   //  printf("Error on socket(), error code=%d",WSAGetLastError());
150   //Unix:
151   perror ("socket");
152   return -1;
153  }
154  // set up local address and bind() socket to wild card IP address
155  // and input port number
156  struct sockaddr_in temp_saddr;
157 
158  temp_saddr.sin_family = PF_INET;
159  temp_saddr.sin_port = htons (port);
160  temp_saddr.sin_addr.s_addr = htonl (INADDR_ANY);
161  memset (&(temp_saddr.sin_zero), '\0', 8);      // zero the rest of the struct
162  if (GNAURAL_IS_SOCKET_ERROR
163      (bind (sok, (struct sockaddr *) &temp_saddr, sizeof (struct sockaddr))))
164  {
165   //Windows:
166   //sprintf(szMessageText1,"Error on server bind(), error code=%d",WSAGetLastError());
167   perror ("bind");
168   return -1;
169  }
170  return sok;
171 }
172 
173 /////////////////////////////////////////////////////////////////////
GN_Socket_SetBlocking(SOCKET sok,int yesflag)174 void GN_Socket_SetBlocking (SOCKET sok, int yesflag)
175 //yesflag == 0 sets the socket to non-blocking
176 {
177  if (0 == yesflag)
178  {
179  GN_DBGOUT ("Setting socket for Non-Blocking")}
180  else
181  {
182  GN_DBGOUT ("Setting socket for Blocking")};
183 
184 #ifdef GNAURAL_WIN32
185  unsigned long ioctl_opt;
186 
187  if (0 == yesflag)
188   ioctl_opt = 1;
189  else
190   ioctl_opt = 0;
191 
192  if (SOCKET_ERROR == ioctlsocket (sok, FIONBIO, &ioctl_opt))
193  {
194   perror ("ioctlsocket FAILED");
195  }
196 #else
197  int flags = fcntl (sok, F_GETFL, 0);
198 
199  if (flags != -1)
200  {
201   if (0 == yesflag)
202    flags |= O_NONBLOCK;
203   else
204    flags &= ~O_NONBLOCK;
205 
206   fcntl (sok, F_SETFL, flags);
207  }
208 #endif
209 }
210 
211 /////////////////////////////////////////////////////////////////////
212 //If successful, sets globals (in network order!) GN_My.IP, GN_My.Port,
213 //and GN_My.ID, returning SUCCESS:
GN_Socket_SetLocalInfo()214 int GN_Socket_SetLocalInfo ()
215 {
216  struct sockaddr_in s_in;
217 
218 #ifdef GNAURAL_WIN32
219  int namelen = sizeof (s_in);
220 #else
221  unsigned int namelen = sizeof (s_in);
222 #endif
223 
224  if (getsockname (GN_My.Socket, (struct sockaddr *) &s_in, &namelen) < 0)
225  {
226   GN_DBGOUT_STR ("Can't get local port: %s\n", strerror (errno));
227   return GNAURALNET_FAILURE;
228  }
229 
230  GN_DBGOUT ("Setting local info");
231  GN_My.IP = s_in.sin_addr.s_addr;
232  GN_My.Port = s_in.sin_port;
233  GN_My.ID = rand ();
234  return GNAURALNET_SUCCESS;
235 }
236 
237 ////////////////////////////////////////
238 //waits timeout microsecs for data to arrive on socket
GN_Socket_IsReadyForRead(int sd,unsigned int secs,unsigned int usecs)239 int GN_Socket_IsReadyForRead (int sd, unsigned int secs, unsigned int usecs)
240 {
241  fd_set socketReadSet;
242 
243  FD_ZERO (&socketReadSet);
244  FD_SET (sd, &socketReadSet);
245  struct timeval tv;
246 
247  tv.tv_sec = secs;
248  tv.tv_usec = usecs;
249 
250  if (GNAURAL_IS_SOCKET_ERROR (select (sd + 1, &socketReadSet, 0, 0, &tv)))
251  {
252   return 0;
253  }
254  return (FD_ISSET (sd, &socketReadSet) != 0);
255 }
256