📄 netsession_generic.cpp
字号:
/* $Id: netsession_generic.cpp,v 1.27 2003/12/01 13:35:54 mbn Exp $
**
** ClanLib Game SDK
** Copyright (C) 2003 The ClanLib Team
** For a total list of contributers see the file CREDITS.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library 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
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef WIN32
#pragma warning(disable : 4355)
#pragma warning(disable : 4503)
#endif
#include "netsession_generic.h"
#include "netcomputer_generic.h"
#include "API/Network/NetSession/netstream.h"
#include "API/Core/System/threadfunc_v0.h"
#include "API/Core/System/event_listener.h"
#include "API/Core/System/error.h"
#include "API/Core/System/clanstring.h"
#include "API/Core/System/log.h"
#include <cstdio>
/////////////////////////////////////////////////////////////////////////////
// CL_NetSession_Generic construction:
CL_NetSession_Generic::CL_NetSession_Generic(const std::string &app_id)
:
udp_sock(CL_Socket::udp), app_id(app_id),
show_debug(false), accept_tcp_socket(CL_Socket::tcp), accept_udp_socket(CL_Socket::udp),
listen_thread(new CL_ThreadFunc_Runnable_v0<CL_NetSession_Generic>(this, &CL_NetSession_Generic::accept_thread), true),
send_udp_thread(new CL_ThreadFunc_Runnable_v0<CL_NetSession_Generic>(this, &CL_NetSession_Generic::udp_thread), true),
ref(0)
{
boot();
}
CL_NetSession_Generic::~CL_NetSession_Generic()
{
shutdown();
}
/////////////////////////////////////////////////////////////////////////////
// CL_NetSession_Generic attributes:
/////////////////////////////////////////////////////////////////////////////
// CL_NetSession_Generic operations:
void CL_NetSession_Generic::add_ref()
{
ref++;
}
int CL_NetSession_Generic::release_ref()
{
ref--;
if (ref == 0)
{
delete this;
return 0;
}
return ref;
}
CL_NetComputer CL_NetSession_Generic::connect(const CL_IPAddress &address)
{
// Connect to server:
CL_Socket sock(CL_Socket::tcp);
sock.connect(address);
// Establish clanlib netcomputer protocol communication over it:
CL_MutexSection mutex_section(&mutex);
CL_NetComputer_Generic *comp = new CL_NetComputer_Generic(sock, this, false, address.get_port());
comp->add_ref();
computers.push_back(comp);
all.push_back(CL_NetComputer(comp));
return CL_NetComputer(comp);
}
CL_NetComputer CL_NetSession_Generic::connect_async(const std::string &hostname, const std::string &port)
{
// Establish clanlib netcomputer protocol communication over it:
CL_MutexSection mutex_section(&mutex);
CL_NetComputer_Generic *comp = new CL_NetComputer_Generic(hostname, port, this);
comp->add_ref();
computers.push_back(comp);
all.push_back(CL_NetComputer(comp));
return CL_NetComputer(comp);
}
void CL_NetSession_Generic::start_listen(const std::string &port)
{
accept_shutdown_trigger.reset();
accept_tcp_socket = CL_Socket(CL_Socket::tcp);
accept_tcp_socket.set_nonblocking(false);
accept_tcp_socket.bind(CL_IPAddress(port));
accept_udp_socket = CL_Socket(CL_Socket::udp);
accept_udp_socket.bind(CL_IPAddress(port));
accept_tcp_socket.listen(5);
listen_thread.start();
}
void CL_NetSession_Generic::stop_listen()
{
accept_shutdown_trigger.set_flag();
listen_thread.wait();
}
void CL_NetSession_Generic::setup_udp_map(CL_NetComputer_Generic *computer, const std::string &address, unsigned int secret)
{
CL_MutexSection mutex_section(&mutex);
// adds computer to udp connection pending list
udp_secret_ip_map[SecretIPPair(secret, address)] = computer;
}
void CL_NetSession_Generic::remove_udp_map(CL_NetComputer_Generic *computer)
{
CL_MutexSection mutex_section(&mutex);
// removes computer from the list of udp ready connection if avialable
udp_ip_port_map.erase(computer->udp_address);
}
void CL_NetSession_Generic::received_netpacket(const std::string &channel, const CL_NetPacket &packet, CL_NetComputer_Generic *comp)
{
if (show_debug)
CL_Log::log("debug", "Netpacket arrived in %1", channel);
received_netpackets[channel].push(PacketCompPair(packet, CL_NetComputer(comp)));
}
/////////////////////////////////////////////////////////////////////////////
// CL_NetSession_Generic implementation:
void CL_NetSession_Generic::boot()
{
// Start udp thread to send/recive pending udp packets.
send_udp_thread.start();
}
void CL_NetSession_Generic::shutdown()
{
stop_listen();
CL_MutexSection mutex_section(&mutex);
// Disconnect all computers:
all = CL_NetGroup();
std::list<CL_NetComputer_Generic *>::iterator it;
for (it = computers.begin(); it != computers.end(); it++)
{
CL_NetComputer_Generic *computer = *it;
computer->do_shutdown();
computer->release_ref();
}
computers = std::list<CL_NetComputer_Generic *>();
// Empty list of received netpackets.
received_netpackets = std::map< std::string, std::queue<PacketCompPair> >();
// Stop udp thread.
udp_shutdown_trigger.set_flag();
send_udp_thread.wait();
}
void CL_NetSession_Generic::keep_alive()
{
CL_MutexSection mutex_section(&mutex);
// Signal received netpackets on each channel.
std::map< std::string, std::queue<PacketCompPair> >::iterator it;
for (it = received_netpackets.begin(); it != received_netpackets.end(); it++)
{
CL_Signal_v2<CL_NetPacket &, CL_NetComputer &> &chan_sig = map_netpacket_receive[it->first];
std::queue<PacketCompPair> &packets = it->second;
while (!packets.empty())
{
chan_sig(packets.front().first, packets.front().second);
packets.pop();
}
}
// Signal incoming connections:
while (!new_connections.empty())
{
all.push_back(new_connections.front());
sig_computer_connected(new_connections.front());
new_connections.pop();
}
// Signal incoming reconnections:
while (!reconnections.empty())
{
all.push_back(reconnections.front());
sig_computer_reconnected(reconnections.front());
reconnections.pop();
}
// Signal incoming netstreams:
while (!new_streams.empty())
{
NewStreamPair &pair = new_streams.front();
CL_Signal_v1<CL_NetStream &> &chan_sig = map_netstream_connect[pair.second];
CL_NetStream stream(pair.first);
chan_sig(stream);
new_streams.pop();
}
// Signal incoming disconnections:
while (!disconnections.empty())
{
all.remove(disconnections.front());
sig_computer_disconnected(disconnections.front());
disconnections.pop();
}
// Check if any computer handles need to be removed:
std::list<CL_NetComputer_Generic *>::iterator it_computers = computers.begin();
while (it_computers != computers.end())
{
CL_NetComputer_Generic *computer = *it_computers;
if (computer->disconnected)
{
computer->release_ref();
computers.erase(it_computers++);
}
else
{
++it_computers;
}
}
}
void CL_NetSession_Generic::accept_thread()
{
char *buffer = 0;
try
{
CL_EventListener listener;
listener.add_trigger(accept_tcp_socket.get_read_trigger());
listener.add_trigger(accept_udp_socket.get_read_trigger());
listener.add_trigger(&udp_wakeup_server_trigger);
listener.add_trigger(&accept_shutdown_trigger);
int msg_size = accept_udp_socket.get_max_msg_size();
buffer = new char[msg_size];
while (true)
{
listener.wait();
if (accept_shutdown_trigger.get_flag()) break; // stop_listen was called.
if (accept_tcp_socket.get_read_trigger()->get_flag())
{
// Incoming TCP connection:
CL_Socket sock = accept_tcp_socket.accept();
CL_MutexSection mutex_section(&mutex);
CL_NetComputer_Generic *comp =
new CL_NetComputer_Generic(sock, this, true, std::string());
comp->add_ref();
computers.push_back(comp);
}
if (accept_udp_socket.get_read_trigger()->get_flag())
{
// Incoming UDP message from client:
CL_IPAddress from;
int bytes_available = accept_udp_socket.recv(buffer, msg_size, from);
dispatch_udp_messege_server(from, bytes_available, buffer);
}
if (udp_wakeup_server_trigger.get_flag())
{
// Pending UDP packets to client:
IPMassagePair udp_message;
while (!send_udp_server_queue.empty())
{
udp_message = send_udp_server_queue.front();
accept_udp_socket.send(udp_message.second.c_str(), udp_message.second.size(), udp_message.first);
send_udp_server_queue.pop();
}
udp_wakeup_server_trigger.reset();
}
}
accept_tcp_socket = CL_Socket(CL_Socket::tcp);
}
catch (CL_Error err)
{
CL_Log::log("debug", "Error %1", err.message);
}
delete[] buffer;
}
void CL_NetSession_Generic::udp_thread()
{
char *buffer = 0;
try
{
CL_EventListener listener;
// client listen
listener.add_trigger(udp_sock.get_read_trigger());
// server client udp send
listener.add_trigger(&udp_wakeup_client_trigger);
listener.add_trigger(&udp_shutdown_trigger);
int msg_size = accept_udp_socket.get_max_msg_size();
buffer = new char[msg_size];
while (true)
{
listener.wait();
if (udp_shutdown_trigger.get_flag()) break; // netsession is destructing it self
if (udp_sock.get_read_trigger()->get_flag())
{
// Incoming UDP message from server:
CL_IPAddress from;
int bytes_available = udp_sock.recv(buffer, msg_size, from);
dispatch_udp_messege_client(from, bytes_available, buffer);
}
// look for data to send
if (udp_wakeup_client_trigger.get_flag())
{
IPMassagePair udp_message;
while (!send_udp_client_queue.empty())
{
udp_message = send_udp_client_queue.front();
udp_sock.send(udp_message.second.c_str(), udp_message.second.size(), udp_message.first);
send_udp_client_queue.pop();
}
udp_wakeup_client_trigger.reset();
}
}
accept_udp_socket = CL_Socket(CL_Socket::udp);
}
catch (CL_Error err)
{
CL_Log::log("debug", "Error %1", err.message);
}
delete[] buffer;
}
void CL_NetSession_Generic::dispatch_udp_messege_server(const CL_IPAddress &from, int bytes_available, char *buffer)
{
if (bytes_available > 0)
{
// todo: search for ip address in the udp connection map and dispatch message
CL_NetPacket packet(buffer, bytes_available);
// get the computer of the host
std::map<CL_IPAddress, CL_NetComputer_Generic *>::iterator iter_ip_port_map;
iter_ip_port_map = udp_ip_port_map.find(from);
// if computer is not listed, try peeping into data and see if it's a udp connection request
if (iter_ip_port_map == udp_ip_port_map.end())
{
char sig = packet.input.read_char8();
if (sig == CL_NetComputer_Generic::cmd_udp_connect)
{
// get secret(magic) key
unsigned short secret = packet.input.read_ushort16();
std::map<SecretIPPair, CL_NetComputer_Generic *>::iterator iter_secret_ip_map;
iter_secret_ip_map = udp_secret_ip_map.find(SecretIPPair(secret, from.get_address()));
// check if computer is listed in pending list
if (iter_secret_ip_map != udp_secret_ip_map.end())
{
// FOUND!!!
CL_NetComputer_Generic *comp = (*iter_secret_ip_map).second;
// set udp adress to computer
comp->udp_address = from;
// return secret(magic) key to number generator
secret_number_generator.return_number(secret);
// now add computer to udp_ip_port_map list
udp_ip_port_map[from] = comp;
// and remove them from pending list
udp_secret_ip_map.erase(iter_secret_ip_map);
// Rebuild data
packet = CL_NetPacket();
packet.output.write_char8(CL_NetComputer_Generic::cmd_udp_connect);
// send data to computer
comp->received_udp_packet(packet);
}
else
{
// ambiguous remote computer trying connect with fake secret(magic) key???
}
}
else
{
// ambiguous remote computer trying to do cheeky stuff???
}
}
else
{
// Computer found
// send data to computer
(*iter_ip_port_map).second->received_udp_packet(packet);
}
}
}
void CL_NetSession_Generic::dispatch_udp_messege_client(const CL_IPAddress &from, int bytes_available, char *buffer)
{
if (bytes_available > 0)
{
CL_NetPacket packet(buffer, bytes_available);
// get the computer of the host
std::map<CL_IPAddress, CL_NetComputer_Generic *>::iterator iter_ip_port_map;
iter_ip_port_map = udp_ip_port_map.find(from);
// if computer is listed send data
if (iter_ip_port_map != udp_ip_port_map.end())
{
// Computer found
// send data to computer
(*iter_ip_port_map).second->received_udp_packet(packet);
}
else
{
// ambiguous remote computer trying to do cheeky stuff???
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -