/** ** File ......... pSocket.cpp ** Published .... 2005-06-14 ** Author ....... grymse@alhem.net **/ /* Copyright (C) 2005 Anders Hedstrom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef _WIN32 #pragma warning(disable:4786) typedef __int64 int64_t; typedef unsigned __int64 uint64_t; typedef unsigned __int32 uint32_t; #else #include #endif #include "PeerHandler.h" #include "Session.h" #include "Peer.h" #include "Piece.h" #include "pSocket.h" #define DEB(x) x int pSocket::m_next_id = 0; pSocket::pSocket(SocketHandler& h) :TcpSocket(h) ,m_state(ACCEPT_LENGTH) ,m_server(false) ,m_cmd(0) ,m_slice(new unsigned char[SLICE]) ,m_id(++m_next_id) ,m_interest(false) ,m_choke(true) ,m_t_choke(time(NULL)) ,m_cts(false) ,m_sess(NULL) ,m_peer(NULL) ,m_last_r(0) ,m_last_w(0) ,m_last_r_ptr(0) { //DEB(static_cast(Handler()).dprintf(m_id, "(%d)pSocket", m_id);) SetConnectionRetry(-1); // retry forever } pSocket::pSocket(SocketHandler& h,const std::string& hash,unsigned char *p) :TcpSocket(h) ,m_hash(hash) ,m_state(ACCEPT_LENGTH) ,m_server(false) ,m_cmd(0) ,m_slice(new unsigned char[SLICE]) ,m_id(++m_next_id) ,m_interest(false) ,m_choke(true) ,m_t_choke(time(NULL)) ,m_cts(false) ,m_sess(NULL) ,m_peer(NULL) ,m_last_r(0) ,m_last_w(0) { //DEB(static_cast(Handler()).dprintf(m_id, "(%d)pSocket", m_id);) m_sess = static_cast(h).GetSession(hash); if (!m_sess) { SetCloseAndDelete(); return; } memcpy(m_remote_peer_id, p, 20); } pSocket::~pSocket() { //DEB(static_cast(Handler()).dprintf(m_id, "(%d)~pSocket", m_id);) delete[] m_slice; } void pSocket::OnAccept() { //DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnAccept", m_id);) // 0x13 // 'BitTorrent protocol' // 8 x 0x00 // 20 byte hash // 20 byte peer id // (If the receiving side's peer id doesn't match // the one the initiating side expects, it severs // the connection.) m_state = ACCEPT_LENGTH; m_server = true; } void pSocket::OnConnect() { m_peer = m_sess -> GetPeer(GetRemoteAddress()); if (m_peer) { m_peer -> SetChoked(true); m_peer -> SetInterested(false); } DEB( if (m_sess && (static_cast(Handler()).GetDebug() & 1024)) static_cast(Handler()).dprintf(m_id, "(%d)OnConnect", m_id);) SendHello(); SendBitmap(); m_state = ACCEPT_LENGTH; } void pSocket::OnDelete() { DEB( if (m_sess && (static_cast(Handler()).GetDebug() & 1024)) static_cast(Handler()).dprintf(m_id, "(%d)OnDelete", m_id);) } void pSocket::SendHello() { //DEB(static_cast(Handler()).dprintf(m_id, "(%d)SendHello", m_id);) Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)SendHello: no session (fatal)", m_id);) SetCloseAndDelete(); return; } char buf[68]; *buf = 0x13; memcpy(buf + 1, "BitTorrent protocol", 19); memset(buf + 20, 0, 8); memcpy(buf + 28, sess -> GetHashptr(), 20); memcpy(buf + 48, sess -> GetPeerId(), 20); SendBuf(buf, 68); } void pSocket::SendBitmap() { Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)SendBitmap: no session (fatal)", m_id);) SetCloseAndDelete(); return; } piece_v& pcs = sess -> Complete(); // TODO: uncomment.. // if (pcs.size()) { bitmap_t bitmap(sess -> GetNumberOfPieces()); for (piece_v::iterator it = pcs.begin(); it != pcs.end(); it++) { Piece *p = *it; bitmap.set(p -> GetNumber()); } uint32_t l = htonl(bitmap.GetBitmapSize() + 1); SendBuf( (char *)&l, 4); Send("\05"); SendBuf( (char *)bitmap.GetBitmap(),bitmap.GetBitmapSize()); } } void pSocket::OnRead() { /* if (m_state != STATE_GET_PIECE) DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead, state = %d, ptr = %d", m_id, m_state, m_ptr);) */ Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess && m_hash.size() ) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead: no session (fatal)", m_id);) SetCloseAndDelete(); return; } Peer *peer = m_peer; //sess ? sess -> GetPeer(GetRemoteAddress()) : NULL; char slask[100]; TcpSocket::OnRead(); while (ibuf.GetLength() && !CloseAndDelete() ) { size_t l = ibuf.GetLength(); switch (m_state) { case ACCEPT_LENGTH: { char c; ibuf.Read(&c, 1); if (c != 0x13) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead: protocol length != 0x13 (fatal)", m_id);) SetCloseAndDelete(); return; } m_state = ACCEPT_PROTOCOL; } break; case ACCEPT_PROTOCOL: if (l < 19) { return; } ibuf.Read(slask, 19); slask[19] = 0; if (strcmp(slask, "BitTorrent protocol")) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead: protocol != BitTorrent protocol (fatal)", m_id);) SetCloseAndDelete(); return; } m_state = ACCEPT_NULLBYTES; break; case ACCEPT_NULLBYTES: if (l < 8) return; ibuf.Read(slask, 8); m_state = ACCEPT_HASH; break; case ACCEPT_HASH: if (l < 20) return; ibuf.Read(slask, 20); { std::string hash; for (size_t i = 0; i < 20; i++) { unsigned char c; char tmp[10]; memcpy(&c, slask + i, 1); sprintf(tmp, "%02x", c); hash += tmp; } //DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead: %s", m_id, hash.c_str() );) if (m_server) // incoming { // resolve session for incoming connection (m_sess is NULL) Session *s = static_cast(Handler()).GetSession(hash); if (!s) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead: bad incoming hash (fatal)", m_id);) SetCloseAndDelete(); return; } m_hash = hash; m_sess = s; m_peer = s -> GetPeer(GetRemoteAddress()); // can be NULL sess = s; peer = m_peer; } if (hash != m_hash) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead: unexpected hash (fatal)", m_id);) SetCloseAndDelete(); return; } if (m_server) { SendHello(); SendBitmap(); } m_state = ACCEPT_PEER_ID; } break; case ACCEPT_PEER_ID: if (l < 20) return; ibuf.Read(slask, 20); // (If the receiving side's peer id doesn't match // the one the initiating side expects, it severs // the connection.) if (!m_server) { bool ok = true; for (size_t i = 0; i < 20 && ok; i++) if (m_remote_peer_id[i] != (unsigned char)(slask[i]) ) ok = false; if (!ok) { { //DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead: peer id (fatal)", m_id);) SetCloseAndDelete(); return; } } } else // if (m_server) { std::string ip = GetRemoteAddress(); std::string id = static_cast(slask).substr(0,20); if (sess && !sess -> GetPeer(ip)) { Peer *p = new Peer(Handler(),m_hash,ip,id,0); sess -> AddPeer(p); m_peer = p; peer = p; } } m_state = STATE_COMMAND; m_cts = true; break; case STATE_COMMAND: if (l < 4) return; { uint32_t len; ibuf.Read( (char *)&len, 4); m_length_cmd = ntohl(len); /* DEB( size_t msglen = ntohl(len); static_cast(Handler()).dprintf(m_id, "(%d)OnRead: next message length: %d", m_id, msglen);) */ if (len > 0) { m_state = STATE_COMMAND2; } } break; case STATE_COMMAND2: ibuf.Read(&m_cmd, 1); //static_cast(Handler()).dprintf(m_id, "(%d)Command: %d", m_id, m_cmd); // * 0 - choke // * 1 - unchoke // * 2 - interested // * 3 - not interested // * 4 - have [piece(integer)] // * 5 - bitfield [bitmap] // * 6 - request [index begin length] // * 7 - piece [index begin piece(byte[])] // * 8 - cancel [index begin length] switch (m_cmd) { case 0: cmdChoke(); m_state = STATE_COMMAND; break; case 1: cmdUnchoke(); m_state = STATE_COMMAND; break; case 2: cmdInterested(); m_state = STATE_COMMAND; break; case 3: cmdNotinterested(); m_state = STATE_COMMAND; break; case 4: m_integers = 1; m_ptr = 0; m_state = STATE_GET_INTEGERS; break; case 5: m_ptr = 0; m_state = STATE_GET_BITMAP; break; case 6: m_integers = 3; m_ptr = 0; m_state = STATE_GET_INTEGERS; break; case 7: m_integers = 2; m_ptr = 0; m_state = STATE_GET_INTEGERS; break; case 8: m_integers = 3; m_ptr = 0; m_state = STATE_GET_INTEGERS; break; default: static_cast(Handler()).dprintf(m_id, "(%d)OnRead: unknown command code %02x length %d bytes", m_id, m_cmd, m_length_cmd ); m_ptr = 1; m_state = STATE_GET_COMMAND; } break; case STATE_GET_INTEGERS: if (l < 4) return; uint32_t ll; ibuf.Read( (char *)&ll, 4); m_int[m_ptr++] = ntohl(ll); if (m_ptr >= m_integers) { switch (m_cmd) { case 4: if (m_int[0] < sess -> GetNumberOfPieces()) { cmdHave(m_int[0]); m_state = STATE_COMMAND; } else { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead/STATE_GET_INTEGERS:4(Have)", m_id);) SetCloseAndDelete(); } break; case 6: if (m_int[0] < sess -> GetNumberOfPieces() && m_int[1] < sess -> GetPieceLength() && m_int[2] < 131072 && m_int[1] + m_int[2] <= sess -> GetPieceLength() && m_int[2] > 0) { cmdRequest(m_int[0], m_int[1], m_int[2]); m_state = STATE_COMMAND; } else { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead/STATE_GET_INTEGERS:6(Request)", m_id);) SetCloseAndDelete(); } break; case 7: m_ptr = 0; m_state = STATE_GET_PIECE; break; case 8: if (m_int[0] < sess -> GetNumberOfPieces() && m_int[1] < sess -> GetPieceLength() && m_int[2] < 131072 && m_int[1] + m_int[2] <= sess -> GetPieceLength() && m_int[2] > 0) { cmdCancel(m_int[0], m_int[1], m_int[2]); m_state = STATE_COMMAND; } else { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead/STATE_GET_INTEGERS:8(Cancel)", m_id);) SetCloseAndDelete(); } break; } } break; case STATE_GET_BITMAP: //static_cast(Handler()).dprintf(m_id, "(%d)waiting for bitmap size %d bytes", m_id, peer -> GetBitmapSize()); if (l < peer -> GetBitmapSize()) return; ibuf.Read( (char *)peer -> GetBitmap(), peer -> GetBitmapSize()); cmdBitfield(); m_state = STATE_COMMAND; break; case STATE_GET_PIECE: if (m_ptr + l < SLICE) { ibuf.Read( (char *)&m_slice[m_ptr], l); m_ptr += l; } else { ibuf.Read( (char *)&m_slice[m_ptr], 1); m_ptr++; } if (m_ptr >= SLICE) { if (m_int[0] < sess -> GetNumberOfPieces() && m_int[1] < sess -> GetPieceLength()) { cmdPiece(m_int[0], m_int[1], m_slice); if (peer) { peer -> RefreshRequests(); } m_state = STATE_COMMAND; } else { DEB(static_cast(Handler()).dprintf(m_id, "(%d)OnRead/STATE_GET_PIECE", m_id);) SetCloseAndDelete(); } } break; case STATE_GET_COMMAND: { std::string hex; std::string txt; char slask[10]; while (m_ptr < m_length_cmd && l) { unsigned char c; ibuf.Read( (char *)&c,1); sprintf(slask,"%02x",c); hex += slask; sprintf(slask,"%c",isprint((char)c) ? (char)c : '.'); txt += slask; l--; m_ptr++; } //DEB(printf("%s\n%s\n",hex.c_str(),txt.c_str());) if (m_ptr == m_length_cmd) { m_state = STATE_COMMAND; } } break; } } } // * 0 - choke 1 // * 1 - unchoke 2 // * 2 - interested 4 // * 3 - not interested 8 // * 4 - have [piece(integer)] 16 // * 5 - bitfield [bitmap] 32 // * 6 - request [index begin length] 64 // * 7 - piece [index begin piece(byte[])] 128 // * 8 - cancel [index begin length] 256 void pSocket::cmdChoke() { Peer *peer = GetPeer(); DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 1) static_cast(Handler()).dprintf(m_id, ">(%d)Choke", m_id);) if (peer) { peer -> SetChoked(true); peer -> RemoveRequests(); } } void pSocket::cmdUnchoke() { Peer *peer = GetPeer(); DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 2) static_cast(Handler()).dprintf(m_id, ">(%d)Unchoke", m_id);) if (peer) { peer -> SetChoked(false); peer -> RequestAvailable(); } } void pSocket::cmdInterested() { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 4) static_cast(Handler()).dprintf(m_id, ">(%d)Interested", m_id);) Peer *peer = GetPeer(); if (peer) peer -> SetInterested(true); } void pSocket::cmdNotinterested() { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 8) static_cast(Handler()).dprintf(m_id, ">(%d)Notinterested", m_id);) Peer *peer = GetPeer(); if (peer) peer -> SetInterested(false); if (!m_choke) SendChoke(true); } void pSocket::cmdHave(size_t piece) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 16) static_cast(Handler()).dprintf(m_id, ">(%d)Have(%d)", m_id, piece);) Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)cmdRequest: no session (fatal)", m_id);) SetCloseAndDelete(); return; } Peer *peer = GetPeer(); if (peer) { peer -> set(piece); } sess -> SetUpdateInterested(); } void pSocket::cmdBitfield() { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 32) static_cast(Handler()).dprintf(m_id, ">(%d)Bitfield", m_id);) Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)cmdRequest: no session (fatal)", m_id);) SetCloseAndDelete(); return; } sess -> SetUpdateInterested(); // SendChoke(false); } void pSocket::cmdRequest(size_t piece, size_t offset, size_t length) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 64) static_cast(Handler()).dprintf(m_id, ">(%d)Request(%d,%d,%d)", m_id, piece, offset, length);) Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)cmdRequest: no session (fatal)", m_id);) SetCloseAndDelete(); return; } // TODO if (0&&sess -> SliceSent(piece, offset)) { SendChoke(true); } else { SendPiece(piece, offset, length); } } void pSocket::cmdCancel(size_t piece, size_t offset, size_t length) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 256) static_cast(Handler()).dprintf(m_id, ">(%d)Cancel(%d,%d,%d)", m_id, piece, offset, length);) } void pSocket::cmdPiece(size_t piece, size_t offset, unsigned char *data) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 128) static_cast(Handler()).dprintf(m_id, ">(%d)Piece(%d,%d)", m_id, piece, offset);) Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)cmdPiece: no session (fatal)", m_id);) SetCloseAndDelete(); return; } Peer *peer = GetPeer(); size_t length = peer ? peer -> GotSlice(piece, offset) : 0; if (length) { sess -> SaveSlice(piece, offset, length, data); sess -> SetCheckComplete(); } else { DEB(static_cast(Handler()).dprintf(m_id, "(%d)cmdPiece: no length for slice write", m_id);) } // sess -> GenerateRequest(peer); } Peer *pSocket::GetPeer() { return m_peer; /* PeerHandler& ref = static_cast(Handler()); Session *sess = ref.GetSession(m_hash); return sess ? sess -> GetPeer(GetRemoteAddress()) : NULL; */ } void pSocket::SendInterest(bool interested) { DEB( int debug = static_cast(Handler()).GetDebug(); if ((interested && (debug&4)) || (!interested && (debug&8))) static_cast(Handler()).dprintf(m_id, "<(%d)SendInterest(%s)", m_id, interested ? "true" : "false" );) uint32_t l = htonl(1); SendBuf( (char *)&l, 4); if (interested) Send("\02"); else Send("\03"); m_interest = interested; } void pSocket::SendRequest(size_t piece,size_t offset,size_t length) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 64) static_cast(Handler()).dprintf(m_id, "<(%d)SendRequest(%d,%d,%d)", m_id, piece, offset, length );) uint32_t tmp = htonl(piece); char buf[13]; *buf = 6; // request memcpy(buf + 1, &tmp, 4); tmp = htonl(offset); memcpy(buf + 5, &tmp, 4); tmp = htonl(length); memcpy(buf + 9, &tmp, 4); uint32_t l = htonl(13); SendBuf( (char *)&l, 4); SendBuf(buf, 13); } void pSocket::SendCancel(size_t piece,size_t offset,size_t length) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 256) static_cast(Handler()).dprintf(m_id, "<(%d)SendCancel(%d,%d,%d)", m_id, piece, offset, length );) uint32_t tmp = htonl(piece); char buf[13]; *buf = 8; // cancel memcpy(buf + 1, &tmp, 4); tmp = htonl(offset); memcpy(buf + 5, &tmp, 4); tmp = htonl(length); memcpy(buf + 9, &tmp, 4); uint32_t l = htonl(13); SendBuf( (char *)&l, 4); SendBuf(buf, 13); } void pSocket::SendChoke(bool choke) { DEB( int debug = static_cast(Handler()).GetDebug(); if ((choke && (debug&1)) || (!choke && (debug&2))) static_cast(Handler()).dprintf(m_id, "<(%d)SendChoke(%s)", m_id, choke ? "true" : "false" );) uint32_t l = htonl(1); SendBuf( (char *)&l, 4); if (choke) Send("\00"); else Send("\01"); m_choke = choke; if (choke) m_t_choke = time(NULL); } void pSocket::SendHave(size_t piece) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 16) static_cast(Handler()).dprintf(m_id, "<(%d)SendHave(%d)", m_id, piece );) uint32_t l = htonl(piece); uint32_t sz = htonl(5); SendBuf( (char *)&sz, 4); Send("\04"); SendBuf( (char *)&l, 4); } void pSocket::SendPiece(size_t piece,size_t offset,size_t length) { DEB( int debug = static_cast(Handler()).GetDebug(); if (debug & 128) static_cast(Handler()).dprintf(m_id, "<(%d)SendPiece(%d,%d,%d)", m_id, piece, offset, length );) Session *sess = m_sess; //ref.GetSession(m_hash); if (!sess) { DEB(static_cast(Handler()).dprintf(m_id, "(%d)SendPiece: no session (fatal)", m_id);) SetCloseAndDelete(); return; } sess -> SendSlice(this, piece, offset, length); } void pSocket::OnConnectFailed() { Peer *peer = GetPeer(); if (peer) peer -> SetFailed(); } size_t pSocket::GetBytesR() { size_t sz = GetBytesReceived() - m_last_r; m_last_r = GetBytesReceived(); m_last_r_buf[m_last_r_ptr++ % DLCHECKSIZE] = m_last_r; return sz; } size_t pSocket::GetBytesW() { size_t sz = GetBytesSent() - m_last_w; m_last_w = GetBytesSent(); return sz; } bool pSocket::GetDownloadRate(size_t& sz) { if (m_last_r_ptr >= DLCHECKSIZE) { int ptr = m_last_r_ptr - 1; sz = m_last_r_buf[ptr % DLCHECKSIZE] - m_last_r_buf[m_last_r_ptr % 60]; sz /= DLCHECKSIZE; // bytes / sec return true; } return false; } void pSocket::ShowStatus(size_t max) { if (CTS()) { std::string client = "unknown"; std::string version = "-"; std::string id = GetPeer() -> GetID(); switch (id[0]) { case '-': if (id.substr(1,2) == "AZ") client = "Azureus"; if (id.substr(1,2) == "BB") client = "BitBuddy"; if (id.substr(1,2) == "CT") client = "CTorrent"; if (id.substr(1,2) == "MT") client = "MoonlightTorrent"; if (id.substr(1,2) == "LT") client = "libtorrent"; if (id.substr(1,2) == "BX") client = "Bittorrent X"; if (id.substr(1,2) == "TS") client = "Torrentstorm"; if (id.substr(1,2) == "TN") client = "TorrentDotNET"; if (id.substr(1,2) == "SS") client = "SwarmScope"; if (id.substr(1,2) == "XT") client = "XanTorrent"; if (id.substr(1,2) == "BS") client = "BTSlave"; if (id.substr(1,2) == "ZT") client = "ZipTorrent"; if (id.substr(1,2) == "AR") client = "Arctic"; if (id.substr(1,2) == "SB") client = "Swiftbit"; if (id.substr(1,2) == "++") client = "C++"; version = id.substr(3,4); break; case 'S': client = "Shadow"; version = id.substr(1,3); break; case 'U': client = "UPnP NAT Bit Torrent"; version = id.substr(1,3); break; case 'T': client = "BitTornado"; version = id.substr(1,3); break; case 'A': client = "ABC"; version = id.substr(1,3); break; case 'M': client = "Brams"; version = id.substr(1,7); break; case 'e': client = "BitComet"; break; } char up[100]; char dn[100]; if (GetBytesSent() / 1024 > 999) sprintf(up, "%4d MB", GetBytesSent() / (1024 * 1024)); else sprintf(up, "%4d kB", GetBytesSent() / 1024); if (GetBytesReceived() / 1024 > 999) sprintf(dn, "%4d MB", GetBytesReceived() / (1024 * 1024)); else sprintf(dn, "%4d kB", GetBytesReceived() / 1024); { std::string tmp; for (size_t i = 0; i < version.size(); i++) tmp += isprint(version[i]) ? version[i] : '.'; version = tmp; } static_cast(Handler()).dprintf(m_id, "(%5d)%18s %5d / %d (%16s/%7s) UL:%s DL:%s", m_id, GetRemoteAddress().c_str(), GetPeer() -> GetSet(), max, client.c_str(), version.c_str(), up, dn); } }