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