open_toontown_panda3d/panda/src/net/connectionManager.cxx

343 lines
12 KiB
C++

// Filename: connectionManager.cxx
// Created by: jns (07Feb00)
//
////////////////////////////////////////////////////////////////////
#include "connectionManager.h"
#include "connection.h"
#include "connectionReader.h"
#include "connectionWriter.h"
#include "netAddress.h"
#include "pprerror.h"
#include "config_net.h"
#include <prerror.h>
#ifdef WIN32_VC
#include <winsock.h> // For gethostname()
#endif
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
ConnectionManager::
ConnectionManager() {
_set_mutex = PR_NewLock();
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
ConnectionManager::
~ConnectionManager() {
// Notify all of our associated readers and writers that we're gone.
Readers::iterator ri;
for (ri = _readers.begin(); ri != _readers.end(); ++ri) {
(*ri)->clear_manager();
}
Writers::iterator wi;
for (wi = _writers.begin(); wi != _writers.end(); ++wi) {
(*wi)->clear_manager();
}
PR_DestroyLock(_set_mutex);
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::open_UDP_connection
// Access: Public
// Description: Opens a socket for sending and/or receiving UDP
// packets. If the port number is negative, it will not
// be bound to a socket; this is generally a pointless
// thing to do. If the port number is zero, a random
// socket will be chosen. Otherwise, the specified
// port number is used. Normally, you don't care what
// port a UDP connection is opened on, so you should use
// the default value of zero.
//
// Use a ConnectionReader and ConnectionWriter to handle
// the actual communication.
////////////////////////////////////////////////////////////////////
PT(Connection) ConnectionManager::
open_UDP_connection(int port) {
NetAddress address;
address.set_any(port);
PRFileDesc *socket = PR_NewUDPSocket();
if (socket == (PRFileDesc *)NULL) {
pprerror("PR_NewUDPSocket");
return PT(Connection)();
}
if (port >= 0) {
PRStatus result = PR_Bind(socket, address.get_addr());
if (result != PR_SUCCESS) {
pprerror("PR_Bind");
PR_Close(socket);
return PT(Connection)();
}
net_cat.info()
<< "Creating UDP connection for port " << port << "\n";
} else {
net_cat.info()
<< "Creating UDP connection\n";
}
PT(Connection) connection = new Connection(this, socket);
new_connection(connection);
return connection;
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::open_TCP_server_rendezvous
// Access: Public
// Description: Creates a socket to be used as a rendezvous socket
// for a server to listen for TCP connections. The
// socket returned by this call should only be added to
// a ConnectionListener (not to a generic
// ConnectionReader).
//
// backlog is the maximum length of the queue of pending
// connections.
////////////////////////////////////////////////////////////////////
PT(Connection) ConnectionManager::
open_TCP_server_rendezvous(int port, int backlog) {
NetAddress address;
address.set_any(port);
PRFileDesc *socket = PR_NewTCPSocket();
if (socket == (PRFileDesc *)NULL) {
pprerror("PR_NewTCPSocket");
return PT(Connection)();
}
PRStatus result = PR_Bind(socket, address.get_addr());
if (result != PR_SUCCESS) {
pprerror("PR_Bind");
net_cat.info()
<< "Unable to bind to port " << port << " for TCP.\n";
PR_Close(socket);
return PT(Connection)();
}
result = PR_Listen(socket, backlog);
if (result != PR_SUCCESS) {
pprerror("PR_Listen");
net_cat.info()
<< "Unable to listen to port " << port << " for TCP.\n";
PR_Close(socket);
return PT(Connection)();
}
net_cat.info()
<< "Listening for TCP connections on port " << port << "\n";
PT(Connection) connection = new Connection(this, socket);
new_connection(connection);
return connection;
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::open_TCP_client_connection
// Access: Public
// Description: Attempts to establish a TCP client connection to a
// server at the indicated address. If the connection
// is not established within timeout_ms milliseconds, a
// null connection is returned.
////////////////////////////////////////////////////////////////////
PT(Connection) ConnectionManager::
open_TCP_client_connection(const NetAddress &address, int timeout_ms) {
PRFileDesc *socket = PR_NewTCPSocket();
if (socket == (PRFileDesc *)NULL) {
pprerror("PR_NewTCPSocket");
return PT(Connection)();
}
PRStatus result = PR_Connect(socket, address.get_addr(),
PR_MillisecondsToInterval(timeout_ms));
if (result != PR_SUCCESS) {
if (PR_GetError() != PR_CONNECT_RESET_ERROR) {
pprerror("PR_Connect");
}
net_cat.info()
<< "Unable to open TCP connection to server "
<< address.get_ip_string() << " on port " << address.get_port() << "\n";
PR_Close(socket);
return PT(Connection)();
}
net_cat.info()
<< "Opened TCP connection to server " << address.get_ip_string() << " "
<< " on port " << address.get_port() << "\n";
PT(Connection) connection = new Connection(this, socket);
new_connection(connection);
return connection;
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::open_TCP_client_connection
// Access: Public
// Description: This is a shorthand version of the function to
// directly establish communcations to a named host and
// port.
////////////////////////////////////////////////////////////////////
PT(Connection) ConnectionManager::
open_TCP_client_connection(const string &hostname, int port,
int timeout_ms) {
NetAddress address;
if (!address.set_host(hostname, port)) {
return PT(Connection)();
}
return open_TCP_client_connection(address, timeout_ms);
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::close_connection
// Access: Public
// Description: Terminates a UDP or TCP socket previously opened.
// This also removes it from any associated
// ConnectionReader or ConnectionListeners.
//
// The socket itself may not be immediately closed--it
// will not be closed until all outstanding pointers to
// it are cleared, including any pointers remaining in
// NetDatagrams recently received from the socket.
//
// The return value is true if the connection was marked
// to be closed, or false if close_connection() had
// already been called (or the connection did not belong
// to this ConnectionManager). In neither case can you
// infer anything about whether the connection has
// *actually* been closed yet based on the return value.
////////////////////////////////////////////////////////////////////
bool ConnectionManager::
close_connection(const PT(Connection) &connection) {
PR_Lock(_set_mutex);
Connections::iterator ci = _connections.find(connection);
if (ci == _connections.end()) {
// Already closed, or not part of this ConnectionManager.
PR_Unlock(_set_mutex);
return false;
}
_connections.erase(ci);
Readers::iterator ri;
for (ri = _readers.begin(); ri != _readers.end(); ++ri) {
(*ri)->remove_connection(connection);
}
PR_Unlock(_set_mutex);
PRFileDesc *socket = connection->get_socket();
if (PR_GetDescType(socket) == PR_DESC_SOCKET_TCP) {
// We can't *actually* close the connection right now, because
// there might be outstanding pointers to it. But we can at least
// shut it down. It will be eventually closed when all the
// pointers let go.
PRStatus result = PR_Shutdown(socket, PR_SHUTDOWN_BOTH);
if (result != PR_SUCCESS) {
PRErrorCode errcode = PR_GetError();
if (errcode != PR_NOT_CONNECTED_ERROR) {
pprerror("PR_Shutdown");
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::get_host_name
// Access: Public, Static
// Description: Returns the name of this particular machine on the
// network, if available, or the empty string if the
// hostname cannot be determined.
////////////////////////////////////////////////////////////////////
string ConnectionManager::
get_host_name() {
char temp_buff[1024];
if (gethostname(temp_buff, 1024) == 0) {
return string(temp_buff);
}
return string();
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::new_connection
// Access: Protected
// Description: This internal function is called whenever a new
// connection is established. It allows the
// ConnectionManager to save all of the pointers to open
// connections so they can't be inadvertently deleted
// until close_connection() is called.
////////////////////////////////////////////////////////////////////
void ConnectionManager::
new_connection(const PT(Connection) &connection) {
PR_Lock(_set_mutex);
_connections.insert(connection);
PR_Unlock(_set_mutex);
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::add_reader
// Access: Protected
// Description: This internal function is called by ConnectionReader
// when it is constructed.
////////////////////////////////////////////////////////////////////
void ConnectionManager::
add_reader(ConnectionReader *reader) {
PR_Lock(_set_mutex);
_readers.insert(reader);
PR_Unlock(_set_mutex);
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::remove_reader
// Access: Protected
// Description: This internal function is called by ConnectionReader
// when it is destructed.
////////////////////////////////////////////////////////////////////
void ConnectionManager::
remove_reader(ConnectionReader *reader) {
PR_Lock(_set_mutex);
_readers.erase(reader);
PR_Unlock(_set_mutex);
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::add_writer
// Access: Protected
// Description: This internal function is called by ConnectionWriter
// when it is constructed.
////////////////////////////////////////////////////////////////////
void ConnectionManager::
add_writer(ConnectionWriter *writer) {
PR_Lock(_set_mutex);
_writers.insert(writer);
PR_Unlock(_set_mutex);
}
////////////////////////////////////////////////////////////////////
// Function: ConnectionManager::remove_writer
// Access: Protected
// Description: This internal function is called by ConnectionWriter
// when it is destructed.
////////////////////////////////////////////////////////////////////
void ConnectionManager::
remove_writer(ConnectionWriter *writer) {
PR_Lock(_set_mutex);
_writers.erase(writer);
PR_Unlock(_set_mutex);
}