1 // sg_socket.cxx -- Socket I/O routines
3 // Written by Curtis Olson, started November 1999.
5 // Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org
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.
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.
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.
24 #include <simgear/compiler.h>
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(), fcntl()
33 # include <fcntl.h> // fcntl()
35 # include <sys/timeb.h> // select()
36 # include <winsock2.h> // socket(), bind(), listen(), accept(),
37 // struct sockaddr_in, gethostbyname()
48 #include <simgear/debug/logstream.hxx>
50 #include "sg_socket.hxx"
55 SGSocket::SGSocket( const string& host, const string& port,
56 const string& style ) :
62 if ( style == "udp" ) {
63 sock_style = SOCK_DGRAM;
64 } else if ( style == "tcp" ) {
65 sock_style = SOCK_STREAM;
67 sock_style = SOCK_DGRAM;
68 FG_LOG( FG_IO, FG_ALERT,
69 "Error: SGSocket() unknown style = " << style );
72 set_type( sgSocketType );
76 SGSocket::~SGSocket() {
80 int SGSocket::make_server_socket () {
81 struct sockaddr_in name;
83 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER )
90 sock = socket (PF_INET, sock_style, 0);
92 FG_LOG( FG_IO, FG_ALERT,
93 "Error: socket() failed in make_server_socket()" );
97 // Give the socket a name.
98 name.sin_family = AF_INET;
99 name.sin_addr.s_addr = INADDR_ANY;
100 name.sin_port = htons(port); // set port to zero to let system pick
101 name.sin_addr.s_addr = htonl (INADDR_ANY);
102 if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
103 FG_LOG( FG_IO, FG_ALERT,
104 "Error: bind() failed in make_server_socket()" );
108 // Find the assigned port number
109 length = sizeof(struct sockaddr_in);
110 if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
111 FG_LOG( FG_IO, FG_ALERT,
112 "Error: getsockname() failed in make_server_socket()" );
115 port = ntohs(name.sin_port);
121 int SGSocket::make_client_socket () {
122 struct sockaddr_in name;
125 FG_LOG( FG_IO, FG_INFO, "Make client socket()" );
127 // Create the socket.
128 sock = socket (PF_INET, sock_style, 0);
130 FG_LOG( FG_IO, FG_ALERT,
131 "Error: socket() failed in make_client_socket()" );
135 // specify address family
136 name.sin_family = AF_INET;
138 // get the hosts official name/info
139 hp = gethostbyname( hostname.c_str() );
141 // Connect this socket to the host and the port specified on the
143 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ )
144 bcopy(hp->h_addr, (char *)(&(name.sin_addr.s_addr)), hp->h_length);
146 bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
148 name.sin_port = htons(port);
150 if ( connect(sock, (struct sockaddr *) &name,
151 sizeof(struct sockaddr_in)) < 0 )
158 FG_LOG( FG_IO, FG_ALERT,
159 "Error: connect() failed in make_client_socket()" );
167 // If specified as a server (in direction for now) open the master
168 // listening socket. If specified as a client (out direction), open a
169 // connection to a server.
170 bool SGSocket::open( SGProtocolDir dir ) {
171 if ( port_str == "" || port_str == "any" ) {
174 port = atoi( port_str.c_str() );
177 // client_connections.clear();
179 if ( dir == SG_IO_IN ) {
180 // this means server for now
182 // Setup socket to listen on. Set "port" before making this
183 // call. A port of "0" indicates that we want to let the os
184 // pick any available port.
185 sock = make_server_socket();
186 FG_LOG( FG_IO, FG_INFO, "socket is connected to port = " << port );
188 if ( sock_style == SOCK_DGRAM ) {
190 fcntl( sock, F_SETFL, O_NONBLOCK );
193 // Specify the maximum length of the connection queue
194 listen( sock, SG_MAX_SOCKET_QUEUE );
197 } else if ( dir == SG_IO_OUT ) {
198 // this means client for now
200 sock = make_client_socket();
202 if ( sock_style == SOCK_DGRAM ) {
204 fcntl( sock, F_SETFL, O_NONBLOCK );
207 FG_LOG( FG_IO, FG_ALERT,
208 "Error: bidirection mode not available yet for sockets." );
213 FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname
222 // read data from socket (server)
223 // read a block of data of specified size
224 int SGSocket::read( char *buf, int length ) {
227 // check for potential input
230 FD_SET(sock, &ready);
235 // test for any input available on sock (returning immediately, even if
237 select(32, &ready, 0, 0, &tv);
239 if ( FD_ISSET(sock, &ready) ) {
241 result = _read( sock, buf, length );
243 result = std::read( sock, buf, length );
245 if ( result != length ) {
246 FG_LOG( FG_IO, FG_INFO,
247 "Warning: read() not enough bytes." );
255 // read a line of data, length is max size of input buffer
256 int SGSocket::readline( char *buf, int length ) {
259 // check for potential input
262 FD_SET(sock, &ready);
267 // test for any input read on sock (returning immediately, even if
269 select(32, &ready, 0, 0, &tv);
271 if ( FD_ISSET(sock, &ready) ) {
272 // read a chunk, keep in the save buffer until we have the
273 // requested amount read
275 char *buf_ptr = save_buf + save_len;
277 result = _read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
279 result = std::read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
282 // cout << "current read = " << buf_ptr << endl;
283 // cout << "current save_buf = " << save_buf << endl;
284 // cout << "save_len = " << save_len << endl;
287 // look for the end of line in save_buf
289 for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
290 if ( save_buf[i] == '\n' ) {
293 // no end of line yet
294 // cout << "no eol found" << endl;
297 // cout << "line length = " << result << endl;
299 // we found an end of line
301 // copy to external buffer
302 strncpy( buf, save_buf, result );
304 // cout << "sg_socket line = " << buf << endl;
307 for ( i = result; i < save_len; ++i ) {
308 save_buf[ i - result ] = save_buf[i];
316 // write data to socket (client)
317 int SGSocket::write( char *buf, int length ) {
318 bool error_condition = false;
321 if ( _write(sock, buf, length) < 0 ) {
323 if ( std::write(sock, buf, length) < 0 ) {
325 FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
326 error_condition = true;
330 // check for any new client connection requests
333 FD_SET(sock, &ready);
338 // test for any input on sock (returning immediately, even if
340 select(32, &ready, 0, 0, &tv);
342 // any new connections?
343 if ( FD_ISSET(sock, &ready) ) {
344 int msgsock = accept(sock, 0, 0);
346 FG_LOG( FG_IO, FG_ALERT,
347 "Error: accept() failed in write()" );
350 client_connections.push_back( msgsock );
354 FG_LOG( FG_IO, FG_INFO, "Client connections = " <<
355 client_connections.size() );
356 for ( int i = 0; i < (int)client_connections.size(); ++i ) {
357 int msgsock = client_connections[i];
359 // read and junk any possible incoming messages.
360 // char junk[ SG_IO_MAX_MSG_SIZE ];
361 // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE );
363 // write the interesting data to the socket
365 if ( _write(msgsock, buf, length) < 0 ) {
367 if ( std::write(msgsock, buf, length) < 0 ) {
369 FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
370 error_condition = true;
372 #ifdef _POSIX_SYNCHRONIZED_IO
373 // fdatasync(msgsock);
381 if ( error_condition ) {
389 // write null terminated string to socket (server)
390 int SGSocket::writestring( char *str ) {
391 int length = strlen( str );
392 return write( str, length );
397 bool SGSocket::close() {
399 for ( int i = 0; i < (int)client_connections.size(); ++i ) {
400 int msgsock = client_connections[i];
404 std::close( msgsock );