一、基于ActiveX的聊天室
基于ActiveX的聊天室其实现过程类似于前文所述的Java聊天室。在返回给客户端的HTML页面里插入ActiveX控件,该ActiveX控件与Web服务器上运行的专用聊天服务程序进行连接,当客户端用户输入新发言时,发言被传送到聊天服务器并由其广播。和Java Applet一样,ActiveX控件实现了实时聊天。
在网页上使用ActiveX比用Java Applet具有更快的速度和更大的灵活性。因为ActiveX控件下载后,不需要虚拟机解释而是直接执行,如同本机上的应用程序一样。在网页中你可以嵌入各种ActiveX控件(如数据表格、多媒体、图形图象、压缩加密等等),可以方便的将传统应用程序轻松的移植到Intranet/Internet上。它所能实现的功能远远超过现有的各种动态网页技术,包括DHTML、CGI、ASP等。不过话又说回来,由于ActiveX能够存取客户机的所有资源,所以安全性成为该技术流行开来的最大障碍。当下载的网页中包含ActiveX控件时,浏览器用户必须确认该控件带有可以信赖的公司的电子签名,才敢放心执行。ActiveX的另一缺陷是它只能运行于Windows平台,而且只同IE浏览器配合较好,对于Netscape的用户来说,必须下载专门的PlugIn才能查看ActiveX网页。
我们使用Visual Basic 6来实现聊天服务器和客户端的ActiveX控件。
(1)聊天服务器的编写
新建一个工程,类型为标准EXE,名字为prjChatServer,将主Form名字改为frmServer,其上放置的控件有:
txtLog,类型TextBox,属性MultiLine=True ScrollBars=Vertical
sktChatServer,类型 Winsock,属性 Index=0
cmdExit,类型 CommandButton
txtLog用于显示各客户连接信息,sktChatServer为Winsock控件数组,用于处理与各客户的TCP/IP连接,cmdExit按钮则用于退出系统。
在frmServer的代码窗口中加入以下代码:
Option Explicit
Private Const LISTEN_PORT As Integer = 1888 '监听端口号
Private Type ActiveClient_Type
Connected As Boolean '是否在连接状态
ClientIP As String '客户端的IP地址
End Type
Private gClients() As ActiveClient_Type '存储所有连接的聊天用户
Private Sub AddLog(sMess As String) '显示信息
txtLog.Text = txtLog.Text + sMess + Chr(13) + Chr(10)
End Sub
Private Sub cmdExit_Click()
Unload Me
End Sub
Private Sub Form_Load()
ReDim gClients(0)
sktChatServer(0).LocalPort = LISTEN_PORT
sktChatServer(0).Listen '启动服务器
AddLog "聊天服务器启动,端口号为" + CStr(LISTEN_PORT) + " ..."
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Dim i As Integer
'退出时断开所有的连接
For i = 1 To UBound(gClients)
If gClients(i).Connected Then
sktChatServer(i).Close
Unload sktChatServer(i)
End If
Next
End Sub
Private Sub sktChatServer_Close(Index As Integer)
AddLog sktChatServer(Index).RemoteHostIP + "(" + CStr(Index) + ")" + " 离开了聊天室."
sktChatServer(Index).Close
Unload sktChatServer(Index)
gClients(Index).Connected = False
BroadCast Index, "离开了聊天室"
End Sub
'接受新用户连接服务器请求
Private Sub sktChatServer_ConnectionRequest(Index As Integer, ByVal requestID As Long)
Dim i As Integer
Dim pos As Integer
'寻找gClients中是否有空闲的位置
pos = 0
i = 0
Do While (pos = 0) And (i < UBound(gClients))
i = i + 1
If Not gClients(i).Connected Then
pos = i
Exit Do
End If
Loop
'如果没有,则添加一个新的元素
If pos = 0 Then
pos = UBound(gClients) + 1
ReDim Preserve gClients(pos)
End If
'创建Accept该用户的Socket
Load sktChatServer(pos)
sktChatServer(pos).Accept requestID
gClients(pos).Connected = True
gClients(pos).ClientIP = sktChatServer(pos).RemoteHostIP
AddLog gClients(pos).ClientIP + "(" + CStr(pos) + ") 进入了聊天室..."
BroadCast pos, "进入了聊天室"
End Sub
'当客户有消息发出时,将该消息进行广播
Private Sub sktChatServer_DataArrival(Index As Integer, ByVal bytesTotal As Long)
Dim sTemp As String
Dim i As Integer
sktChatServer(Index).GetData sTemp, vbString
If UCase(CStr(sTemp)) = "QUIT" Then '用户要求退出
sktChatServer_Close Index
Else
BroadCast Index, sTemp
End If
AddLog gClients(Index).ClientIP + "(" + CStr(Index) + "):" + sTemp
End Sub
Private Sub BroadCast(idx As Integer, sMess As String) ‘广播过程
Dim i As Integer
For i = 1 To UBound(gClients)
If gClients(i).Connected Then
sktChatServer(i).SendData gClients(idx).ClientIP + "(" + CStr(idx) + "):" + sMess
DoEvents
End If
Next
End Sub
其工作原理同前文所述的聊天服务器类似,用一个动态数组存储各个聊天用户的连接状态。当新用户连接时,增加一个Winsock控件,即Load sktChatServer(pos),用于同该用户通信。
运行时的聊天服务器画面如下图所示:
(2)聊天客户端的ActiveX控件的编写
新建一个工程,名字为prjChatClient,类型为ActiveX控件。将工程中的用户控件类命名为MyChat。在MyChat用户控件上放置下列控件:
txtLog,类型 TextBox,属性 MultiLine=True,ScrollBars=Vertical
sktChatClient,类型 Winsock,属性缺省(不用设置)
txtSend,类型 TextBox,属性 Text=127.0.0.1 (默认连接到本机)
cmdConnect,类型 CommandButton,属性 Caption=连接
cmdSend,类型 CommandButton,属性 Caption=发送
txtLog用于显示消息;sktChatClient是Winsock控件,用于连接到服务器进行TCP/IP会话;txtSend编辑框有两个作用,在按〖连接〗按钮时是服务器的域名或IP地址(如localhost或127.0.0.1),在按〖发送〗按钮时,是该用户要说的话,在实际操作时,可以按回车键发出命令。
在MyChat的代码窗口中加入以下代码:
Option Explicit
Private Const REMOTE_PORT As Integer = 1888
Private Sub AddLog(sMess As String)
txtLog.Text = txtLog.Text + sMess + Chr(13) + Chr(10)
End Sub
Private Sub cmdConnect_Click()
sktChatClient.Close
sktChatClient.RemoteHost = txtSend.Text
sktChatClient.RemotePort = REMOTE_PORT
sktChatClient.Connect
AddLog "登录服务器 " + txtSend.Text + "..."
Do
DoEvents
Loop Until sktChatClient.State = sckConnected Or sktChatClient.State = sckError
If sktChatClient.State = sckError Then
AddLog "不能登录到服务器!"
Else
AddLog "连接成功!"
cmdConnect.Enabled = False
cmdSend.Enabled = True
End If
End Sub
Private Sub cmdSend_Click()
sktChatClient.SendData txtSend.Text
End Sub
Private Sub sktChatClient_Close()
AddLog "从服务器断开..."
sktChatClient.Close
Do
DoEvents
Loop Until sktChatClient.State = sckClosed
cmdConnect.Enabled = True
cmdSend.Enabled = False
End Sub
Private Sub sktChatClient_DataArrival(ByVal bytesTotal As Long)
Dim sTemp As String
sktChatClient.GetData sTemp, vbString
AddLog sTemp
End Sub
Private Sub txtSend_KeyPress(KeyAscii As Integer)
If KeyAscii = 13 Then '回车键
If cmdSend.Enabled Then '发送
cmdSend_Click
Else
cmdConnect_Click
End If
txtSend.Text = ""
End If
End Sub
所有工作完成后,选择菜单〖文件〗-〖生成prjChatClient.ocx〗生成该ActiveX控件。我们必须使用VB的打包与安装向导来在网上发布这个ActiveX控件。启动VB的Package&Deployment向导,选择工程为prjChatClient.vbp,点击〖打包〗按钮,选择包类型为〖Internet软件包〗,打包向导最终为我们生成prjChatClient.HTM和prjChatClient.CAB两个文件。下面我们开始测试,假定在Web服务器中建了虚拟目录chat,将两个文件拷入chat对应的实际目录,在浏览器的地址中敲入http://服务器地址/chat/prjChatClient.htm就可以进入聊天,运行时的画面如下所示:
当〖连接〗按钮有效时,表示目前是断开状态,在输入框中敲入服务器名或IP地址再按回车键就可以连接服务器;当〖发送〗按钮有效时,表示目前已经连接到聊天服务器,在输入框中敲入信息按回车键则发送信息到服务器,敲入QUIT则退出聊天室。
需要指出的是,VB的安装向导缺省并未将MSWinsck.OCX(Winsocket控件)和VB6的运行库打入CAB安装包中,所以如果别的机器要登录到聊天服务器,则需要mswinsck.ocx和vb6的运行库,否则会从www.microsoft.com下载,为了测试方便,可以在安装向导的最后一步选择将winsock.ocx和VB6的运行库加入CAB文件,或在客户机上安装vb6。
二、基于ASP的聊天室
ASP(Active Server Page)因为其使用简单的VBScript语言以及方便的连接后端数据库等特性而成为NT+IIS平台的首选技术。使用ASP编写出的聊天室代码很少,其原理类似前文所述的CGI聊天室,不过聊天信息现在可以不用存放到文件或数据库中,而是存放到Application对象里。
每一个ASP应用(即对应的虚拟目录及子目录)都会有一个Application对象,所有进入该应用的浏览器客户可以存取到共享的Application对象。同时每个客户都有自己的会话对象Session。这样我们可以在Application对象里放置发言缓冲区,在Session对象里放置每个聊天用户的名字。
下面我们来逐步编写一个ASP聊天室,首先在Web服务器上建立一个虚拟目录aspchat,对应c:\aspchat,在c:\aspchat下编写global.asa文件,内容如下:
(1) 初始化 global.asa文件
<script language="VBScript" runat="Server">
Sub Application_OnStart
Dim gMess(10)
Application("gMess")=gMess
Application("gMess_pos")=0
End Sub
</script>
Application的gMess变量存储最近的发言(此处最多为10条),gMess_pos表示下一条发言所放的位置。
(2)在C:\aspchat下编写default.asp文件,其实是上下两个Frame:
<title>ASP聊天室</title>
<FRAMESET ROWS="*,50">
<FRAME SRC="mess.asp">
<FRAME SRC="login.asp">
</FRAMESET>
其中mess.asp用于显示最近的发言,login.asp用于登录及会话。
(3)在c:\aspchat下编写mess.asp,在开头放置了定时刷新的HEAD标签:
<html>
<head>
<META HTTP-EQUIV="Refresh" CONTENT="4">
</head>
<body>
<%
Dim gmess
gmess=Application("gMess")
for i=0 to 9 ‘显示缓冲区中所有的发言
response.write "<p>"&gmess(i)
Next
%>
</body>
</html>
(4)编写登录及会话的login.asp文件
<html><head></head><body>
<%
Dim uname
Dim gmess
uname=Session("UserName")
if uname="" then ' 用户尚未登录
uname=Request("username") '是否在登录Form中输入了名字
if uname="" then '显示Login Form
%>
<form action=login.asp method="post">
输入您的名字:
<input type=text name=username size=20>
<input type=submit value="进入聊天室">
</form>
<%
response.end '停止ASP输出
else '置Session变量
Session("UserName")=uname
'以下代码显示该用户进入聊天室
pos=Application("gMess_pos")
gmess=Application("gMess")
gmess(pos)=uname&"进入了聊天室"
pos=pos+1
if pos=10 then pos=0
Application.Lock
Application("gMess_pos")=pos
Application("gMess")=gmess
Application.Unlock
end if
%>
<%
else
‘将发言加入Application的发言缓冲区
mess=Request("Message")
mess=Session("UserName")&": "&mess&"<font color=green>(时间:"&Now&")</font>"
pos=Application("gMess_pos")
gmess=Application("gMess")
gmess(pos)=mess
pos=pos+1
if pos=10 then pos=0
Application.Lock ‘为了防止并发写,先锁定
Application("gMess_pos")=pos
Application("gMess")=gmess
Application.Unlock ‘解锁
end if
%>
<form action=login.asp method="post">
输入您的发言:
<input type=text name=Message size=50>
<input type=submit value="发言">
</form>
</body>
</html>
所有工作完成后,就可以在浏览器中输入http://服务器地址/aspchat/ 来测试ASP聊天室了,首先输入名字进入聊天室,然后就可以开始会话。
运行时的画面如下图所示:
三、总结
至此,四种常见的聊天室实现方案已经叙述完毕。通过Web聊天室的实现过程可以了解到各种不同的动态网页技术的特点,从而能根据实际应用环境进行正确的选择。下面是这四种方案的比较:
比较项目 |
CGI |
Java |
ActiveX |
ASP |
Winxx平台适应性 |
OK |
OK |
OK |
OK |
Unix平台适应性 |
OK |
OK |
-- |
-- |
IE浏览器适应性 |
OK |
OK |
OK(需安全保证) |
OK |
Netscape浏览器适应性 |
OK |
OK |
需安装PlugIn |
OK |
执行速度 |
慢 |
慢 |
最快 |
较快 |
实时聊天 |
-- |
OK |
OK |
-- |
OK表示此项指标良好,--表示无
|