上述这些功能点在我以前写的文章里已经出现了N多次了,所以这里我不想过多的在次对他们进行解说,详细请查阅《Flex与.NET互操作系列文章 》,这里我将核心的几个方法代码贴出来简单说明。首先就是客户实现用户登录,通过FluorineFx提供的RemotingService的接口方法进行数据验证。
{
remoteConn = new RemotingConnection( " http://localhost:2020/ChatRoom.FluorineFxWeb/Gateway.aspx " ,ObjectEncoding.AMF3);
myInfo = new UserInfo();
myInfo.UserName = this .txtUserName.text;
myInfo.Password = this .txtPassword.text;
remoteConn.RemotingCall( " ChatRoom.Services.DataService.Login " ,onLoginResult,onLoginFault,myInfo);
}
private function onLoginResult(result:UserInfo): void
{
if (result != null )
{
this .myInfo = result;
this .viewStack.selectedChild = chatView;
rtmpnc = new RtmpConnection( " rtmp://localhost:2777/VideoChat " ,ObjectEncoding.AMF3,onNetStatusHandler,myInfo);
}
else
{
this .lbState.text = " 登陆失败,用户名或密码错误! " ;
}
}
private function onLoginFault( event :Object): void
{
this .lbState.text = " 登陆失败,请重试! " ;
}
private function onClear( event :MouseEvent): void
{
this .txtUserName.text = "" ;
this .txtPassword.text = "" ;
this .lbState.text = "" ;
this .txtUserName.setFocus();
}
RemotingConnection和RtmpConnection是我自己扩展的NetConnection类,功能和NetConnection一样,不同的是封装后的使用相对来说比较方便点。首先通过RemotingService的接口进行用户名和密码验证,通过了则创建一个到RTMP服务器的连接RtmpConnection(等同于NetConnection)。
{
trace( event .info.code);
switch ( event .info.code)
{
case " NetConnection.Connect.Success " :onConnSuccess(); break ;
case " NetConnection.Connect.Failed " :onConnError(); break ;
}
}
private function onConnSuccess(): void
{
// 将自己的视频数据发布到RTMP服务器,这里使用的是FluorineFx
var mic:Microphone = Microphone.getMicrophone();
var publishNs:NetStream = new NetStream(rtmpnc);
publishNs.attachCamera(cam);
publishNs.attachAudio(mic);
publishNs.client = this ;
publishNs.publish(myInfo.ID.toString()); // 将用户ID作为流名进行发布实况流
userSO = SharedObject.getRemote( " OnLineUsers " ,rtmpnc.uri, false );
userSO.addEventListener(SyncEvent.SYNC,onSyncHandler);
userSO.client = this ;
userSO.connect(rtmpnc);
timer = new Timer( 1000 );
timer.addEventListener(TimerEvent.TIMER,onTimerHandler);
timer.start();
}
private function onConnError(): void
{
trace( " login error " );
writeMessage( " <font color=/ " #FF0000/ " >系统提示:连接视频服务器失败</font> " );
}
创建连接的同时指定了由那一个方法(onNetStatusHandler)来处理连接状态,通过判断连接状态如果连接成功则将自己的视频数据发布到RTMP服务器(特别提醒:在发布流的时候是使用的用户ID作为流名,在建立视频聊天的时候需要根据这个ID才能查看到视频),同时还连接到服务器上的远程共享对象(作用:通过异步事件处理函数实现在线用户列表),最后建立了一个Timer是不断的调用服务器方法获取当前系统时间(注意:实际开发中不建议这样做);如果连接服务器失败则在聊天消息显示区输入一条提示信息。
在线用户列表使用共享对象来实现,可以及时的处理用户上线下线功能和实现客户端数据同步更新等。下面是共享对象的异步事件处理函数:
{
var array:Array = event .target.data.UserInfo as Array;
if (array != null )
{
userArray.removeAll();
for (var i:Number = 0 ; i < array.length; i ++ )
{
var info:UserInfo = array[i] as UserInfo;
userArray.addItem(info);
}
trace( " userArray length: " + userArray.length);
}
}
从异步事件中取出当前最新的数据,然后添加到用户界面的显示列表数组(userArray)里,Flex直接使用List组件显示在线用户列表,通过绑定userArray设置数据源,当userArray改变后List组件的显示也会同步更新显示。
那么怎么去建立视频聊天查看到对方的视频呢?其实实现也很简单,这里还是要从用户列表出发,通过点击用户列表上的在线用户,然后建立与该用户的视频连接。同时判断是否选择的是怎么,本案例中我没有将自己从在线列表里屏蔽而是通过判断当前选择的是否为自己,如果是自己则不进行视频连接,也不能发送文本聊天信息。
{
info = List( event .target).selectedItem as UserInfo; //把当前选择的用户信息通过变量保存下来
this .lbNickName.text = info.NickName;
if (info.UserName == myInfo.UserName)
{
writeMessage( " <font color=/ " #FF0000/ " >系统提示:不能和自己进行视频聊天</font> " );
}
else
{
// 建立视频流的连接
if ( this .ns)
{
this .ns.close();
}
this .ns = new NetStream( this .rtmpnc);
ns.client = this ;
sound = this .ns.soundTransform;
var v1:Video = new Video();
v1.width = 320 ;
v1.height = 240 ;
v1.attachNetStream(ns);
this .videoDisplay.addChild(v1);
ns.play(info.ID.toString()); //当前选择的用户的ID
}
}
OK,到这里就成功的完成了用户登录,建立与RTMP服务器的连接,发布视频流,接收指定的视频流等功能,接下来就是实现文字聊天的功能了。实现文字聊天功能是最简单的,我曾经先后在《FMS3系列(六):使用远程共享对象(SharedObject)实现多人时时在线聊天(Flex | Flash) 》和《Flex与.NET互操作(十二):FluorineFx.Net的及时通信应用(Remote Shared Objects)(三) 》这两篇文章中都介绍到了,这里我使用的是第二篇文章里所介绍的方法(提示:该方法就是直接使用SharedObject的send()方法)来实现文字聊天功能。
{
if (info != null )
{
userSO.send( " chatMessage " , this .txtMessage.text, myInfo, info);
this .txtMessage.text = "" ;
}
else
{
writeMessage( " 系统提示:请选择聊天对象 " );
}
}
public function chatMessage(message:String, sayUser:UserInfo, recUser:UserInfo): void
{
if (recUser.UserName == this .myInfo.UserName)
{
message = sayUser.NickName + " 对你说: " + message;
writeMessage(message);
}
if (sayUser.UserName == this .myInfo.UserName)
{
message = " 我对 " + recUser.NickName + " 说: " + message;
writeMessage(message);
}
}
private function writeMessage(message:String): void
{
this .txtDisMessage.htmlText += message + " /n " ;
this .txtDisMessage.verticalScrollPosition = this .txtDisMessage.maxVerticalScrollPosition;
}