/* net/netclient.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include #include #include #include "sys/sys.h" #include "core/net.h" #include "core/application.h" #include "core/stats.h" namespace core { NetClient::NetClient(std::string host, int port, int fd) : client_host(host) { client_error = true; client_state = Connecting; client_fd = fd; client_host = host; client_port = port; client_player = new Player(this); if (!fd) { con_warn << "Network invalid client file descriptor!" << std::endl; abort(); return; } client_addr.sin_family = AF_INET; client_addr.sin_port = htons(port); client_addr.sin_addr.s_addr = inet_addr(host.c_str()); memset(client_addr.sin_zero, '\0', sizeof(client_addr.sin_zero)); if (client_addr.sin_addr.s_addr == INADDR_NONE) { con_warn << "Network invalid client address " << host << "!" << std::endl; abort(); return; } sendq.clear(); messageblock.clear(); client_keepalive = application()->time(); client_timeout = application()->time(); client_error = false; } NetClient::~NetClient() { con_print << host() << ":" << port() << " disconnected" << std::endl; delete client_player; } void NetClient::abort() { client_error = true; } std::string NetClient::host() const { return client_host; } int NetClient::port() const { return client_port; } Player *NetClient::player() { return client_player; } bool NetClient::has_messages() const { return (recvq.size() > 0); } void NetClient::retreive(std::string & message) { if (recvq.size() > 0) { message.assign(recvq.front()); recvq.pop_front(); } else { message.clear(); } } // receive data and decode it into lines void NetClient::receive(char *data) { const char *c = data; while (*c) { if ((*c == '\n') || (*c == '\r')) { if (messageblock.size() > 0) { recvq.push_back(messageblock); messageblock.clear(); } } else { if (messageblock.size() < FRAMESIZE) { messageblock += *c; } else { con_warn << "Incoming message exceeds " << FRAMESIZE << " bytes!\n"; messageblock.clear(); } } c++; } client_timeout = application()->time(); } void NetClient::send_raw(std::string const &msg) { if (error()) return; if ((sendq.size()) && (sendq.size() + msg.size() >= BLOCKSIZE - 16)) { transmit(); } sendq.append(msg); } void NetClient::transmit() { if (!sendq.size()) { if (client_keepalive + NETTIMEOUT / 2 < application()->time()) { sendq.assign("ping\n"); } else { return; } } else if (sendq.size() >= BLOCKSIZE - 16) { con_warn << host() << ":" << port() << " outgoing data exceeds " << BLOCKSIZE - 16 << " bytes!\n"; sendq.clear(); return; } unsigned char zbuf[BLOCKSIZE]; const char *data = 0; uLong compressed_size = (uLong) BLOCKSIZE - 5; uLong uncompressed_size = (uLong) sendq.size(); size_t total_size = 0; memset(zbuf, 0, sizeof(zbuf)); Stats::network_uncompressed_bytes_sent += sendq.size(); // zlib compress int status = compress((Bytef*)(zbuf + 4), &compressed_size, (Bytef*)sendq.c_str(), uncompressed_size); if ((status == Z_OK) && (compressed_size + 4 < sendq.size())) { // add a header to the compress packet data = (char *) zbuf; total_size = (size_t)(compressed_size + 4); zbuf[0] = '\xff'; zbuf[1] = '\xff'; zbuf[2] = compressed_size % 256; zbuf[3] = compressed_size >> 8; } else { data = sendq.c_str(); total_size = sendq.size(); } size_t total_sent = 0; while (total_sent < total_size && !error()) { ssize_t bytes_sent = ::sendto(fd(), data, total_size - total_sent, 0, (struct sockaddr *) & client_addr, sizeof(client_addr)); if (bytes_sent < 0) { abort(); return; } total_sent += bytes_sent; data += bytes_sent; Stats::network_bytes_sent += bytes_sent; } sendq.clear(); client_keepalive = application()->time(); } }