]> git.mxchange.org Git - flightgear.git/blob - src/Network/fg_socket.cxx
Removed some testing cruft related to a dumb attempt to disable the nagle
[flightgear.git] / src / Network / fg_socket.cxx
1 // fg_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 <Include/compiler.h>
25
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()
34
35 #include STL_STRING
36
37 #include <Debug/logstream.hxx>
38
39 #include "fg_socket.hxx"
40
41 FG_USING_STD(string);
42
43
44 FGSocket::FGSocket() :
45     save_len(0)
46 {
47 }
48
49
50 FGSocket::~FGSocket() {
51 }
52
53
54 int FGSocket::make_server_socket () {
55     struct sockaddr_in name;
56     socklen_t length;
57      
58     // Create the socket.
59     sock = socket (PF_INET, SOCK_STREAM, 0);
60     if (sock < 0) {
61         FG_LOG( FG_IO, FG_ALERT, 
62                 "Error: socket() failed in make_server_socket()" );
63         return -1;
64     }
65      
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()" );
74         return -1;
75     }
76      
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()" );
82         return -1;
83     }
84     port = ntohs(name.sin_port);
85
86     return sock;
87 }
88
89
90 int FGSocket::make_client_socket () {
91     struct sockaddr_in name;
92     struct hostent *hp;
93      
94     // Create the socket.
95     sock = socket (PF_INET, SOCK_STREAM, 0);
96     if (sock < 0) {
97         FG_LOG( FG_IO, FG_ALERT, 
98                 "Error: socket() failed in make_client_socket()" );
99         return -1;
100     }
101      
102     // specify address family
103     name.sin_family = AF_INET;
104
105     // get the hosts official name/info
106     hp = gethostbyname( hostname.c_str() );
107
108     // Connect this socket to the host and the port specified on the
109     // command line
110     bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
111     name.sin_port = htons(port);
112
113     if ( connect(sock, (struct sockaddr *) &name, 
114                  sizeof(struct sockaddr_in)) < 0 )
115     {
116         std::close(sock);
117         FG_LOG( FG_IO, FG_ALERT, 
118                 "Error: connect() failed in make_client_socket()" );
119         return -1;
120     }
121
122     return sock;
123 }
124
125
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
128 // server.
129
130 bool FGSocket::open( FGProtocol::fgProtocolDir dir ) {
131     if ( port_str == "" || port_str == "any" ) {
132         port = 0; 
133     } else {
134         port = atoi( port_str.c_str() );
135     }
136     
137     client_connections.clear();
138
139     if ( dir == FGProtocol::out ) {
140         // this means server for now
141
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;
147
148         // Specify the maximum length of the connection queue
149         listen(sock, FG_MAX_SOCKET_QUEUE);
150
151     } else if ( dir == FGProtocol::in ) {
152         // this means client for now
153
154         sock = make_client_socket();
155     } else {
156         FG_LOG( FG_IO, FG_ALERT, 
157                 "Error:  bidirection mode not available yet for sockets." );
158         return false;
159     }
160
161     if ( sock < 0 ) {
162         FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname
163                 << ":" << port );
164         return false;
165     }
166
167     return true;
168 }
169
170
171 // read data from socket (client)
172 // read a block of data of specified size
173 int FGSocket::read( char *buf, int length ) {
174     int result = 0;
175
176     // check for potential input
177     fd_set ready;
178     FD_ZERO(&ready);
179     FD_SET(sock, &ready);
180     struct timeval tv;
181     tv.tv_sec = 0;
182     tv.tv_usec = 0;
183
184     // test for any input read on sock (returning immediately, even if
185     // nothing)
186     select(32, &ready, 0, 0, &tv);
187
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." );
193         }
194     }
195
196     return result;
197 }
198
199
200 // read a line of data, length is max size of input buffer
201 int FGSocket::readline( char *buf, int length ) {
202     int result = 0;
203
204     // check for potential input
205     fd_set ready;
206     FD_ZERO(&ready);
207     FD_SET(sock, &ready);
208     struct timeval tv;
209     tv.tv_sec = 0;
210     tv.tv_usec = 0;
211
212     // test for any input read on sock (returning immediately, even if
213     // nothing)
214     select(32, &ready, 0, 0, &tv);
215
216     if ( FD_ISSET(sock, &ready) ) {
217         // read a chunk, keep in the save buffer until we have the
218         // requested amount read
219
220         char *buf_ptr = save_buf + save_len;
221         result = std::read( sock, buf_ptr, FG_MAX_MSG_SIZE - save_len );
222         save_len += result;
223         cout << "current read = " << buf_ptr << endl;
224         cout << "current save_buf = " << save_buf << endl;
225         cout << "save_len = " << save_len << endl;
226     }
227
228     // look for the end of line in save_buf
229     int i;
230     for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
231     if ( save_buf[i] == '\n' ) {
232         result = i + 1;
233     } else {
234         // no end of line yet
235         cout << "no eol found" << endl;
236         return 0;
237     }
238     cout << "line length = " << result << endl;
239
240     // we found an end of line
241
242     // copy to external buffer
243     strncpy( buf, save_buf, result );
244     buf[result] = '\0';
245     cout << "fg_socket line = " << buf << endl;
246     
247     // shift save buffer
248     for ( i = result; i < save_len; ++i ) {
249         save_buf[ i - result ] = save_buf[i];
250     }
251     save_len -= result;
252
253     return result;
254 }
255
256
257 // write data to socket (server)
258 int FGSocket::write( char *buf, int length ) {
259
260     // check for any new client connection requests
261     fd_set ready;
262     FD_ZERO(&ready);
263     FD_SET(sock, &ready);
264     struct timeval tv;
265     tv.tv_sec = 0;
266     tv.tv_usec = 0;
267
268     // test for any input on sock (returning immediately, even if
269     // nothing)
270     select(32, &ready, 0, 0, &tv);
271
272     // any new connections?
273     if ( FD_ISSET(sock, &ready) ) {
274         int msgsock = accept(sock, 0, 0);
275         if ( msgsock < 0 ) {
276             FG_LOG( FG_IO, FG_ALERT, 
277                     "Error: accept() failed in write()" );
278             return 0;
279         } else {
280             client_connections.push_back( msgsock );
281         }
282     }
283
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];
289
290         // read and junk any possible incoming messages.
291         // char junk[ FG_MAX_MSG_SIZE ];
292         // std::read( msgsock, junk, FG_MAX_MSG_SIZE );
293
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;
298         } else {
299 #ifdef _POSIX_SYNCHRONIZED_IO
300             // fdatasync(msgsock);
301 #else
302             // fsync(msgsock);
303 #endif
304         }
305     }
306
307     if ( error_condition ) {
308         return 0;
309     }
310
311     return length;
312 }
313
314
315 // close the port
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 );
320     }
321
322     std::close( sock );
323
324     return true;
325 }