/********************************************************************** * * FreeDoko a Doppelkopf-Game * * Copyright (C) 2001-2007 by Diether Knof and Borg Enders * * 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 can find this license in the file 'gpl.txt'. * * 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 * * Contact: * Diether Knof dknof@gmx.de * Borg Enders borg@borgsoft.de * ********************************************************************/ #ifdef USE_NETWORK #include "constants.h" #include "connection.h" #include "../server.h" #include "../types.h" #include "../exceptions.h" #include "../../ui/ui.h" namespace Network { namespace GNet { /** ** constructor ** ** @param server the corresponding server ** @param address address to connect to ** @param port port to connect to ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.1 **/ Connection::Connection(::Network::Server& server, string const& address, unsigned const port) : Network::Connection(server, address, port), socket(), iochannel(NULL), watch(0) { this->socket = gnet_tcp_socket_connect(address.c_str(), port); if (!this->socket) throw ConnectionFailureException(); this->set_relation(PARENT); this->gnet_init(); } // Connection::Connection(Server& server, string address, unsigned port) /** ** constructor ** ** @param server corresponding server ** @param client_socket socket of the client ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.0 **/ Connection::Connection(::Network::Server& server, GTcpSocket* client_socket) : Network::Connection(server), socket(client_socket), iochannel(NULL), watch(0) { if (client_socket == NULL) { cerr << "Connection error" << endl; exit (EXIT_FAILURE); } this->set_relation(CHILD); this->gnet_init(); } // Connection::Connection() /** ** destructor ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.0 **/ Connection::~Connection() { g_source_remove(this->watch); g_io_channel_shutdown(this->iochannel, false, NULL); gnet_tcp_socket_delete(this->socket); } // Connection::~Connection() /** ** init the gnet part ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.0 **/ void Connection::gnet_init() { { // get some information of the client // internet address of the client GInetAddr* client_address = gnet_tcp_socket_get_remote_inetaddr(this->socket); if (client_address == NULL) { cerr << "Error getting the client address" << endl; exit(EXIT_FAILURE); } { // address of the connected computer //gchar* name = gnet_inetaddr_get_canonical_name(client_address); gchar* name = gnet_inetaddr_get_name(client_address); this->address_ = name; this->port_ = gnet_inetaddr_get_port(client_address); // first set the name to the address this->set_name(this->address()); g_free(name); gnet_inetaddr_delete(client_address); } } // get some information of the client // create an io channel to the client this->iochannel = gnet_tcp_socket_get_io_channel(this->socket); if (this->iochannel == NULL) { cerr << "Error getting the client iochannel" << endl; exit(EXIT_FAILURE); } this->watch = g_io_add_watch(this->iochannel, static_cast(G_IO_IN //| G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL), // watch flags async_client_iofunc, this); if (this->name().empty()) { cerr << "Error getting the client name" << endl; #ifdef POSTPHONED exit(EXIT_FAILURE); #endif } if (this->port() == 0) { cerr << "Error getting the client port" << endl; #ifdef POSTPHONED exit(EXIT_FAILURE); #endif } return ; } // void Connection::gnet_init() /** ** sends the text over the connection ** ** @param text text to send ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.2 **/ void Connection::send(string const& text) { if (!this->accepted()) return ; this->outgoing_bytes_ += text.size(); #ifdef NETWORK_LOG_ON_STDOUT cout << "> " << text << endl; #endif GIOError error = G_IO_ERROR_NONE; // error code gsize bytes_written = 0; // written bytes { // Write the data into the stream gchar* buffer = new gchar[text.size()]; if (buffer == NULL) { cerr << "Memory problems.\n" << endl; exit(EXIT_FAILURE); } strncpy(buffer, text.c_str(), text.size()); #ifndef OUTDATED error = gnet_io_channel_writen(this->iochannel, buffer, text.size(), &bytes_written); #else { GError* error = NULL; GIOStatus const status = g_io_channel_write_chars(this->iochannel, buffer, text.size(), &bytes_written, &error); } #endif delete[] buffer; buffer = NULL; } // Write the data into the stream if (error != G_IO_ERROR_NONE) { cerr << "Client write error " << "(" << this->name() << ":" << this->port() << ")" << " : " << error << endl; return ; } if (bytes_written != text.size()) { cerr << "Client write problem " << "(" << this->name() << ":" << this->port() << ")" << " : not all bytes written " << "(" << bytes_written << " out of " << text.size() << ")" << endl; return ; } ::ui->network_traffic(*this, TRAFFIC_OUTGOING, text); return ; } // void Connection::send(string const& text) /** ** traffic on the network ** ** @param condition traffic condition ** ** @return whether the source shall not be removed ** ** @author Diether Knof ** ** @version 0.7.0 ** ** @todo closing of socket **/ bool Connection::traffic(GIOCondition condition) { // Check for socket error if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { cerr << "Client socket error " << "(" << this->name() << ":" << this->port() << ")" << ": " << condition << endl; #ifndef WORKAROUND // error 25 return true; #endif return false; } #ifdef WORKAROUND // see BugReport 5 (under MS-Windows) if (condition == 0) return false; #endif // Check for data to be read (or if the socket was closed) if (condition & G_IO_IN) { #ifdef OUTDATED gchar* buffer = NULL; // buffer gsize bytes_read = 0; // number of read bytes // error code GIOError error = gnet_io_channel_readline_strdup(this->iochannel, &buffer, &bytes_read); // Check for socket error if (error != G_IO_ERROR_NONE) { cerr << "Client read error " << "(" << this->name() << ":" << this->port() << ")" << " : " << error << endl; return false; } #endif gchar buffer[1000]; gsize bytes_read = 0; // number of read bytes GError* error = NULL; if (g_io_channel_read_chars(this->iochannel, buffer, 1000, &bytes_read, &error) != G_IO_STATUS_NORMAL) { cerr << "AClient read error " << "(" << this->name() << ":" << this->port() << ")" << " : " << (error ? static_cast(error->message) : "no error message") << endl; cerr << "closing connection" << endl; this->server().delete_connection(this); return false; } // Check if we read 0 bytes, which means the connection is closed if (bytes_read == 0) { cerr << "Connection closed " << "(" << this->name() << ":" << this->port() << ")" << endl; this->close(); return true; } // so we read something string text(buffer, bytes_read); #if 0 // remove trailing endlines if (text[text.size() - 1] == '\n') { text.erase(text.size() - 1); if (text[text.size() - 1] == '\r') text.erase(text.size() - 1); } #endif #ifdef NETWORK_LOG_ON_STDOUT cout << "< " << text << endl; #endif this->received(text); } else { // if !(condition & G_IO_IN) DEBUG_ASSERTION(false, "Connection::traffic():\n" " unknown condition '" << condition << "'"); } // if !(condition & G_IO_IN) return true; } // bool Connection::traffic(GIOChannel* iochannel, GIOCondition condition) /** ** traffic on the network ** ** @param iochannel IO-channel ** @param condition condition ** @param connection_ptr pointer to the connection ** ** @return whether the data was accepted ** ** @author Diether Knof ** ** @version 0.7.2 **/ gboolean Connection::async_client_iofunc(GIOChannel* iochannel, GIOCondition condition, gpointer connection_ptr) { DEBUG_ASSERTION((static_cast(connection_ptr)->iochannel == iochannel), "Connection::async_client_iofunc():\n" " the iochannels are not the same"); return static_cast(connection_ptr)->traffic(condition); } } // namespace GNet } // namespace Network #endif // #ifdef USE_NETWORK