MainServer.pm.svn-base
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:5k
- package Bus::Server::MainServer;
- use strict;
- use warnings;
- use Time::HiRes qw(sleep);
- use Bus::Server::AbstractServer;
- use base qw(Bus::Server::AbstractServer);
- use encoding 'utf8';
- # Client state constants.
- use constant NOT_IDENTIFIED => 1;
- use constant IDENTIFIED => 2;
- # Special message arguments, generated by the bus server:
- # - FROM - The ID of the client who sent the message.
- #
- # Special message arguments, generated by the client:
- # - TO - The ID of the client who is to receive this message.
- # If specified, it indicates that this message is a private message.
- # If not specified, then this is a global message, and will be
- # broadcasted to all clients.
- # - SEQ - A sequence number, used to map reply messages to the original
- # query message. Reply messages MUST have the same SEQ.
- # - IRY - Specifies that this message is a reply to a query.
- sub new {
- my $class = shift;
- my $port = shift;
- my $bind = shift;
- my %args = @_;
- my $self = $class->SUPER::new($port, $bind);
- $self->{quiet} = $args{quiet};
- return $self;
- }
- sub log {
- my ($self, $message) = @_;
- print $message if (!$self->{quiet});
- }
- # New client connected to network
- sub onClientNew {
- my ($self, $client, $index) = @_;
- $self->SUPER::onClientNew($client, $index);
- # Initiate handshake.
- $self->log("New client connected: $client->{ID}n");
- $self->send($client->{ID}, "HELLO", { yourID => $client->{ID} });
- $client->{userAgent} = "Unknown";
- $client->{name} = "Unknown:$client->{ID}";
- $client->{state} = NOT_IDENTIFIED;
- }
- # A client disconnected.
- sub onClientExit {
- my ($self, $client) = @_;
- $self->SUPER::onClientExit($client);
- $self->log("Client exited: $client->{ID}n");
- if ($client->{state} == IDENTIFIED) {
- $self->broadcast('LEAVE', { clientID => $client->{ID} });
- }
- }
- # A client sent a message
- sub messageReceived {
- my ($self, $client, $MID, $args) = @_;
- $self->log("Message: $MID (from $client->{name})n");
- # Process known messages internally.
- # Deliver unknown messages to client(s).
- if (my $handler = $self->can("process$MID")) {
- $handler->($self, $client, $args);
- } elsif (exists $args->{TO}) {
- # Deliver private message.
- my $recepient = $self->getBusClient($args->{TO});
- if ($recepient) {
- my $recepientName = $recepient->{name};
- print "Delivering message from $client->{name} to $recepientNamen";
- $args->{FROM} = $client->{ID};
- if ($self->send($args->{TO}, $MID, $args) != 1) {
- # Delivery failed for some reason. Notify the client.
- my %args2 = ( clientID => $args->{TO} );
- $args2{SEQ} = $args->{SEQ} if (exists $args->{SEQ});
- $args2{IRY} = 1;
- $self->send($client->{ID}, 'DELIVERY_FAILED', %args2);
- }
- } else {
- # Unable to deliver the message because the specified client doesn't exist.
- # Notify the sender.
- my %args2 = ( clientID => $args->{TO} );
- $args2{SEQ} = $args->{SEQ} if (exists $args->{SEQ});
- $args2{IRY} = 1;
- $self->send($client->{ID}, 'CLIENT_NOT_FOUND', %args2);
- }
- } else {
- # Broadcast global message.
- $args->{FROM} = $client->{ID};
- $self->broadcast($MID, $args, { exclude => $client->{ID} });
- }
- }
- # broadcast(String messageID, args, Hash* options)
- #
- # Broadcast a message to all clients on the bus, except:
- # - Clients which are in the NOT_IDENTIFIED state.
- # - Clients which have privateOnly turned on.
- # - The client with the ID as specified in the 'exclude' option.
- #
- # Allowed options:
- # - exclude - The ID of a client, for which this message will not be sent to.
- sub broadcast {
- my ($self, $MID, $args, $options) = @_;
- foreach my $client (@{$self->clients()}) {
- if ($client->{state} != NOT_IDENTIFIED
- && !$client->{privateOnly}
- && (!defined $options->{exclude} || $client->{ID} ne $options->{exclude})) {
- $self->send($client->{ID}, $MID, $args);
- }
- }
- }
- ########### Internal message processors ###########
- sub processHELLO {
- my ($self, $client, $args) = @_;
- if (ref($args) ne 'HASH') {
- # Arguments must be a hash.
- $self->log("Client $client->{ID} didn't sent HELLO arguments as map.");
- $client->close();
- } elsif ($client->{state} == NOT_IDENTIFIED) {
- # A new client just connected.
- $client->{userAgent} = $args->{userAgent} || "Unknown";
- $client->{privateOnly} = $args->{privateOnly};
- $client->{name} = $args->{userAgent} . ":" . $client->{ID};
- $client->{state} = IDENTIFIED;
- # Broadcast a JOIN message about this client.
- $self->log("Client identified as $client->{name}; broadcasting JOINn");
- my %args = (
- clientID => $client->{ID},
- name => $client->{name},
- userAgent => $client->{userAgent},
- host => $client->getIP()
- );
- $self->broadcast("JOIN", %args, { exclude => $client->{ID} });
- } else {
- # The client sent HELLO even though it has already done that.
- $self->log("Client $client->{ID} sent invalid HELLO.n");
- $client->close();
- }
- }
- sub processLIST_CLIENTS {
- my ($self, $client, $args) = @_;
- if (ref($args) ne 'HASH') {
- # Arguments must be a hash.
- $self->log("Client $client->{ID} didn't sent LIST_CLIENTS arguments as map.");
- $client->close();
- } else {
- my %args2;
- my $i = 0;
- foreach my $client (@{$self->clients()}) {
- if ($client->{state} == IDENTIFIED) {
- $args2{"client$i"} = $client->{ID};
- $args2{"clientUserAgent$i"} = $client->{userAgent};
- $i++;
- }
- }
- $args2{count} = $i;
- $args2{SEQ} = $args->{SEQ} if (exists $args->{SEQ});
- $args2{IRY} = 1;
- $self->send($client->{ID}, "LIST_CLIENTS", %args2);
- }
- }
- 1;