⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chat1.0pre5.pl

📁 基于socket的聊天室实现原理 附带论文
💻 PL
📖 第 1 页 / 共 2 页
字号:
#!/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">&nbsp;
<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>&nbsp;</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="&lt;&lt;" 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 + -