ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/lowlevel.C
Revision: 1.22
Committed: Fri Dec 15 19:59:20 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.21: +9 -10 lines
Log Message:
the rename for sanity campaign hits
you died
- renamed stuff
- partially updated copyrights
- some cleanups

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