ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/lowlevel.C
Revision: 1.21
Committed: Fri Dec 15 00:14:13 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.20: +3 -0 lines
Log Message:
- make client_socket accessible to perl
- on linux, record rtt and rttvar
- who shows rtt/rttvar

File Contents

# Content
1 /*
2 CrossFire, A Multiplayer game for X-windows
3
4 Copyright (C) 1992 Frank Tore Johansen
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 The author can be reached via e-mail to mark@pyramid.com
21 */
22
23 /**
24 * \file
25 * Low-level socket-related functions.
26 *
27 * \date 2003-12-02
28 *
29 * Contains some base functions that both the client and server
30 * can use. As such, depending what we are being compiled for will
31 * determine what we can include. the client is designed have
32 * CFCLIENT defined as part of its compile flags.
33 */
34
35 using namespace std;
36
37 #include <global.h>
38 #include <newclient.h>
39 #include <sproto.h>
40 #include <cstdarg>
41
42 #ifdef __linux__
43 # include <sys/types.h>
44 # include <sys/socket.h>
45 # include <netinet/in.h>
46 # define TCP_HZ 1000 // sorry...
47 # include <netinet/tcp.h>
48 #endif
49
50 // use a really low timeout, as it doesn't cost any bandwidth, and you can
51 // easily die in 20 seconds...
52 #define SOCKET_TIMEOUT1 10
53 #define SOCKET_TIMEOUT2 20
54
55 void
56 client_socket::flush ()
57 {
58 #ifdef __linux__
59 // check time of last ack, and, if too old, kill connection
60 struct tcp_info tcpi;
61 socklen_t len = sizeof (tcpi);
62
63 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
64 {
65 unsigned int diff = tcpi.tcpi_last_ack_recv - tcpi.tcpi_last_data_sent;
66
67 rtt = tcpi.tcpi_rtt;
68 rttvar = tcpi.tcpi_rttvar;
69
70 if (tcpi.tcpi_unacked && SOCKET_TIMEOUT1 * TCP_HZ < diff && diff < 0x80000000UL // ack delayed for 20s
71 && SOCKET_TIMEOUT2 * TCP_HZ < tcpi.tcpi_last_data_sent) // no data sent for 10s
72 {
73 LOG (llevDebug, "Connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd,
74 (unsigned) tcpi.tcpi_last_ack_recv, (unsigned) tcpi.tcpi_last_data_sent, (unsigned) tcpi.tcpi_unacked);
75 status = Ns_Dead;
76 }
77 }
78 #endif
79
80 /**
81 * Writes data to socket.
82 *
83 * When the socket is clear to write, and we have backlogged data, this
84 * is called to write it out.
85 */
86
87 if (!outputbuffer.len || socket_ev.poll () & PE_W)
88 return;
89
90 write_outputbuffer ();
91 }
92
93 void
94 client_socket::write_outputbuffer ()
95 {
96 while (outputbuffer.len)
97 {
98 int res = write (fd, outputbuffer.data + outputbuffer.start,
99 min (outputbuffer.len, SOCKETBUFSIZE - outputbuffer.start));
100
101 if (res > 0)
102 {
103 outputbuffer.start += res;
104 /* wrap back to start of buffer */
105 if (outputbuffer.start == SOCKETBUFSIZE)
106 outputbuffer.start = 0;
107
108 outputbuffer.len -= res;
109 #ifdef CS_LOGSTATS
110 cst_tot.obytes += res;
111 cst_lst.obytes += res;
112 #endif
113 }
114 else if (res == 0)
115 {
116 LOG (llevError, "socket write failed, connection closed.\n");
117 status = Ns_Dead;
118 return;
119 }
120 else if (errno == EINTR)
121 {
122 // just retry
123 }
124 else if (errno == EAGAIN)
125 {
126 // delay till ready
127 socket_ev.poll (socket_ev.poll () | PE_W);
128 socket_ev.start ();
129 return;
130 }
131 else
132 {
133 LOG (llevError, "socket write failed: %s\n", strerror (errno));
134 status = Ns_Dead;
135 return;
136 }
137 }
138
139 socket_ev.poll (socket_ev.poll () & ~PE_W);
140 }
141
142 /***********************************************************************
143 *
144 * packet functions/utilities
145 *
146 **********************************************************************/
147
148 packet &packet::operator <<(const data &v)
149 {
150 if (room () < v.len)
151 reset ();
152 else
153 {
154 if (v.len)
155 {
156 memcpy (cur, v.ptr, v.len);
157 cur += v.len;
158 }
159 }
160
161 return *this;
162 }
163
164 packet &packet::operator <<(const data8 &v)
165 {
166 unsigned int len = min (v.len, 0x00FF);
167 return *this << uint8 (len) << data (v.ptr, len);
168 }
169
170 packet &packet::operator <<(const data16 &v)
171 {
172 unsigned int len = min (v.len, 0xFFFF);
173 return *this << uint16 (len) << data (v.ptr, len);
174 }
175
176 packet &packet::operator <<(const char *v)
177 {
178 return *this << data (v, strlen (v ? v : 0));
179 }
180
181 void
182 packet::printf (const char *format, ...)
183 {
184 int size = room ();
185
186 va_list ap;
187 va_start (ap, format);
188 int len = vsnprintf ((char *)cur, size, format, ap);
189 va_end (ap);
190
191 if (len >= size)
192 return reset ();
193
194 cur += len;
195 }
196
197 /******************************************************************************
198 *
199 * Start of read routines.
200 *
201 ******************************************************************************/
202
203 int
204 client_socket::read_packet ()
205 {
206 for (;;)
207 {
208 if (inbuf_len >= 2)
209 {
210 unsigned int pkt_len = (inbuf [0] << 8) | inbuf [1];
211
212 if (inbuf_len >= 2 + pkt_len)
213 return pkt_len + 2;
214 }
215
216 int amount = sizeof (inbuf) - inbuf_len;
217
218 if (amount <= 0)
219 {
220 LOG (llevError, "packet too large");//TODO
221 return -1;
222 }
223
224 amount = read (fd, inbuf + inbuf_len, amount);
225
226 if (!amount)
227 {
228 status = Ns_Dead;
229 return -1;
230 }
231 else if (amount < 0)
232 {
233 if (errno != EAGAIN && errno != EINTR)
234 {
235 LOG (llevError, "read error: %s\n", strerror (errno));
236 return -1;
237 }
238
239 return 0;
240 }
241
242 inbuf_len += amount;
243
244 cst_tot.ibytes += amount;
245 cst_lst.ibytes += amount;
246 }
247 }
248
249 void
250 client_socket::skip_packet (int len)
251 {
252 inbuf_len -= len;
253 memmove (inbuf, inbuf + len, inbuf_len);
254 }
255
256 /*******************************************************************************
257 *
258 * Start of write related routines.
259 *
260 ******************************************************************************/
261
262 /**
263 * Adds data to a socket buffer for whatever reason.
264 *
265 * ns is the socket we are adding the data to, buf is the start of the
266 * data, and len is the number of bytes to add.
267 */
268 void
269 client_socket::send (void *buf_, int len)
270 {
271 char *buf = (char *)buf_;
272 char *pos = buf;
273 int amt = 0;
274
275 if (status == Ns_Dead || !buf)
276 {
277 LOG (llevDebug, "Write_To_Socket called with dead socket\n");
278 return;
279 }
280
281 if ((len + outputbuffer.len) > SOCKETBUFSIZE)
282 {
283 LOG (llevDebug, "Socket on fd %d has overrun internal buffer - marking as dead\n", fd);
284 status = Ns_Dead;
285 return;
286 }
287
288 int avail, end;
289
290 /* data + end is where we start putting the new data. The last byte
291 * currently in use is actually data + end -1
292 */
293 end = outputbuffer.start + outputbuffer.len;
294 /* The buffer is already in a wrapped state, so adjust end */
295 if (end >= SOCKETBUFSIZE)
296 end -= SOCKETBUFSIZE;
297
298 avail = SOCKETBUFSIZE - end;
299
300 /* We can all fit it behind the current data without wrapping */
301 if (avail >= len)
302 memcpy (outputbuffer.data + end, buf, len);
303 else
304 {
305 memcpy (outputbuffer.data + end, buf, avail);
306 memcpy (outputbuffer.data, buf + avail, len - avail);
307 }
308
309 outputbuffer.len += len;
310 }
311
312 void
313 client_socket::socket_cb (iow &w, int got)
314 {
315 write_outputbuffer ();
316
317 if (!outputbuffer.len)
318 socket_ev.poll (socket_ev.poll () & ~PE_W);
319 }
320
321 /**
322 * Takes a string of data, and writes it out to the socket. A very handy
323 * shortcut function.
324 */
325 void
326 client_socket::send_packet (packet &sl)
327 {
328 if (status == Ns_Dead)
329 return;
330
331 if (sl.length () >= MAXSOCKBUF)
332 {
333 LOG (llevError, "Trying to send a buffer beyond properly size, len =%d\n", sl.length ());
334 /* Almost certainly we've overflowed a buffer, so quit now to make
335 * it easier to debug.
336 */
337 abort ();
338 }
339
340 if (!sl.length ())
341 return;
342
343 assert (sl.hdrlen == 2);
344
345 sl.buf_ [0] = sl.length () >> 8;
346 sl.buf_ [1] = sl.length () ;
347
348 send (sl.buf_, sl.length () + sl.hdrlen);
349 }
350
351 void
352 client_socket::send_packet (const char *buf, int len)
353 {
354 packet sl;
355
356 sl << data (buf, len);
357 send_packet (sl);
358 }
359
360 void
361 client_socket::send_packet (const char *buf)
362 {
363 send_packet (buf, strlen (buf));
364 }
365
366 /******************************************************************************
367 *
368 * statistics logging functions.
369 *
370 ******************************************************************************/
371
372 #ifdef CS_LOGSTATS
373
374 /* cst_tot is for the life of the server, cst_last is for the last series of
375 * stats
376 */
377 CS_Stats cst_tot, cst_lst;
378
379 /**
380 * Writes out the gathered stats. We clear cst_lst.
381 */
382 void
383 write_cs_stats (void)
384 {
385 time_t now = time (NULL);
386
387 /* If no connections recently, don't both to log anything */
388 if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
389 return;
390
391 /* CSSTAT is put in so scripts can easily find the line */
392 LOG (llevInfo, "CSSTAT: %.16s tot %d %d %d %d inc %d %d %d %d\n",
393 ctime (&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn,
394 now - cst_tot.time_start, cst_lst.ibytes, cst_lst.obytes, cst_lst.max_conn, now - cst_lst.time_start);
395 cst_lst.ibytes = 0;
396 cst_lst.obytes = 0;
397 cst_lst.time_start = now;
398 }
399 #endif
400