ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/lowlevel.C
Revision: 1.20
Committed: Thu Dec 14 21:46:34 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.19: +89 -153 lines
Log Message:
- write() is now independent of server ticks and will be done immediately
- get rid of CORK hack, it is no longer necessary

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