|
用于简单的 flash client/server 测试,自己添加内容还是比较方便
#!/usr/bin/perl
# Eric Lau 2011-12-17 QQ340004
use strict;
use warnings;
use POE;
use POE::Component::Server::TCP;
# 发往客户端的信息结尾符号,客户端 FLASH 一般要求以 \0 结尾接收信息
my $CLIENT_EOL = "\0";
# 首次连接时要求客户端确认的信息
my $CONFIRM_Q = "ok, connected";
# 在指定端口创建 TCP 服务
POE::Component::Server::TCP->new(
Alias => "game_server",
Port => 5122,
InlineStates => {send => \&handle_send},
ClientConnected => \&client_connected,
ClientError => \&client_disconnected,
ClientDisconnected => \&client_disconnected,
ClientInput => \&client_input,
);
POE::Kernel->run();
exit 0;
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# 当前在线用户信息
my %users;
# 发信处理
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub handle_send {
my ($heap, $message) = @_[HEAP, ARG0];
# 客户端 FLASH 要求以 \0 结尾接收信息
$heap->{client}->put($message.$CLIENT_EOL);
}
# 首次连接处理
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub client_connected {
my $session_id = $_[SESSION]->ID;
# 创建在现用户
$users{$session_id} = {
SID => $session_id,
STATUS => 0, # 客户端连接状态
};
# 一旦客户端连接到服务器,立即向客户端确认
POE::Kernel->post($session_id => send => $CONFIRM_Q);
}
# 断开连接
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub client_disconnected {
my $session_id = $_[SESSION]->ID;
# 删除在线用户信息
my $user = delete $users{$session_id};
$_[KERNEL]->yield("shutdown");
# 客户端下线后的处理
# ...
}
# 处理客户端输入
# 注意,要使 POE 接收到客户端发来的数据,其数据内容必须以 "\n" 结尾
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub client_input {
my($kernel, $session, $input) = @_[KERNEL, SESSION, ARG0];
# 客户端信息
my $session_id = $session->ID;
my $user = $users{$session_id};
# 将信息结尾的 \0 或 \n 去除
chomp $input; #$input =~ s/\0$//;
# 这里列出了一个通讯过程
# 1. 客户端与服务端建立连接,服务端立即发送 $CONFIRM_Q 进行确认(这里设置为要求客户端登录)
# 2. 客户端发送一串字符,则服务端返回 “.” ,请继续其他指令
# 如果客户端发送了一个空字符串(这里表示登陆错误),服务端则要求其继续登录
# 3 设置客户端已登陆成功信息 $user->{STATUS} = 1;
# 4. 客户端发送 1或2,3等指令,服务端返回相应数据,如果是其他内容则强行断开连接
#
# 验证客户端是否通过了首次连接验证,这里用作登录处理
if ($user->{STATUS} == 0)
{
# 验证用户名和密码
my $userinfo = user_login($input);
# 登录成功,更新用户信息
if (defined $userinfo) {
$user->{STATUS} = 1;
$user->{NAME} = $user->{name};
# 可以继续,等待客户端请求
POE::Kernel->post($session_id => send => '.');
}
else {
# 失败,请重试
POE::Kernel->post($session_id => send => $CONFIRM_Q);
}
return;
}
# 客户端已登录,处理客户端的各种请求
CASE:
{
# 可以编辑 “请求代码 => 函数” 列表进行处理
# 客户端请求代码为 1
$input eq "1" && do {
# 信息只发给当前客户端
POE::Kernel->post($session_id => send => '.');
last CASE;
};
$input eq "2" && do {
# 广播
broadcast($session_id, "b.");
last CASE;
};
$input eq "3" && do {
broadcast($session_id, "c.");
last CASE;
};
# ELSE 错误指令,强行断开客户端的连接
# 删除在线用户信息
delete $users{$session_id};
$_[KERNEL]->yield("shutdown");
# 客户端下线后的处理
# ...
}
}
# 用户登录,返回用户信息
my $user_id = 0;
sub user_login {
my($input) = @_;
# 这里进行用户登录验证
if ($input) {
$user_id++;
my $userinfo = {
id => $user_id,
name => 'User_'.$user_id,
};
return $userinfo;
}
return undef;
}
# 向所有用户广播
sub broadcast {
my ($sender, $message) = @_;
foreach my $user (keys %users) {
# 这里可以根据 $users{$user} 的信息进行选择性广播,比如只向屏幕范围内的用户广播。
if ($user == $sender) {
POE::Kernel->post($user => send => "You $message");
} else {
POE::Kernel->post($user => send => "$sender $message");
}
}
}
|
|