📄 chat1.0pre5.pl
字号:
#!/usr/bin/perl
#用户数据[$ready_sock 0,$name 1,$passwd 2,$privilige 3,$filter 4,login time 5,color 6]
use strict;
use POSIX;
use IO::Socket;
use IO::Select;
use FileHandle;
use Fcntl;
#use Tie::RefHash;
use Log::Logger;
#下面两句用来以daemon方式运行chat server,在安装和测试的时候为方便察看错误信息,
#最好注释掉
use Proc::Daemon;
Proc::Daemon::Init;
use chat_vars_test qw(@admin @mod @emote @colors %config);
my $version="1.0 pre5";
$^M = 'a' x (1<<16);
$SIG{__DIE__} = \&Carp::confess;
my (%MOD,%ADMIN);
foreach (@mod){
$MOD{$_}=1;
}
foreach (@admin){
$ADMIN{$_}=1;
}
my %sys_filter;#在这个名单里的人的所有消息将被过滤,2000/3/24
$SIG{'PIPE'} = 'IGNORE'; # ignore Broken Pipe
my $lh= new Log::Logger;
$lh->open_append($config{'logfile'});
$|=1;
my $base_sock=IO::Socket::INET->new(Proto=>'tcp',
LocalPort=>$config{'port'},
Listen=>SOMAXCONN,
Reuse=>1) or die $!;
my $sel=IO::Select->new($base_sock);
nonblock($base_sock);
my (@ready,%CLIENTS,%blacklist);
my (%out_buf,%chat_sock);
#tie %out_buf, 'Tie::RefHash';
#tie %chat_sock, 'Tie::RefHash';
while(1){
check_clients();
@ready=$sel->can_read(1);
my $ready_sock;
foreach $ready_sock (@ready){
if ($ready_sock==$base_sock){#新的连接请求
my $new_sock=$base_sock->accept;
if (defined($new_sock)){
$new_sock->autoflush(1);
$sel->add($new_sock);#加入select列表
nonblock($new_sock);
}
}else{#所有用户的数据处理
my $buf="";
$ready_sock->recv($buf,2048);
#print "buf:$buf";
if (!defined($buf)){
log_event("Read buf undef");
last;
}
if ($buf eq ""){#对方关闭了Socket
$sel->remove($ready_sock);
delete $chat_sock{$ready_sock};
$ready_sock->close;
next;
}
my %FORM={};
if ($buf=~/^GET/i){
my ($get,$url,$query,$pair,@pairs);
(undef,$get,undef) = split(/ /, $buf, 3);
($url,$query) = split(/\?/, $get, 2);
@pairs = split(/&/, $query);
foreach $pair (@pairs) {
my ($name,$value) = split(/=/, $pair, 2);
$value =~ tr/+/ /;
$value =~ s/\n//g;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
$FORM{$name} = trim($value);
}
my $sid=$FORM{'sid'};
if ($sid eq "") {
do {
$sid=rand_str($config{'sid_len'});
}while(exists($CLIENTS{$sid}));
}
$out_buf{$ready_sock}.="HTTP/1.1 200 OK\n";
$out_buf{$ready_sock}.="Server: Socket Chat Server/$version\n";
$out_buf{$ready_sock}.="Cache-Control: no-cache\n";
$out_buf{$ready_sock}.="Pragma: no-cache\n";
$out_buf{$ready_sock}.="Content-Type: text/html\n\n";
if($url eq "" || $url eq "/"){#直接浏览端口
#重定向到入口处
my $html=qq(
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;
charset=iso-8859-1">
<META HTTP-EQUIV="refresh"
CONTENT="0;URL=$config{'entry_url'}">
</HEAD>
</HTML>
);
$out_buf{$ready_sock}.=$html;
next;
}
if($url=~/login/){#第一次进入,显示主框架frame,输入数据有name,passwd
my ($name,$passwd);
$name = substr($FORM{'name'},0,$config{'max_name_len'});
$passwd=substr($FORM{'passwd'},0,$config{'max_passwd_len'});
if ($blacklist{$name}==1){
$out_buf{$ready_sock}.="<html>你被禁止进入聊天室</html>";
next;
}
my ($ok,$privilige,$sex)=check_pass($name,$passwd);
my $mid;
if ($ok){
my $color_idx=$FORM{'color'};
my $color;
my $color_num=@colors;
if (!defined($color_idx) || $color_idx=~/\D/ || $color_idx>$color_num-1 || $color_idx<1){
$color=$colors[rand($color_num-1)+1];#discard the first item(black),it's reserved for system info
}else{
$color=$colors[$color_idx];
}
$mid=get_sid($name);
if ($mid) {#如果存在此用户名用户数据
#$msock=$CLIENTS{$mid}->[0];
if (id_ok($mid)){
$out_buf{$ready_sock}.="<html>用户名$name已经有人用了。</html>";
next;
}else{
$CLIENTS{$mid}=[undef,$name,$passwd,$privilige,{},time(),$color];
$sid=$mid;
}
} else {
$CLIENTS{$sid}=[undef,$name,$passwd,$privilige,{},time(),$color];
#{}是哈希结构%filter的引用,过滤列表
#time()数据项是为超时处理,代表登录时间。如果超过TIMEOUT秒,就不再等待chat页面
#socket一项要等chat页面连结以后再添入
}
}else{
#密码错误
$out_buf{$ready_sock}.="<html>密码错了</html>";
next;
}
my $html=qq(
<html>
<head>
<title>聊天室</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<frameset cols="*,170" rows="*" border="1" framespacing="1">
<frameset rows="*,100,0" cols="*" border="0" framespacing="0">
<frame src="/chat?sid=$sid&passwd=$passwd" name="u" frameborder="NO" noresize>
<frame src="/talk?sid=$sid&passwd=$passwd" name="d" frameborder="NO" noresize>
<frame src="about:blank" name="bl">
</frameset>
<frame src="/names?sid=$sid" name="r" noresize>
</frameset>
<noframes><body bgcolor="#FFFFFF">
</body></noframes>
</html>
);
$out_buf{$ready_sock}.=$html;
my $ip;
if (!eval{$ip=$ready_sock->peerhost;}){
log_event("Get peer ip error!$@");
delete $out_buf{$ready_sock};
delete $CLIENTS{$sid};
$sel->remove($ready_sock);
eval{$ready_sock->close;};#是否必要?
next;
}
if ((!$mid)&&defined($name) && $name ne ""){
notice(qq(<a href="javascript:parent.d.csn('$CLIENTS{$sid}->[1]');" target=d>$name</a>进入聊天室了。));
log_event("$name($ip) login");
}
}elsif ($url=~/names/){ #在线名单
my ($id,@list);
foreach $id (keys %CLIENTS){
if (id_ok($id)){
my $name=$CLIENTS{$id}->[1];
if (defined($name) && $name ne ""){
push(@list,$name);
}
}
}
my $num=@list;
my $refresh="";
if (id_ok($sid)){
$refresh=qq(<meta http-equiv='refresh' content="$config{'refresh_interval'};URL=/names?sid=$sid">);
}
my $html=qq(
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<style type="text/css">
<!--
.p9 { font-size: 9pt}
a:visited { color: #0000FF; text-decoration: none}
a:link { color: #0000FF; text-decoration: none}
a:hover { color: #FF0000}
-->
</style></head>
$refresh
<body bgcolor="#DDDDFF">
<p class="p9">
<a href='javascript:location.reload();'>
<font color=red>[更新名单]</font></a></p><p class='p9'>
[在线人数:$num]<br><br>
[<a href="javascript:parent.d.csn('所有人');" target=d>所有人</a>]<br>
);
my ($name,$list);
foreach $name (@list){
$list.=qq{[<a href="javascript:parent.d.csn('$name');" target=d>$name</a>]<br>\n};
}
$html.=$list;
$html.=qq(
<hr><p> </p>
</body></html>
);
$out_buf{$ready_sock}.=$html;
antiidle($sid);#防止断线
}elsif ($url=~/doTalk/){#输入消息处理
#先检查身份
my ($name,$passwd);
$name = $CLIENTS{$sid}->[1];
$passwd=$FORM{'passwd'};
if (!check_auth($sid,$passwd)) {
#密码错误
$out_buf{$ready_sock}.="<html>密码错误1</html>";
next;
}
#检查说话对象、是否私聊、动作等
#print "sendmsg:$sid,$FORM{'talkto'},$FORM{'message'},$FORM{'ws'}\n";
sendmsg($sid,$FORM{'talkto'},$FORM{'message'},$FORM{'ws'});
$out_buf{$ready_sock}.="<html>junk</html>";#与netscape兼容
}elsif($url=~/leave/){
my $passwd=$FORM{'passwd'};
if (!check_auth($sid,$passwd)) {
$out_buf{$ready_sock}.="<html>密码错误2</html>";
next;
}
offline($sid);
$out_buf{$ready_sock}.=qq(<script>top.window.close()</script>);
}elsif ($url=~/talk/){ #信息输入frame显示
my $passwd=$FORM{'passwd'};
my $html=qq(
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<style type="text/css">
<!--
.p9 { font-size: 9pt}
-->
</style>
<script Language='JavaScript'>
var dx ='';
function checksay( ){
if(document.inputform.msg.value==''){
alert('发言内容为空');
return false;
}
if ((document.inputform.msg.value==document.inputform.message.value)&&(document.inputform.talkto.value==dx)){
alert('发言重复');
return false;
}
document.inputform.message.value =document.inputform.msg.value;
document.inputform.msg.value ='';
dx=document.inputform.talkto.value;
document.inputform.msg.focus();
return(true);
}
function csn(str){
document.inputform.talkto.value=str;
document.inputform.msg.focus();
}
function prev(){
document.inputform.msg.value=document.inputform.message.value;
document.inputform.msg.focus();
}
</script>
</head>
<body bgcolor="#DDDDFF">
<form name=inputform action='/doTalk' target='bl' onsubmit='return(checksay());'>
<input type='hidden' name='sid' value='$sid'>
<input type='hidden' name='passwd' value='$passwd'>
<input type='hidden' name='message' value=''>
<table width="580" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="200">对象
<input type="text" name="talkto" size="8" maxlength="20" readonly value="所有人">
<span class="p9">
<input type=button name=filter value='过滤' onClick="document.inputform.msg.value='/filter';document.inputform.Submit.click()">
</span>
</td>
<td valign="middle">
<span class="p9">悄悄话</span>
<input type="checkbox" name="ws" >
<span class="p9">自动滚屏
<input type='checkbox' name='as' checked=true onclick='parent.u.scrollit();'>
<a href="javascript:top.u.location.reload();">刷新</a>
<a href="http://lease.qz.fj.cn/chat40/newhelp.htm" target=_new>帮助</a>
</td>
</tr>
<tr>
<td colspan="2">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>信息
<input type="text" name="msg" MAXLENGTH="120" size="50">
<input type="submit" name="Submit" value="发送">
<input type="button" value="<<" name="pre" language="javascript" onclick="prev();">
</td>
<td> <span class="p9">
<INPUT TYPE=submit name=submit Value='离开' onclick="inputform.action='/leave';onsubmit='return true'">
</span></td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>
);
$out_buf{$ready_sock}.=$html;
}elsif ($url=~/chat/){ #谈话内容初始化,真正谈话内容在别处刷新
my ($name,$passwd);
$name = $CLIENTS{$sid}->[1];
$passwd=$FORM{'passwd'};
if (!check_auth($sid,$passwd)) {
#密码错误
$out_buf{$ready_sock}.=qq(<html>密码错误!如果在刷新时出现这个问题,请<A href="javascript:top.location.reload()">点击此处</a>。</html>);
next;
}
$CLIENTS{$sid}->[0]=$ready_sock;
my $html=qq(
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<style type="text/css">
<!--
.p9 { font-size: 11pt; line-height: 15pt}
a:visited { color: #0000FF; text-decoration: none}
a:link { color: #0000FF; text-decoration: none}
a:hover { color: #FF0000}
-->
</style></head>
<SCRIPT LANGUAGE="JavaScript1.1">
<!--
var autoScrollOn = 1;
var scrollOnFunction;
var scrollOffFunction;
function scrollit(){
if(!parent.d.document.inputform.as.checked){
autoScrollOn=0;
return true;
}else {
autoScrollOn=1;
StartUp();
return true;
}
}
function scrollWindow( ){
if ( autoScrollOn == 1 ){
this.scroll(0, 65000);
setTimeout('scrollWindow()',200);
}
}
function scrollOn( ){
autoScrollOn = 1;
scrollWindow( );
}
function scrollOff( ){
autoScrollOn = 0;
}
function StartUp( ){
this.onblur = scrollOnFunction;
this.onfocus = scrollOffFunction;
scrollWindow( );
}
scrollOnFunction = new Function('scrollOn( )')
scrollOffFunction = new Function('scrollOff( )')
StartUp();
//--></script>
<body bgcolor="#EEEEFF">
<p><font color="red"><span class="p9">[系统信息]</span></font><span class="p9">测试中,欢迎提出宝贵意见<br>
<hr size="1">
</span></p>
<p class="p9" align="left">
);
$out_buf{$ready_sock}.=$html;
$chat_sock{$ready_sock}=1;
}
}
}
}
my @w_ready=$sel->can_write(1);
foreach $ready_sock (@w_ready){
next unless exists $out_buf{$ready_sock};
my $rv;
if (!defined(eval{$rv = $ready_sock->send($out_buf{$ready_sock}, 0);})){
# send error
log_event("Send Error!!$@");
delete $out_buf{$ready_sock};
$sel->remove($ready_sock);
$ready_sock->close;
delete $chat_sock{$ready_sock};
next;
}
unless (defined $rv) {
log_event("I was told I could write, but I can't.");
next;
}
if ($rv == length $out_buf{$ready_sock} || $! == POSIX::EWOULDBLOCK) {
substr($out_buf{$ready_sock}, 0, $rv) = '';
if (!length $out_buf{$ready_sock}){
delete $out_buf{$ready_sock};
if (!$chat_sock{$ready_sock}){
$sel->remove($ready_sock);
$ready_sock->close;
}
}
} else {
# Couldn't write all the data, and it wasn't because
# it would have blocked. Shutdown and move on.
delete $out_buf{$ready_sock};
$sel->remove($ready_sock);
close($ready_sock);
next;
}
}
}#while loop
exit;
sub nickinuse {
my($nick,$msid) = @_;
if ($nick eq ""||$nick eq "所有人") { return 1; }
my $id;
foreach $id (keys %CLIENTS) {
if (($CLIENTS{$id}->[1] eq $nick) && ($id ne $msid) && ($id ne "")) {
return 1;
}
}
return 0;
}
sub get_sid{
my $nick=shift;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -