1 // fg_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 <Include/compiler.h>
26 #include <sys/time.h> // select()
27 #include <sys/types.h> // socket(), bind(), select(), accept()
28 #include <sys/socket.h> // socket(), bind(), listen(), accept()
29 #include <netinet/in.h> // struct sockaddr_in
30 #include <netinet/tcp.h> // #define TCP_NODELAY, this attempts to
31 // disable the Nagle algorithm.
32 #include <netdb.h> // gethostbyname()
33 #include <unistd.h> // select(), fsync()/fdatasync()
37 #include <Debug/logstream.hxx>
39 #include "fg_socket.hxx"
44 FGSocket::FGSocket() :
50 FGSocket::~FGSocket() {
54 int FGSocket::make_server_socket () {
55 struct sockaddr_in name;
59 sock = socket (PF_INET, SOCK_STREAM, 0);
61 FG_LOG( FG_IO, FG_ALERT,
62 "Error: socket() failed in make_server_socket()" );
66 // Give the socket a name.
67 name.sin_family = AF_INET;
68 name.sin_addr.s_addr = INADDR_ANY;
69 name.sin_port = htons(port); // set port to zero to let system pick
70 name.sin_addr.s_addr = htonl (INADDR_ANY);
71 if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
72 FG_LOG( FG_IO, FG_ALERT,
73 "Error: bind() failed in make_server_socket()" );
77 // Find the assigned port number
78 length = sizeof(struct sockaddr_in);
79 if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
80 FG_LOG( FG_IO, FG_ALERT,
81 "Error: getsockname() failed in make_server_socket()" );
84 port = ntohs(name.sin_port);
90 int FGSocket::make_client_socket () {
91 struct sockaddr_in name;
95 sock = socket (PF_INET, SOCK_STREAM, 0);
97 FG_LOG( FG_IO, FG_ALERT,
98 "Error: socket() failed in make_client_socket()" );
102 // specify address family
103 name.sin_family = AF_INET;
105 // get the hosts official name/info
106 hp = gethostbyname( hostname.c_str() );
108 // Connect this socket to the host and the port specified on the
110 bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
111 name.sin_port = htons(port);
113 if ( connect(sock, (struct sockaddr *) &name,
114 sizeof(struct sockaddr_in)) < 0 )
117 FG_LOG( FG_IO, FG_ALERT,
118 "Error: connect() failed in make_client_socket()" );
126 // If specified as a server (out direction for now) open the master
127 // listening socket. If specified as a client, open a connection to a
130 bool FGSocket::open( FGProtocol::fgProtocolDir dir ) {
131 if ( port_str == "" || port_str == "any" ) {
134 port = atoi( port_str.c_str() );
137 client_connections.clear();
139 if ( dir == FGProtocol::out ) {
140 // this means server for now
142 // Setup socket to listen on. Set "port" before making this
143 // call. A port of "0" indicates that we want to let the os
144 // pick any available port.
145 sock = make_server_socket();
146 cout << "socket is connected to port = " << port << endl;
148 // Specify the maximum length of the connection queue
149 listen(sock, FG_MAX_SOCKET_QUEUE);
151 } else if ( dir == FGProtocol::in ) {
152 // this means client for now
154 sock = make_client_socket();
156 FG_LOG( FG_IO, FG_ALERT,
157 "Error: bidirection mode not available yet for sockets." );
162 FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname
171 // read data from socket (client)
172 // read a block of data of specified size
173 int FGSocket::read( char *buf, int length ) {
176 // check for potential input
179 FD_SET(sock, &ready);
184 // test for any input read on sock (returning immediately, even if
186 select(32, &ready, 0, 0, &tv);
188 if ( FD_ISSET(sock, &ready) ) {
189 result = std::read( sock, buf, length );
190 if ( result != length ) {
191 FG_LOG( FG_IO, FG_INFO,
192 "Warning: read() not enough bytes." );
200 // read a line of data, length is max size of input buffer
201 int FGSocket::readline( char *buf, int length ) {
204 // check for potential input
207 FD_SET(sock, &ready);
212 // test for any input read on sock (returning immediately, even if
214 select(32, &ready, 0, 0, &tv);
216 if ( FD_ISSET(sock, &ready) ) {
217 // read a chunk, keep in the save buffer until we have the
218 // requested amount read
220 char *buf_ptr = save_buf + save_len;
221 result = std::read( sock, buf_ptr, FG_MAX_MSG_SIZE - save_len );
223 cout << "current read = " << buf_ptr << endl;
224 cout << "current save_buf = " << save_buf << endl;
225 cout << "save_len = " << save_len << endl;
228 // look for the end of line in save_buf
230 for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
231 if ( save_buf[i] == '\n' ) {
234 // no end of line yet
235 cout << "no eol found" << endl;
238 cout << "line length = " << result << endl;
240 // we found an end of line
242 // copy to external buffer
243 strncpy( buf, save_buf, result );
245 cout << "fg_socket line = " << buf << endl;
248 for ( i = result; i < save_len; ++i ) {
249 save_buf[ i - result ] = save_buf[i];
257 // write data to socket (server)
258 int FGSocket::write( char *buf, int length ) {
260 // check for any new client connection requests
263 FD_SET(sock, &ready);
268 // test for any input on sock (returning immediately, even if
270 select(32, &ready, 0, 0, &tv);
272 // any new connections?
273 if ( FD_ISSET(sock, &ready) ) {
274 int msgsock = accept(sock, 0, 0);
276 FG_LOG( FG_IO, FG_ALERT,
277 "Error: accept() failed in write()" );
280 client_connections.push_back( msgsock );
284 bool error_condition = false;
285 FG_LOG( FG_IO, FG_INFO, "Client connections = " <<
286 client_connections.size() );
287 for ( int i = 0; i < (int)client_connections.size(); ++i ) {
288 int msgsock = client_connections[i];
290 // read and junk any possible incoming messages.
291 // char junk[ FG_MAX_MSG_SIZE ];
292 // std::read( msgsock, junk, FG_MAX_MSG_SIZE );
294 // write the interesting data to the socket
295 if ( std::write(msgsock, buf, length) < 0 ) {
296 FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
297 error_condition = true;
299 #ifdef _POSIX_SYNCHRONIZED_IO
300 // fdatasync(msgsock);
307 if ( error_condition ) {
316 bool FGSocket::close() {
317 for ( int i = 0; i < (int)client_connections.size(); ++i ) {
318 int msgsock = client_connections[i];
319 std::close( msgsock );