]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_socket.cxx
Moved to SimGear.
[simgear.git] / simgear / io / sg_socket.cxx
1 // sg_socket.cxx -- Socket I/O routines
2 //
3 // Written by Curtis Olson, started November 1999.
4 //
5 // Copyright (C) 1999  Curtis L. Olson - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #include <simgear/compiler.h>
25
26 #if ! defined( _MSC_VER )
27 #  include <sys/time.h>         // select()
28 #  include <sys/types.h>        // socket(), bind(), select(), accept()
29 #  include <sys/socket.h>       // socket(), bind(), listen(), accept()
30 #  include <netinet/in.h>       // struct sockaddr_in
31 #  include <netdb.h>            // gethostbyname()
32 #  include <unistd.h>           // select(), fsync()/fdatasync()
33 #else
34 #  include <sys/timeb.h>        // select()
35 #  include <winsock2.h>         // socket(), bind(), listen(), accept(),
36                                 // struct sockaddr_in, gethostbyname()
37 #  include <windows.h>
38 #  include <io.h>
39 #endif
40
41 #if defined( sgi )
42 #include <strings.h>
43 #endif
44
45 #include STL_STRING
46
47 #include <simgear/debug/logstream.hxx>
48
49 #include "sg_socket.hxx"
50
51 FG_USING_STD(string);
52
53
54 SGSocket::SGSocket() :
55     save_len(0)
56 {
57     set_type( sgSocketType );
58 }
59
60
61 SGSocket::~SGSocket() {
62 }
63
64
65 int SGSocket::make_server_socket () {
66     struct sockaddr_in name;
67
68 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER )
69     int length;
70 #else
71     socklen_t length;
72 #endif
73      
74     // Create the socket.
75     sock = socket (PF_INET, SOCK_STREAM, 0);
76     if (sock < 0) {
77         FG_LOG( FG_IO, FG_ALERT, 
78                 "Error: socket() failed in make_server_socket()" );
79         return -1;
80     }
81      
82     // Give the socket a name.
83     name.sin_family = AF_INET;
84     name.sin_addr.s_addr = INADDR_ANY;
85     name.sin_port = htons(port); // set port to zero to let system pick
86     name.sin_addr.s_addr = htonl (INADDR_ANY);
87     if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
88         FG_LOG( FG_IO, FG_ALERT,
89                 "Error: bind() failed in make_server_socket()" );
90         return -1;
91     }
92      
93     // Find the assigned port number
94     length = sizeof(struct sockaddr_in);
95     if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
96         FG_LOG( FG_IO, FG_ALERT,
97                 "Error: getsockname() failed in make_server_socket()" );
98         return -1;
99     }
100     port = ntohs(name.sin_port);
101
102     return sock;
103 }
104
105
106 int SGSocket::make_client_socket () {
107     struct sockaddr_in name;
108     struct hostent *hp;
109      
110     FG_LOG( FG_IO, FG_INFO, "Make client socket()" );
111
112     // Create the socket.
113     sock = socket (PF_INET, SOCK_STREAM, 0);
114     if (sock < 0) {
115         FG_LOG( FG_IO, FG_ALERT, 
116                 "Error: socket() failed in make_client_socket()" );
117         return -1;
118     }
119      
120     // specify address family
121     name.sin_family = AF_INET;
122
123     // get the hosts official name/info
124     hp = gethostbyname( hostname.c_str() );
125
126     // Connect this socket to the host and the port specified on the
127     // command line
128 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ )
129     bcopy(hp->h_addr, (char *)(&(name.sin_addr.s_addr)), hp->h_length);
130 #else
131     bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
132 #endif
133     name.sin_port = htons(port);
134
135     if ( connect(sock, (struct sockaddr *) &name, 
136                  sizeof(struct sockaddr_in)) < 0 )
137     {
138 #ifdef _MSC_VER
139         _close(sock);
140 #else
141         std::close(sock);
142 #endif
143         FG_LOG( FG_IO, FG_ALERT, 
144                 "Error: connect() failed in make_client_socket()" );
145         return -1;
146     }
147
148     return sock;
149 }
150
151
152 // If specified as a server (out direction for now) open the master
153 // listening socket.  If specified as a client, open a connection to a
154 // server.
155
156 bool SGSocket::open( SGProtocolDir dir ) {
157     if ( port_str == "" || port_str == "any" ) {
158         port = 0; 
159     } else {
160         port = atoi( port_str.c_str() );
161     }
162     
163     client_connections.clear();
164
165     if ( dir == SG_IO_OUT ) {
166         // this means server for now
167
168         // Setup socket to listen on.  Set "port" before making this
169         // call.  A port of "0" indicates that we want to let the os
170         // pick any available port.
171         sock = make_server_socket();
172         FG_LOG( FG_IO, FG_INFO, "socket is connected to port = " << port );
173
174         // Specify the maximum length of the connection queue
175         listen(sock, SG_MAX_SOCKET_QUEUE);
176
177     } else if ( dir == SG_IO_IN ) {
178         // this means client for now
179
180         sock = make_client_socket();
181     } else {
182         FG_LOG( FG_IO, FG_ALERT, 
183                 "Error:  bidirection mode not available yet for sockets." );
184         return false;
185     }
186
187     if ( sock < 0 ) {
188         FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname
189                 << ":" << port );
190         return false;
191     }
192
193     return true;
194 }
195
196
197 // read data from socket (client)
198 // read a block of data of specified size
199 int SGSocket::read( char *buf, int length ) {
200     int result = 0;
201
202     // check for potential input
203     fd_set ready;
204     FD_ZERO(&ready);
205     FD_SET(sock, &ready);
206     struct timeval tv;
207     tv.tv_sec = 0;
208     tv.tv_usec = 0;
209
210     // test for any input read on sock (returning immediately, even if
211     // nothing)
212     select(32, &ready, 0, 0, &tv);
213
214     if ( FD_ISSET(sock, &ready) ) {
215 #ifdef _MSC_VER
216         result = _read( sock, buf, length );
217 #else
218         result = std::read( sock, buf, length );
219 #endif
220         if ( result != length ) {
221             FG_LOG( FG_IO, FG_INFO, 
222                     "Warning: read() not enough bytes." );
223         }
224     }
225
226     return result;
227 }
228
229
230 // read a line of data, length is max size of input buffer
231 int SGSocket::readline( char *buf, int length ) {
232     int result = 0;
233
234     // check for potential input
235     fd_set ready;
236     FD_ZERO(&ready);
237     FD_SET(sock, &ready);
238     struct timeval tv;
239     tv.tv_sec = 0;
240     tv.tv_usec = 0;
241
242     // test for any input read on sock (returning immediately, even if
243     // nothing)
244     select(32, &ready, 0, 0, &tv);
245
246     if ( FD_ISSET(sock, &ready) ) {
247         // read a chunk, keep in the save buffer until we have the
248         // requested amount read
249
250         char *buf_ptr = save_buf + save_len;
251 #ifdef _MSC_VER
252         result = _read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
253 #else
254         result = std::read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
255 #endif
256         save_len += result;
257         // cout << "current read = " << buf_ptr << endl;
258         // cout << "current save_buf = " << save_buf << endl;
259         // cout << "save_len = " << save_len << endl;
260     }
261
262     // look for the end of line in save_buf
263     int i;
264     for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
265     if ( save_buf[i] == '\n' ) {
266         result = i + 1;
267     } else {
268         // no end of line yet
269         // cout << "no eol found" << endl;
270         return 0;
271     }
272     // cout << "line length = " << result << endl;
273
274     // we found an end of line
275
276     // copy to external buffer
277     strncpy( buf, save_buf, result );
278     buf[result] = '\0';
279     // cout << "sg_socket line = " << buf << endl;
280     
281     // shift save buffer
282     for ( i = result; i < save_len; ++i ) {
283         save_buf[ i - result ] = save_buf[i];
284     }
285     save_len -= result;
286
287     return result;
288 }
289
290
291 // write data to socket (server)
292 int SGSocket::write( char *buf, int length ) {
293
294     // check for any new client connection requests
295     fd_set ready;
296     FD_ZERO(&ready);
297     FD_SET(sock, &ready);
298     struct timeval tv;
299     tv.tv_sec = 0;
300     tv.tv_usec = 0;
301
302     // test for any input on sock (returning immediately, even if
303     // nothing)
304     select(32, &ready, 0, 0, &tv);
305
306     // any new connections?
307     if ( FD_ISSET(sock, &ready) ) {
308         int msgsock = accept(sock, 0, 0);
309         if ( msgsock < 0 ) {
310             FG_LOG( FG_IO, FG_ALERT, 
311                     "Error: accept() failed in write()" );
312             return 0;
313         } else {
314             client_connections.push_back( msgsock );
315         }
316     }
317
318     bool error_condition = false;
319     FG_LOG( FG_IO, FG_INFO, "Client connections = " << 
320             client_connections.size() );
321     for ( int i = 0; i < (int)client_connections.size(); ++i ) {
322         int msgsock = client_connections[i];
323
324         // read and junk any possible incoming messages.
325         // char junk[ SG_IO_MAX_MSG_SIZE ];
326         // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE );
327
328         // write the interesting data to the socket
329 #ifdef _MSC_VER
330         if ( _write(msgsock, buf, length) < 0 ) {
331 #else
332         if ( std::write(msgsock, buf, length) < 0 ) {
333 #endif
334             FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
335             error_condition = true;
336         } else {
337 #ifdef _POSIX_SYNCHRONIZED_IO
338             // fdatasync(msgsock);
339 #else
340             // fsync(msgsock);
341 #endif
342         }
343     }
344
345     if ( error_condition ) {
346         return 0;
347     }
348
349     return length;
350 }
351
352
353 // write null terminated string to socket (server)
354 int SGSocket::writestring( char *str ) {
355     int length = strlen( str );
356     return write( str, length );
357 }
358
359
360 // close the port
361 bool SGSocket::close() {
362     for ( int i = 0; i < (int)client_connections.size(); ++i ) {
363         int msgsock = client_connections[i];
364 #ifdef _MSC_VER
365         _close( msgsock );
366 #else
367         std::close( msgsock );
368 #endif
369     }
370
371 #ifdef _MSC_VER
372     _close( sock );
373 #else
374     std::close( sock );
375 #endif
376     return true;
377 }