ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/lowlevel.C
(Generate patch)

Comparing deliantra/server/socket/lowlevel.C (file contents):
Revision 1.42 by root, Mon May 28 21:22:26 2007 UTC vs.
Revision 1.43 by root, Sat Jun 9 22:54:04 2007 UTC

44# include <sys/socket.h> 44# include <sys/socket.h>
45# include <netinet/in.h> 45# include <netinet/in.h>
46# include <netinet/tcp.h> 46# include <netinet/tcp.h>
47#endif 47#endif
48 48
49// use a really low timeout, as it doesn't cost any bandwidth, and you can 49// disconnect a socket after this many seconds without an ack
50// easily die in 20 seconds...
51#define SOCKET_TIMEOUT1 10 * 1000 50#define SOCKET_TIMEOUT 8.
52#define SOCKET_TIMEOUT2 20 * 1000 51
52// force a packet when idle for more than this many seconds,
53// forcing an ack regularly.
54#define IDLE_PING 2.
53 55
54void 56void
55client::flush () 57client::flush ()
56{ 58{
57 if (destroyed ()) 59 if (destroyed ())
58 return; 60 return;
59 61
60#ifdef __linux__ 62#ifdef __linux__
63 // check about once per second, spread evenly over all clients
64 if (!((pticks + fd) & 7))
65 {
61 // check time of last ack, and, if too old, kill connection 66 // check time of last ack, and, if too old, kill connection
62 struct tcp_info tcpi; 67 struct tcp_info tcpi;
63 socklen_t len = sizeof (tcpi); 68 socklen_t len = sizeof (tcpi);
64 69
65 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi)) 70 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
66 { 71 {
67 unsigned int diff = tcpi.tcpi_last_ack_recv - tcpi.tcpi_last_data_sent;
68
69 rtt = tcpi.tcpi_rtt; 72 rtt = tcpi.tcpi_rtt;
70 rttvar = tcpi.tcpi_rttvar; 73 rttvar = tcpi.tcpi_rttvar;
71 74
72 if (tcpi.tcpi_unacked && SOCKET_TIMEOUT1 < diff && diff < 0x80000000UL // ack delayed for 20s 75 if (tcpi.tcpi_last_ack_recv > int (SOCKET_TIMEOUT * 1000))
73 && SOCKET_TIMEOUT2 < tcpi.tcpi_last_data_sent) // no data sent for 10s
74 { 76 {
77 send_msg (NDI_RED, "connection-timeout", "safety disconnect due to tcp/ip timeout (no packets received)");
78 write_outputbuffer ();
79
75 LOG (llevDebug, "Connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd, 80 LOG (llevDebug, "connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd,
76 (unsigned) tcpi.tcpi_last_ack_recv, (unsigned) tcpi.tcpi_last_data_sent, (unsigned) tcpi.tcpi_unacked); 81 (unsigned)tcpi.tcpi_last_ack_recv, (unsigned)tcpi.tcpi_last_data_sent, (unsigned)tcpi.tcpi_unacked);
77 destroy (); 82 destroy ();
83 }
78 } 84 }
79 } 85 }
80#endif 86#endif
81 87
82 /** 88 /**
84 * 90 *
85 * When the socket is clear to write, and we have backlogged data, this 91 * When the socket is clear to write, and we have backlogged data, this
86 * is called to write it out. 92 * is called to write it out.
87 */ 93 */
88 94
89 if (!outputbuffer.len || socket_ev.poll () & PE_W) 95 // write a nop to the socket at least every IDLE_NOP seconds.
96 if (!outputbuffer.len)
97 {
98 if (last_send + IDLE_PING <= NOW)
99 {
100 // this is a bit ugly, but map1/map1a seem to be the only
101 // nop'able commands and they are quite small.
102 packet sl (mapmode == Map1Cmd ? "map1" : "map1a");
103 send_packet (sl);
104 }
105 else
106 return;
107 }
108
109 if (socket_ev.poll () & PE_W)
90 return; 110 return;
91 111
112 last_send = NOW;
92 write_outputbuffer (); 113 write_outputbuffer ();
93} 114}
94 115
95void 116void
96client::write_outputbuffer () 117client::write_outputbuffer ()
106 /* wrap back to start of buffer */ 127 /* wrap back to start of buffer */
107 if (outputbuffer.start == SOCKETBUFSIZE) 128 if (outputbuffer.start == SOCKETBUFSIZE)
108 outputbuffer.start = 0; 129 outputbuffer.start = 0;
109 130
110 outputbuffer.len -= res; 131 outputbuffer.len -= res;
111#ifdef CS_LOGSTATS
112 cst_tot.obytes += res;
113 cst_lst.obytes += res;
114#endif
115 } 132 }
116 else if (res == 0) 133 else if (res == 0)
117 { 134 {
118 LOG (llevError, "socket write failed, connection closed.\n"); 135 LOG (llevError, "socket write failed, connection closed.\n");
119 destroy (); 136 destroy ();
376 } 393 }
377 else 394 else
378 { 395 {
379 inbuf_len += amount; 396 inbuf_len += amount;
380 397
381 cst_tot.ibytes += amount;
382 cst_lst.ibytes += amount;
383
384 cmd_ev.start (); 398 cmd_ev.start ();
385 } 399 }
386 } 400 }
387} 401}
388 402
606 return reset (); 620 return reset ();
607 621
608 cur += len; 622 cur += len;
609} 623}
610 624
611/******************************************************************************
612 *
613 * statistics logging functions.
614 *
615 ******************************************************************************/
616
617#ifdef CS_LOGSTATS
618
619/* cst_tot is for the life of the server, cst_last is for the last series of
620 * stats
621 */
622CS_Stats cst_tot, cst_lst;
623
624/**
625 * Writes out the gathered stats. We clear cst_lst.
626 */
627void
628write_cs_stats (void)
629{
630 time_t now = time (NULL);
631
632 /* If no connections recently, don't both to log anything */
633 if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
634 return;
635
636 /* CSSTAT is put in so scripts can easily find the line */
637 LOG (llevInfo, "CSSTAT: %.16s tot %d %d %d %d inc %d %d %d %d\n",
638 ctime (&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn,
639 now - cst_tot.time_start, cst_lst.ibytes, cst_lst.obytes, cst_lst.max_conn, now - cst_lst.time_start);
640 cst_lst.ibytes = 0;
641 cst_lst.obytes = 0;
642 cst_lst.time_start = now;
643}
644#endif
645

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines