📄 pseudotcp.cc
字号:
/* * libjingle * Copyright 2004--2005, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "talk/base/basicdefs.h"#include "talk/base/basictypes.h"#include "talk/base/byteorder.h"#include "talk/base/common.h"#include "talk/base/logging.h"#include "talk/base/socket.h"#include "talk/base/stringutils.h"#include "talk/base/time.h"#include "talk/p2p/base/pseudotcp.h"#ifdef POSIXextern "C" {#include <errno.h>}#endif // POSIX// The following logging is for detailed (packet-level) pseudotcp analysis only.#define _DBG_NONE 0#define _DBG_NORMAL 1#define _DBG_VERBOSE 2#define _DEBUGMSG _DBG_NONEnamespace cricket {//////////////////////////////////////////////////////////////////////// Network Constants//////////////////////////////////////////////////////////////////////// Standard MTUsconst uint16 PACKET_MAXIMUMS[] = { 65535, // Theoretical maximum, Hyperchannel 32000, // Nothing 17914, // 16Mb IBM Token Ring 8166, // IEEE 802.4 //4464, // IEEE 802.5 (4Mb max) 4352, // FDDI //2048, // Wideband Network 2002, // IEEE 802.5 (4Mb recommended) //1536, // Expermental Ethernet Networks //1500, // Ethernet, Point-to-Point (default) 1492, // IEEE 802.3 1006, // SLIP, ARPANET //576, // X.25 Networks //544, // DEC IP Portal //512, // NETBIOS 508, // IEEE 802/Source-Rt Bridge, ARCNET 296, // Point-to-Point (low delay) //68, // Official minimum 0, // End of list marker};const uint32 MAX_PACKET = 65535;// Note: we removed lowest level because packet overhead was larger!const uint32 MIN_PACKET = 296;const uint32 IP_HEADER_SIZE = 20; // (+ up to 40 bytes of options?)const uint32 ICMP_HEADER_SIZE = 8;const uint32 UDP_HEADER_SIZE = 8;// TODO: Make JINGLE_HEADER_SIZE transparent to this code?const uint32 JINGLE_HEADER_SIZE = 64; // when relay framing is in use//////////////////////////////////////////////////////////////////////// Global Constants and Functions////////////////////////////////////////////////////////////////////////// 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// 0 | Conversation Number |// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// 4 | Sequence Number |// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// 8 | Acknowledgment Number |// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// | | |U|A|P|R|S|F| |// 12 | Control | |R|C|S|S|Y|I| Window |// | | |G|K|H|T|N|N| |// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// 16 | Timestamp sending |// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// 20 | Timestamp receiving |// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// 24 | data |// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+////////////////////////////////////////////////////////////////////////#define PSEUDO_KEEPALIVE 0const uint32 MAX_SEQ = 0xFFFFFFFF;const uint32 HEADER_SIZE = 24;const uint32 PACKET_OVERHEAD = HEADER_SIZE + UDP_HEADER_SIZE + IP_HEADER_SIZE + JINGLE_HEADER_SIZE;const uint32 MIN_RTO = 250; // 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second")const uint32 DEF_RTO = 3000; // 3 seconds (RFC1122, Sec 4.2.3.1)const uint32 MAX_RTO = 60000; // 60 secondsconst uint32 ACK_DELAY = 100; // 100 millisecondsconst uint8 FLAG_CTL = 0x02;const uint8 FLAG_RST = 0x04;const uint8 CTL_CONNECT = 0;//const uint8 CTL_REDIRECT = 1;const uint8 CTL_EXTRA = 255;/*const uint8 FLAG_FIN = 0x01;const uint8 FLAG_SYN = 0x02;const uint8 FLAG_ACK = 0x10;*/const uint32 CTRL_BOUND = 0x80000000;const long DEFAULT_TIMEOUT = 4000; // If there are no pending clocks, wake up every 4 secondsconst long CLOSED_TIMEOUT = 60 * 1000; // If the connection is closed, once per minute#if PSEUDO_KEEPALIVE// !?! Rethink these timesconst uint32 IDLE_PING = 20 * 1000; // 20 seconds (note: WinXP SP2 firewall udp timeout is 90 seconds)const uint32 IDLE_TIMEOUT = 90 * 1000; // 90 seconds;#endif // PSEUDO_KEEPALIVE//////////////////////////////////////////////////////////////////////// Helper Functions//////////////////////////////////////////////////////////////////////inline void long_to_bytes(uint32 val, void* buf) { *static_cast<uint32*>(buf) = talk_base::HostToNetwork32(val);}inline void short_to_bytes(uint16 val, void* buf) { *static_cast<uint16*>(buf) = talk_base::HostToNetwork16(val);}inline uint32 bytes_to_long(const void* buf) { return talk_base::NetworkToHost32(*static_cast<const uint32*>(buf));}inline uint16 bytes_to_short(const void* buf) { return talk_base::NetworkToHost16(*static_cast<const uint16*>(buf));}uint32 bound(uint32 lower, uint32 middle, uint32 upper) { return talk_base::_min(talk_base::_max(lower, middle), upper);}//////////////////////////////////////////////////////////////////////// Debugging Statistics//////////////////////////////////////////////////////////////////////#if 0 // Not used yetenum Stat { S_SENT_PACKET, // All packet sends S_RESENT_PACKET, // All packet sends that are retransmits S_RECV_PACKET, // All packet receives S_RECV_NEW, // All packet receives that are too new S_RECV_OLD, // All packet receives that are too old S_NUM_STATS};const char* const STAT_NAMES[S_NUM_STATS] = { "snt", "snt-r", "rcv" "rcv-n", "rcv-o"};int g_stats[S_NUM_STATS];inline void Incr(Stat s) { ++g_stats[s]; }void ReportStats() { char buffer[256]; size_t len = 0; for (int i=0; i<S_NUM_STATS; ++i) { len += talk_base::sprintfn(buffer, ARRAY_SIZE(buffer), "%s%s:%d", (i == 0) ? "" : ",", STAT_NAMES[i], g_stats[i]); g_stats[i] = 0; } LOG(LS_INFO) << "Stats[" << buffer << "]";}#endif//////////////////////////////////////////////////////////////////////// PseudoTcp//////////////////////////////////////////////////////////////////////uint32 PseudoTcp::Now() {#if 0 // Use this to synchronize timers with logging timestamps (easier debug) return talk_base::ElapsedTime();#else return talk_base::Time();#endif}PseudoTcp::PseudoTcp(IPseudoTcpNotify * notify, uint32 conv) : m_notify(notify), m_shutdown(SD_NONE), m_error(0) { // Sanity check on buffer sizes (needed for OnTcpWriteable notification logic) ASSERT(sizeof(m_rbuf) + MIN_PACKET < sizeof(m_sbuf)); uint32 now = Now(); m_state = TCP_LISTEN; m_conv = conv; m_rcv_wnd = sizeof(m_rbuf); m_snd_nxt = m_slen = 0; m_snd_wnd = 1; m_snd_una = m_rcv_nxt = m_rlen = 0; m_bReadEnable = true; m_bWriteEnable = false; m_t_ack = 0; m_msslevel = 0; m_largest = 0; ASSERT(MIN_PACKET > PACKET_OVERHEAD); m_mss = MIN_PACKET - PACKET_OVERHEAD; m_mtu_advise = MAX_PACKET; m_rto_base = 0; m_cwnd = 2 * m_mss; m_ssthresh = sizeof(m_rbuf); m_lastrecv = m_lastsend = m_lasttraffic = now; m_bOutgoing = false; m_dup_acks = 0; m_recover = 0; m_ts_recent = m_ts_lastack = 0; m_rx_rto = DEF_RTO; m_rx_srtt = m_rx_rttvar = 0;}PseudoTcp::~PseudoTcp() {}intPseudoTcp::Connect() { if (m_state != TCP_LISTEN) { m_error = EINVAL; return -1; } m_state = TCP_SYN_SENT; LOG(LS_INFO) << "State: TCP_SYN_SENT"; char buffer[1]; buffer[0] = CTL_CONNECT; queue(buffer, 1, true); attemptSend(); return 0;}voidPseudoTcp::NotifyMTU(uint16 mtu) { m_mtu_advise = mtu; if (m_state == TCP_ESTABLISHED) { adjustMTU(); }}voidPseudoTcp::NotifyClock(uint32 now) { if (m_state == TCP_CLOSED) return; // Check if it's time to retransmit a segment if (m_rto_base && (talk_base::TimeDiff(m_rto_base + m_rx_rto, now) <= 0)) { if (m_slist.empty()) { ASSERT(false); } else { // Note: (m_slist.front().xmit == 0)) { // retransmit segments#if _DEBUGMSG >= _DBG_NORMAL LOG(LS_INFO) << "timeout retransmit (rto: " << m_rx_rto << ") (rto_base: " << m_rto_base << ") (now: " << now << ") (dup_acks: " << static_cast<unsigned>(m_dup_acks) << ")";#endif // _DEBUGMSG if (!transmit(m_slist.begin(), now)) { closedown(ECONNABORTED); return; } uint32 nInFlight = m_snd_nxt - m_snd_una; m_ssthresh = talk_base::_max(nInFlight / 2, 2 * m_mss); //LOG(LS_INFO) << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss; m_cwnd = m_mss; // Back off retransmit timer. Note: the limit is lower when connecting. uint32 rto_limit = (m_state < TCP_ESTABLISHED) ? DEF_RTO : MAX_RTO; m_rx_rto = talk_base::_min(rto_limit, m_rx_rto * 2); m_rto_base = now; } } // Check if it's time to probe closed windows if ((m_snd_wnd == 0) && (talk_base::TimeDiff(m_lastsend + m_rx_rto, now) <= 0)) { if (talk_base::TimeDiff(now, m_lastrecv) >= 15000) { closedown(ECONNABORTED); return; } // probe the window packet(m_snd_nxt - 1, 0, 0, 0); m_lastsend = now; // back off retransmit timer m_rx_rto = talk_base::_min(MAX_RTO, m_rx_rto * 2); } // Check if it's time to send delayed acks if (m_t_ack && (talk_base::TimeDiff(m_t_ack + ACK_DELAY, now) <= 0)) { packet(m_snd_nxt, 0, 0, 0); }#if PSEUDO_KEEPALIVE // Check for idle timeout if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lastrecv + IDLE_TIMEOUT, now) <= 0)) { closedown(ECONNABORTED); return; } // Check for ping timeout (to keep udp mapping open) if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3/2 : IDLE_PING), now) <= 0)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -