Refactored the network implementation. It's now based on a

singleton/factory pattern where peers are equal (almost!). This has
simplified the code a bit and also in theory allows clients to
"re-export" connections to third/fourth/... parties.
This commit is contained in:
simon.kagstrom 2009-02-01 19:47:21 +00:00
parent 2ab9d452dc
commit 8c73c900df
6 changed files with 233 additions and 265 deletions

View File

@ -41,6 +41,13 @@
#include "ROlib.h"
#endif
/* Network connection type */
enum
{
NONE,
MASTER,
CLIENT
};
// Sizes of memory areas
const int C64_RAM_SIZE = 0x10000;
@ -185,10 +192,9 @@ public:
#endif
#ifdef HAVE_SDL
VirtualKeyboard *virtual_keyboard;
NetworkServer *network_server;
NetworkClient *network_client;
char server_hostname[255];
int server_port;
int network_connection_type;
TTF_Font *menu_font;
bool fake_key_sequence;

View File

@ -95,20 +95,21 @@ void C64::c64_ctor1(void)
}
this->virtual_keyboard = new VirtualKeyboard(real_screen, this->menu_font);
this->network_server = NULL;
this->network_client = NULL;
strncpy(this->server_hostname, "localhost",
sizeof(this->server_hostname));
this->server_port = 19760;
this->network_connection_type = NONE;
if (fixme_tmp_network_server)
this->network_server = new NetworkServer(this->server_port);
if (fixme_tmp_network_server) {
Network::StartListener(this->server_port);
this->network_connection_type = MASTER;
}
if (fixme_tmp_network_client)
{
strcpy(this->server_hostname, fixme_tmp_network_client);
this->network_client = new NetworkClient(this->server_hostname,
this->server_port);
Network::ConnectTo(this->server_hostname, this->server_port);
this->network_connection_type = CLIENT;
}
}
@ -317,16 +318,16 @@ void C64::networking_menu(Prefs *np)
{
char buf[2][255];
const char *network_client_messages[] = {
"Start network server", /* 0 */
"Listen for connections",/* 0 */
buf[0], /* 1 */
buf[1], /* 2 */
"Connect to server", /* 3 */
"Connect to remote", /* 3 */
NULL,
};
snprintf(buf[0], 255, "Server hostname (now %s)",
snprintf(buf[0], 255, "Remote hostname (now %s)",
this->server_hostname);
snprintf(buf[1], 255, "Server port (now %d)",
snprintf(buf[1], 255, "Port (now %d)",
this->server_port);
opt = menu_select(real_screen, this->menu_font,
network_client_messages, NULL);
@ -342,22 +343,12 @@ void C64::networking_menu(Prefs *np)
this->server_port = atoi(m);
}
else if (opt == 0) {
/* Cannot be both client and server */
if (this->network_client != NULL) {
delete this->network_client;
this->network_client = NULL;
}
this->network_server = new NetworkServer(this->server_port);
Network::StartListener(this->server_port);
this->network_connection_type = MASTER;
}
else if (opt == 3)
{
if (this->network_server != NULL) {
delete this->network_server;
this->network_server = NULL;
}
this->network_client = new NetworkClient(this->server_hostname,
this->server_port);
else if (opt == 3) {
Network::ConnectTo(this->server_hostname, this->server_port);
this->network_connection_type = CLIENT;
}
} while (opt == 1 || opt == 2);
@ -566,97 +557,84 @@ void C64::network_vblank()
Uint32 now = SDL_GetTicks();
#endif
if (this->network_server) {
/* Perhaps accept a new connection */
this->network_server->CheckNewConnection();
/* Perhaps accept a new connection */
Network::CheckNewConnection();
for (int i = 0; i < this->network_server->n_clients; i++) {
Uint8 *master = this->TheDisplay->BitmapBase();
NetworkClient *remote = this->network_server->clients[i];
static bool has_throttled;
for (int i = 0; i < Network::n_peers; i++) {
Uint8 *master = this->TheDisplay->BitmapBase();
Network *remote = Network::peers[i];
uint8 *js;
static bool has_throttled;
remote->Tick( now - last_time_update );
/* Has the client sent any data? */
if (remote->ReceiveUpdate() == true)
{
uint8 *js = &TheCIA1->Joystick2;
if (this->quit_thyself)
{
remote->Disconnect();
continue;
}
if (ThePrefs.JoystickSwap)
js = &TheCIA1->Joystick1;
if (remote->DecodeUpdate(NULL, js, true) == false)
{
/* Disconnect or sending crap, remove this guy! */
this->network_server->RemoveClient(remote);
continue;
}
remote->ResetNetworkUpdate();
}
if (remote->ThrottleTraffic()) {
/* Skip this frame if the data rate is too high */
has_throttled = true;
continue;
}
remote->EncodeDisplay(master, remote->screen);
if (remote->SendUpdate() == false)
{
/* Disconnect or broken data */
printf("Could not send update\n");
this->network_server->RemoveClient(remote);
}
remote->Tick( now - last_time_update );
if (this->network_connection_type == MASTER) {
if (ThePrefs.JoystickSwap)
js = &TheCIA1->Joystick1;
else
js = &TheCIA1->Joystick2;
} else {
if (ThePrefs.JoystickSwap)
js = &TheCIA1->Joystick2;
else
js = &TheCIA1->Joystick1;
}
/* Has the peer sent any data? */
if (remote->ReceiveUpdate() == true)
{
if (remote->DecodeUpdate(remote->GetScreen(), js) == false)
{
/* Disconnect or sending crap, remove this guy! */
Network::RemovePeer(remote);
continue;
}
if (this->network_connection_type == CLIENT)
this->TheDisplay->Update(remote->GetScreen());
}
remote->ResetNetworkUpdate();
/* Perhaps send updates to the other side (what is determinted by
* if this is the master or not) */
remote->EncodeJoystickUpdate(*js);
remote->EncodeDisplay(master, remote->GetScreen());
if (this->network_connection_type == MASTER &&
remote->ThrottleTraffic()) {
/* Skip this frame if the data rate is too high */
has_throttled = true;
continue;
}
if (remote->SendUpdate() == false)
{
/* Disconnect or broken data */
printf("Could not send update\n");
Network::RemovePeer(remote);
}
else
remote->ResetNetworkUpdate();
if (1)
{
static uint32_t last_traffic_update;
if (1)
{
static uint32_t last_traffic_update;
if (last_time_update - last_traffic_update > 300)
{
TheDisplay->NetworkTrafficMeter(remote->GetKbps() / (8 * 1024.0),
has_throttled);
last_traffic_update = now;
has_throttled = false;
}
}
}
}
else if (this->network_client) {
Uint8 js = TheCIA1->Joystick2;
if (last_time_update - last_traffic_update > 300)
{
TheDisplay->NetworkTrafficMeter(remote->GetKbps() / (8 * 1024.0),
has_throttled);
last_traffic_update = now;
has_throttled = false;
}
}
}
if (this->quit_thyself)
{
this->network_client->Disconnect();
delete this->network_client;
this->network_client = NULL;
return;
}
/* Perhaps send joystick data */
if (this->network_client->cur_joystick_data != js)
{
this->network_client->EncodeJoystickUpdate(js);
this->network_client->SendUpdate();
this->network_client->cur_joystick_data = js;
this->network_client->ResetNetworkUpdate();
}
if (this->network_client->ReceiveUpdate())
{
/* Got something? */
if (this->network_client->DecodeUpdate(this->network_client->screen) == true)
{
TheDisplay->Update(this->network_client->screen);
this->network_client->ResetNetworkUpdate();
}
else
{
/* Disconnect or broken data */
this->network_client->Disconnect();
delete this->network_client;
this->network_client = NULL;
}
}
}
last_time_update = now;
last_time_update = now;
}
/*
@ -693,7 +671,7 @@ void C64::VBlank(bool draw_frame)
j2 &= joykey;
#endif
if (this->network_server)
if (this->network_connection_type == MASTER)
{
/* Only poll one joystick for network servers */
if (ThePrefs.JoystickSwap)
@ -712,7 +690,7 @@ void C64::VBlank(bool draw_frame)
TheCIA2->CountTOD();
// Update window if needed
if (draw_frame && this->network_client == NULL) {
if (draw_frame && this->network_connection_type != CLIENT) {
TheDisplay->Update();
}
@ -778,6 +756,7 @@ void C64::VBlank(bool draw_frame)
ThePrefs.Save(PREFS_PATH);
}
this->network_vblank();
#if defined(GEKKO)
now = ticks_to_millisecs(gettime());
#else

View File

@ -227,13 +227,11 @@ void C64Display::Update(uint8 *src_pixels)
}
draw_string(real_screen, 0, 0, networktraffic_string, black, fill_gray);
if (this->TheC64->network_server) {
for (int i = 0; i < this->TheC64->network_server->n_clients; i++)
{
NetworkClient *client = this->TheC64->network_server->clients[i];
for (int i = 0; i < Network::n_peers; i++)
{
Network *peer = Network::peers[i];
client->DrawTransferredBlocks(real_screen);
}
peer->DrawTransferredBlocks(real_screen);
}
SDL_Flip(real_screen);
}

View File

@ -36,10 +36,13 @@
#define RLE_SIZE ( RAW_SIZE * 4 + 8)
#define DIFF_SIZE ( RAW_SIZE * 4 + 8)
Network::Network()
Network::Network(int sock, bool is_master)
{
const size_t size = NETWORK_UPDATE_SIZE;
this->sock = sock;
this->is_master = is_master;
/* "big enough" buffer */
this->ud = (NetworkUpdate*)malloc( size );
this->tmp_ud = (NetworkUpdate*)malloc( size );
@ -59,6 +62,12 @@ Network::Network()
this->square_updated = (Uint32*)malloc( N_SQUARES_W * N_SQUARES_H * sizeof(Uint32));
assert(this->square_updated);
memset(this->square_updated, 0, N_SQUARES_W * N_SQUARES_H * sizeof(Uint32));
this->screen = (Uint8 *)malloc(DISPLAY_X * DISPLAY_Y);
assert(this->screen);
/* Assume black screen */
memset(this->screen, 0, DISPLAY_X * DISPLAY_Y);
}
Network::~Network()
@ -69,6 +78,9 @@ Network::~Network()
free(this->raw_buf);
free(this->rle_buf);
free(this->diff_buf);
free(this->screen);
this->CloseSocket();
}
void Network::Tick(int ms)
@ -223,8 +235,10 @@ bool Network::CompareSquare(Uint8 *a, Uint8 *b)
return true;
}
size_t Network::EncodeDisplay(Uint8 *master, Uint8 *remote)
void Network::EncodeDisplay(Uint8 *master, Uint8 *remote)
{
if (!this->is_master)
return;
for ( int sq = 0; sq < N_SQUARES_H * N_SQUARES_W; sq++ )
{
Uint8 *p_master = &master[ SQUARE_TO_Y(sq) * DISPLAY_X + SQUARE_TO_X(sq) ];
@ -366,11 +380,14 @@ void Network::EncodeJoystickUpdate(Uint8 v)
{
struct NetworkUpdate *dst = this->cur_ud;
if (this->is_master || this->cur_joystick_data == v)
return;
dst->type = JOYSTICK_UPDATE;
dst->u.joystick.val = v;
dst->size = sizeof(NetworkUpdate);
this->AddNetworkUpdate(dst);
this->cur_joystick_data = v;
}
@ -436,26 +453,20 @@ void Network::DrawTransferredBlocks(SDL_Surface *screen)
}
}
bool Network::ReceiveUpdate(int sock)
bool Network::ReceiveUpdate()
{
struct timeval tv;
memset(&tv, 0, sizeof(tv));
return this->ReceiveUpdate(this->ud, sock, &tv);
return this->ReceiveUpdate(this->ud, &tv);
}
bool Network::ReceiveUpdateBlock(int sock)
{
return this->ReceiveUpdate(this->ud, sock, NULL);
}
bool Network::ReceiveUpdate(NetworkUpdate *dst, int sock, struct timeval *tv)
bool Network::ReceiveUpdate(NetworkUpdate *dst, struct timeval *tv)
{
Uint8 *pp = (Uint8*)dst;
NetworkUpdate *p;
if (this->Select(sock, tv) == false)
if (this->Select(this->sock, tv) == false)
return false;
/* Receive until the stop */
do
@ -463,7 +474,7 @@ bool Network::ReceiveUpdate(NetworkUpdate *dst, int sock, struct timeval *tv)
p = (NetworkUpdate*)pp;
/* Receive the header */
if (this->ReceiveData((void*)p, sock, sizeof(NetworkUpdate)) == false)
if (this->ReceiveData((void*)p, this->sock, sizeof(NetworkUpdate)) == false)
return false;
pp = pp + sizeof(NetworkUpdate);
@ -474,7 +485,7 @@ bool Network::ReceiveUpdate(NetworkUpdate *dst, int sock, struct timeval *tv)
{
size_t sz_diff = sz - sizeof(NetworkUpdate);
if (this->ReceiveData((void*)pp, sock, sz_diff) == false)
if (this->ReceiveData((void*)pp, this->sock, sz_diff) == false)
return false;
pp = pp + sz_diff;
}
@ -485,12 +496,16 @@ bool Network::ReceiveUpdate(NetworkUpdate *dst, int sock, struct timeval *tv)
return true;
}
bool Network::SendUpdate(int sock)
bool Network::SendUpdate()
{
NetworkUpdate *src = this->ud;
NetworkUpdate *stop = this->cur_ud;
size_t sz;
/* Nothing to send, that's OK */
if ( src == stop )
return true;
/* Add a stop at the end of the update */
stop->type = STOP;
stop->u.stop.val = STOP;
@ -503,7 +518,7 @@ bool Network::SendUpdate(int sock)
sz = this->GetNetworkUpdateSize();
if (sz <= 0)
return false;
if (this->SendData((void*)src, sock, sz) == false)
if (this->SendData((void*)src, this->sock, sz) == false)
return false;
this->traffic += sz;
@ -585,7 +600,7 @@ bool Network::DeMarshalData(NetworkUpdate *p)
return true;
}
bool Network::DecodeUpdate(uint8 *screen, uint8 *js, bool server)
bool Network::DecodeUpdate(uint8 *screen, uint8 *js)
{
NetworkUpdate *p = this->ud;
bool out = true;
@ -597,13 +612,15 @@ bool Network::DecodeUpdate(uint8 *screen, uint8 *js, bool server)
case DISPLAY_UPDATE_RAW:
case DISPLAY_UPDATE_RLE:
case DISPLAY_UPDATE_DIFF:
if (screen == NULL)
/* No screen updates _to_ the master */
if (this->is_master)
break;
if (this->DecodeDisplayUpdate(screen, p) == false)
out = false;
break;
case JOYSTICK_UPDATE:
if (js)
/* No joystick updates _from_ the master */
if (js && this->is_master)
*js = p->u.joystick.val;
break;
case DISCONNECT:
@ -618,56 +635,32 @@ bool Network::DecodeUpdate(uint8 *screen, uint8 *js, bool server)
return out;
}
void NetworkServer::AddClient(int sock)
void Network::AddPeer(Network *peer)
{
NetworkClient *cli = new NetworkClient(sock);
this->clients[this->n_clients] = cli;
this->n_clients++;
Network::peers[Network::n_peers] = peer;
Network::n_peers++;
}
void NetworkServer::RemoveClient(NetworkClient *client)
void Network::RemovePeer(Network *peer)
{
for (int i = 0; i < this->n_clients; i++)
for (int i = 0; i < Network::n_peers; i++)
{
if (this->clients[i] == client)
if (Network::peers[i] == peer)
{
if (i < this->n_clients - 1)
if (i < Network::n_peers - 1)
{
/* Swap with last */
this->clients[i] = this->clients[this->n_clients - 1];
Network::peers[i] = Network::peers[Network::n_peers - 1];
}
delete client;
this->n_clients--;
delete peer;
Network::n_peers--;
return;
}
}
/* Not found */
}
void NetworkClient::Init()
{
this->screen = (Uint8 *)malloc(DISPLAY_X * DISPLAY_Y);
assert(this->screen);
/* Assume black screen */
memset(this->screen, 0, DISPLAY_X * DISPLAY_Y);
}
NetworkClient::NetworkClient(int sock)
{
this->Init();
this->sock = sock;
}
NetworkClient::~NetworkClient()
{
free(this->screen);
this->CloseSocket(this->sock);
}
void NetworkClient::Disconnect()
void Network::Disconnect()
{
NetworkUpdate *disconnect= this->cur_ud;
size_t sz;
@ -677,6 +670,12 @@ void NetworkClient::Disconnect()
disconnect->size = sizeof(NetworkUpdate);
this->AddNetworkUpdate(disconnect);
this->SendUpdate();
Network::RemovePeer(this);
}
int Network::n_peers;
int Network::listen_sock;
Network *Network::peers[MAX_NETWORK_PEERS];
#include "NetworkUnix.h"

View File

@ -8,6 +8,8 @@
#endif
#include <SDL.h>
#define MAX_NETWORK_PEERS 8
#define NETWORK_UPDATE_SIZE (256 * 1024)
enum
{
@ -49,16 +51,16 @@ struct NetworkUpdate
class Network
{
public:
Network();
Network(int sock, bool is_master);
~Network();
size_t EncodeDisplay(Uint8 *master, Uint8 *remote);
void EncodeDisplay(Uint8 *master, Uint8 *remote);
void EncodeJoystickUpdate(Uint8 v);
bool DecodeUpdate(uint8 *screen, uint8 *js = NULL, bool server = false);
bool DecodeUpdate(uint8 *screen, uint8 *js = NULL);
void ResetNetworkUpdate(void);
@ -78,13 +80,41 @@ public:
void Tick(int ms);
void CloseSocket(int sock);
void CloseSocket();
bool SendUpdate(int sock);
bool SendUpdate();
bool ReceiveUpdate(int sock);
bool ReceiveUpdate();
bool ReceiveUpdateBlock(int sock);
static bool StartListener(int port);
static bool CheckNewConnection();
static bool ConnectTo(const char *hostname, int port);
bool isConnected()
{
return this->sock >= 0;
}
Uint8 *GetScreen()
{
return this->screen;
}
/**
* Disconnect from the other end. You should delete the object
* after having done this.
*/
void Disconnect();
static void AddPeer(Network *who);
static void RemovePeer(Network *peer);
/* Listener-related */
static Network *peers[MAX_NETWORK_PEERS];
static int n_peers;
protected:
size_t DecodeSoundUpdate(struct NetworkUpdate *src, char *buf);
@ -150,7 +180,7 @@ protected:
bool DecodeDisplayRaw(Uint8 *screen, struct NetworkUpdate *src,
int x, int y);
bool ReceiveUpdate(NetworkUpdate *dst, int sock, struct timeval *tv);
bool ReceiveUpdate(NetworkUpdate *dst, struct timeval *tv);
bool ReceiveData(void *dst, int sock, size_t sz);
@ -181,72 +211,17 @@ protected:
int time_since_last_reset;
int target_kbps;
int kbps;
};
class NetworkClient : public Network
{
public:
NetworkClient(int sock);
~NetworkClient();
NetworkClient(const char *hostname, int port);
bool isConnected()
{
return this->sock >= 0;
}
bool SendUpdate()
{
return ((Network*)this)->SendUpdate(this->sock);
}
bool ReceiveUpdate()
{
return ((Network*)this)->ReceiveUpdate(this->sock);
}
bool ReceiveUpdateBlock()
{
return ((Network*)this)->ReceiveUpdateBlock(this->sock);
}
bool ReceiveData(void *dst, int sock, size_t sz);
/**
* Disconnect from the other end. You should delete the object
* after having done this.
*/
void Disconnect();
Uint8 *screen;
int joystick_port;
bool is_master; /* Some peers are more equal than others */
Uint8 cur_joystick_data;
private:
void Init();
/* Connection to the peer */
int sock;
};
#define MAX_NETWORK_CLIENTS 8
class NetworkServer : public Network
{
public:
NetworkServer(int port);
bool CheckNewConnection();
NetworkClient *clients[MAX_NETWORK_CLIENTS];
int n_clients;
void RemoveClient(NetworkClient *client);
private:
void AddClient(int sock);
int listen_sock;
/* Listener-related */
static int listen_sock;
};
#endif /* NETWORK_H */

View File

@ -66,36 +66,44 @@ bool init_sockaddr (struct sockaddr_in *name,
}
NetworkServer::NetworkServer(int port) : Network()
bool Network::StartListener(int port)
{
this->n_clients = 0;
this->listen_sock = make_socket(port);
Network::listen_sock = make_socket(port);
if (listen(this->listen_sock, MAX_NETWORK_CLIENTS) < 0)
if (Network::listen_sock < 0)
return false;
if (listen(Network::listen_sock, MAX_NETWORK_PEERS) < 0)
{
perror("listen");
exit(1);
return false;
}
return true;
}
bool NetworkServer::CheckNewConnection()
bool Network::CheckNewConnection()
{
struct timeval tv;
struct sockaddr_in client_name;
struct sockaddr_in peer_name;
size_t size;
int client_sock;
int peer_sock;
fd_set listen_fds;
Network *peer;
/* Not initialized yet */
if (Network::listen_sock <= 0)
return false;
/* No more than that thanks... */
if (this->n_clients >= MAX_NETWORK_CLIENTS)
if (Network::n_peers >= MAX_NETWORK_PEERS)
return false;
FD_ZERO(&listen_fds);
FD_SET(this->listen_sock, &listen_fds);
FD_SET(Network::listen_sock, &listen_fds);
/* If something connects, create a new client */
memset(&tv, 0, sizeof(tv));
int v = select(this->listen_sock + 1, &listen_fds, NULL, NULL, &tv);
int v = select(Network::listen_sock + 1, &listen_fds, NULL, NULL, &tv);
if ( v < 0)
{
@ -105,33 +113,32 @@ bool NetworkServer::CheckNewConnection()
else if ( v == 0 )
return false;
size = sizeof(client_name);
client_sock = accept(this->listen_sock, (struct sockaddr*)&client_name, &size);
if (client_sock < 0)
size = sizeof(peer_name);
peer_sock = accept(Network::listen_sock, (struct sockaddr*)&peer_name, &size);
if (peer_sock < 0)
{
fprintf(stderr, "Accepting client failed\n");
fprintf(stderr, "Accepting peer failed\n");
return false;
}
/* And add the new one! */
this->AddClient(client_sock);
Network::AddPeer(new Network(peer_sock, true));
return true;
}
NetworkClient::NetworkClient(const char *hostname, int port)
bool Network::ConnectTo(const char *hostname, int port)
{
/* Again from glibc docs */
struct sockaddr_in servername;
this->Init();
int sock;
/* Create the socket. */
this->sock = socket (PF_INET, SOCK_STREAM, 0);
if (this->sock < 0)
sock = socket (PF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("socket (client)");
return;
return false;
}
set_sock_opts(sock);
@ -142,8 +149,12 @@ NetworkClient::NetworkClient(const char *hostname, int port)
sizeof (servername)) != 0)
{
perror ("connect (client)");
return;
return false;
}
Network::AddPeer( new Network(sock, false) );
return true;
}
bool Network::ReceiveData(void *dst, int sock, size_t sz)
@ -198,7 +209,7 @@ bool Network::Select(int sock, struct timeval *tv)
return v > 0;
}
void Network::CloseSocket(int sock)
void Network::CloseSocket()
{
close(sock);
close(this->sock);
}