Professional Documents
Culture Documents
RED5资料
RED5资料
shtml
方法呼叫类似下面;
If(conn instanceof IServiceCapableConnection){
IServiceCapableConnection sc=(IServiceCapableConnection) conn;
Sc.invoke(“the_method”,new Object[]{“One”,1},new MyCallback());
当然你能在你的应用程序中继承接口,并且传递相关到应用程序实例
/**
* @(#)ParamRed5.as
* @author soda.C
* @version 1.0
*
Copyright (C), 2007 soda.C
*
This program is protected by copyright laws.
* @data 2008-2-19
*/
package org.sujun.red5.test
{
import flash.display.Sprite;
import flash.net.NetConnection;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Responder;
/**
* 测试 flash 与 red5 之间参数的传递
*/
public class ParamRed5 extends Sprite
{
private var netConnection:NetConnection;
netConnection.addEventListener(NetStatusEvent.NET_STATUS,
netStatusHandler);
netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR
, securityErrorHandler);
netConnection.connect("rtmp://localhost/paramtest");
switch (event.info["code"])
{
case "NetConnection.Connect.Success":
trace("连接成功…..");
// 呼 叫 服 务 器 的 baseParam 方 法 , 传 递 基 本 参 数 ,
string,int,number,Boolean
netConnection.call("baseParam", new
Responder(baseParamResult),"soda.C",24,1000.1,false);
//封装数组,int
var ary:Array = new Array();
ary.push(1);
ary.push(2);
ary.push(3);
//封装数组,String
var ary1:Array = new Array();
ary1.push("a");
ary1.push("b");
ary1.push("c");
netConnection.call("receiveArray", new
Responder(baseParamResult),ary,ary1);
break;
case "NetStream.Play.StreamNotFound":
trace("Stream not found: ");
break;
}
}
import java.util.List;
import org.red5.server.adapter.ApplicationAdapter;
/**
* 存放被 flash 客户端调用的方法
*/
public class ParamRed5App extends ApplicationAdapter
{
public ParamRed5App()
{
System.out.println("被初始化了……");
}
/**
* 接受服务器传过来的基本参数
*/
public void baseParam(String name, int age, double value, boolean flag)
{
System.out.println("—-name—-" + name);
System.out.println("—-age—-" + age);
System.out.println("—-value—-" + value);
System.out.println("—-flag—-" + flag);
}
/**
* 接受客户端传递过来的数组
*/
public void receiveArray(List<integer> intArray, List<string> strArray)
{
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("—-intArray—-" + intArray.get(i).intValue());
}
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("—-strArray—-" + strArray.get(i));
}
}
}</string></integer>
看结果…
4、ActionScript(AS3) 与 RED5 通信
第一步:设置环境
下载 red5 http://osflash.org/red5
安装版本 当前最新版本为:Red5 v0.6.3 Final released 。
另外一中方法是下载源码编译运行。我这里就不介绍了。
注意安装的时候,有个选项为 “注册为服务” 这个不要选,否则运行时候会报错。
/*
* 调用服务端的 callme 方法,并把参数 luqinglong,123456 两个参数传过去
* 把服务端返回结果存放在 response 对象当中。
* 注意:服务端 callme 的参数个数要和此一一对应, 应该为
* public String callme(Stirng username,String password);否则回出错)
*/
nc.call(”callme”,response,”luqinglong”,”123456″);
}
/*
* 连接上服务器,做其他操作的发生的错误。比如参数传的有问题。
*/
private function status(error:Object):void
{
trace(”连接结果错误”+error);
}
/*
* 打印返回结果,一定要有个参数 re,否则报错,
* 现在只试过返回 String 格式的
* 返回 xml 和复杂数据结构还在研究当中
*/
private function result(returnobj:Object):void{
trace(”服务端返回结果为:…..”+returnobj);
}
/*
* 网络连接错误,根本就没连上
*/
private function netStatus (event:NetStatusEvent):void{
trace( “连接信息……..\n”+event.info.code);
if (event.info.code == “NetConnection.Connect.Rejected” ){
trace( event.info.application );
}
}
}
}
服务端代码
package test;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IConnection;
public class TestCon extends ApplicationAdapter{
private static final Log log = LogFactory.getLog(TestCon.class );
public TestCon(){
}
public String callme(String username,String password){
String info = “服务端接收到你的调用请求,”;
info+= “你传过来的参数如下”;
info+= “用户名:”+username ;
info+= “密码是:”+password ;
return info ;
}
第二步:编写绑定类
package test2{
import fl.controls.TextArea;
import fl.controls.TextInput;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class ChatRoom extends MovieClip{
private var netclient:NetClient ;
public function ChatRoom(){
super();
//发送按钮
send_btn.addEventListener(MouseEvent.CLICK,sendMessage);
//连接服务端的
netclient = new NetClient(this);
}
/**
*服务端调用此方法来更新消息列表
*/
public function receiveBroadMes(mes:Object):void{
/*
*显示窗口加入消息 ,并且清空发送窗口
*因为和元件绑定所以直接访问实例名:show_txt
*/
show_txt.appendText(mes+”\n”);
send_txt.text = “” ;
}
/**
* 当点击发送按钮的时候,发送消息
*/
private function sendMessage(e:MouseEvent):void{
this.netclient.broadcastMes(send_txt.text);
}
}
}
第三步:编写客户端连接类
package test2{
import flash.events.NetStatusEvent;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;
public class NetClient extends NetConnection
{
private var chatroom:ChatRoom ;
public function NetClient(chatroom:ChatRoom) {
this.chatroom = chatroom ;
this.objectEncoding = ObjectEncoding.AMF0;
this.addEventListener(NetStatusEvent.NET_STATUS , netStatus);
this.connect(”rtmp://192.168.0.20/red5_server”);
}
/**
* 服务端调用此方法
* 更新显示窗口
*/
public function updateMes(obj:Object){
trace(”test call back ….”+obj);
this.chatroom.receiveBroadMes(obj);
}
/**
* 一个客户端发消息所以客户端都能收到
* 调用服务端的广播方法
* 通知所有的在线用户
* @param mes 要发送的信息
*/
public function broadcastMes(mes:String):void{
this.call(”broadcastMes”,null,mes);
}
// 网络连接情况
private function netStatus (event:NetStatusEvent):void{
trace(”net connection case is ………”+event.info.code);
}
}
}
第四步:编写服务端类
package test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.service.IServiceCapableConnection;
public class NetServer extends ApplicationAdapter{
private static final Log log = LogFactory.getLog(TestCon.class);
public NetServer(){
}
/**
* broadcast the message .notify all client
*/
第五步:把元件发到主场景中
配置这里就不做说明,写的其他几遍文章都有详细说明。
本 文 来 源 于 冰 山 上 的 播 客 http://xinsync.xju.edu.cn , 原 文 地 址 :
http://xinsync.xju.edu.cn/index.php/archives/1559
5、 RED5 中使用 SharedObject
要在 RED5 中使用远程 sharedObject,必须注意以下几点:
1,
sharedObject = SharedObject.getRemote( “ 远 程 sharedObject 名 称 “ , nc.uri,
true );
对借助服务器在多个客户端间共享的对象返回一个 SharedObject 的引用。
nc 为 NetConnection 对象。
2,
sharedObject.client = this;
客户端对象为本身。便于广播消息,监听事件。
3,
sharedObject.connect( nc );
通过 nc 链接到服务器。
4,
在与 flashplayer 9 以前发布的 FMS 服务器进行 sharedObject 交互时,
一定要指定 nc.objectEncoding = flash.net.ObjectEncoding.AMF0;
否则 flash 无法监听到 SYNC 事件。目前使用的 RED5 服务器也是如此。
FLEX 实例代码:
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”
layout=”absolute” creationComplete=”initFun()”>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private var nc:NetConnection;
private var clientID:Number;
private var soChat:SharedObject;
private var arr:Array = new Array();
[Bindable]
private var con:ArrayCollection;
private function initFun():void
{
nc = new NetConnection();
nc.objectEncoding = flash.net.ObjectEncoding.AMF0;
nc.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
nc.connect( “rtmp://localhost/MySOSample” );
}
private function statusHandler(event:NetStatusEvent):void
{
if(event.info.code == “NetConnection.Connect.Success”){
connectToChat();
}
}
private function connectToChat():void
{
soChat = SharedObject.getRemote( “wxwred5″, nc.uri, true );
soChat.addEventListener( NetStatusEvent.NET_STATUS, netStatusHandler );
soChat.addEventListener( AsyncErrorEvent.ASYNC_ERROR,
asyncErrorHandler );
//为共享对象添加异步事件,这也是服务器同步处理多客户端最关键的地方,
//一个客户端进行了什么操作,其它客户端也会有相应的变化,就是通过此事件来完成的
soChat.addEventListener( SyncEvent.SYNC, sharedObjectSyncHandler );
soChat.client = this;
soChat.connect( nc );
soChat.send( “getName” );
}
public function getName():void
{
trace( “getName: ” + txtUser.text );
soChat.setProperty(”key”,txtUser.text);
}
public function newName(str:String):void
{
arr.push(str);
soChat.setProperty(”arr”,arr);
con = new ArrayCollection(arr);
}
private function sharedObjectSyncHandler( event:SyncEvent ):void
{
for (var chng:uint; chng<e.changeList.length; chng++)
{
switch (e.changeList[chng].code)
{
case "clear" :
break;
case "success" :
trace (text_so.data.msg);
break;
case "change" ://一个客户端改变数据会更新所有客户端
textArea.htmlText+=text_so.data.msg + "\n";
break;
}
}
}
trace( “sharedObjectSyncHandler:code: ” + event.changeList );
arr.push(event.target.data.key);
con = new ArrayCollection(arr);
}
private function netStatusHandler( event:NetStatusEvent ):void
{
trace( “netStatusHandler:code: ” + event.info.code );
}
private function asyncErrorHandler( event:AsyncErrorEvent ):void
{
trace( “asyncErrorHandler:code: ” + event.error );
}
]]>
</mx:Script>
<mx:TextInput id=”txtUser” horizontalCenter=”1″ verticalCenter=”-109″/>
<mx:Button click=”getName()” label=”Button” horizontalCenter=”0″
verticalCenter=”-53″/>
<mx:List id=”listView” height=”203″ dataProvider=”{con}”
verticalCenter=”68″ horizontalCenter=”0″></mx:List>
</mx:Application>
本 文 来 源 于 冰 山 上 的 播 客 http://xinsync.xju.edu.cn , 原 文 地 址 :
http://xinsync.xju.edu.cn/index.php/archives/1549
基本 狐狐 柏夫
6、 RED5 服务 器调用 客户端 程序
callClient(uid,”makeOnlineList”,new Object[]{onLineList});
private boolean callClient(String uid, String method_name,Object[] obj)
{
IConnection toClient=onLineClient.get(uid);
if (toClient instanceof IServiceCapableConnection)
{
//转发消息
IServiceCapableConnection sc = (IServiceCapableConnection) toClient;
sc.invoke(method_name, obj);
return true;
}
return true;
}
其中 uid 为: IConnection.getClient().getId(); 其实就是客户端连接到服务器时服务器
给与的 ID 值
其中”makeOnlineList”为:客户端函数
其中 new Object[]{onLineList}为:传递的参数。
调用方法是:sc.invoke(method_name, obj);
先通过 IConnection toClient=onLineClient.get(uid);来获取指定 id 对象的客户端,然
后调用
名称为 method_name 的函数,参数为 obj;
要想调用客户端所有的 public 函数,客户端的 Netconnection 的 client 必须为 this;
AS3 端
flash 这边的可以传递的参数也就多了一些.就基本的是
String,int,Number,Boolean,Array, 对 应 到 red5 这 边 是
String,int,double,boolean,List
//封装数组,int
var ary:Array = new Array();
ary.push(1);
ary.push(2);
ary.push(3);
//封装数组,String
var ary1:Array = new Array();
ary1.push("a");
ary1.push("b");
ary1.push("c");
netConnection.call("receiveArray", new
Responder(baseParamResult),ary,ary1);
JAVA
/**
* 接受客户端传递过来的数组
*/
public void receiveArray(List<Integer> intArray, List<String> strArray)
{
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("----intArray----" + intArray.get(i).intValue());
}
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("----strArray----" + strArray.get(i));
}
}
JAVA 回调 AS3 函数
//說話
public void talk(String myName, String msg){
log.info("talk(" + myName + ", " + msg + ")");
Iterator<IConnection> it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
callClient(uid,”makeOnlineList”,new Object[]{onLineList});
private boolean callClient(String uid, String method_name,Object[] obj)
{
IConnection toClient=onLineClient.get(uid);
if (toClient instanceof IServiceCapableConnection)
{
//转发消息
IServiceCapableConnection sc = (IServiceCapableConnection) toClient;
sc.invoke(method_name, obj);
return true;
}
return true;
}
其中”makeOnlineList”为:客户端函数
其中 new Object[]{onLineList}为:传递的参数。
调用方法是:sc.invoke(method_name, obj);
客户端的 调用方法:
myconn.call("broadcastMes", null,str_msg);
7、 Red5 中的 共享对 象
在应用程序中存储共享对象的方法在接口 ISharedObjectService 中定义。当在服务端脚
本里处理共享对象时,要特别注意他们被创建的范围。当一个房间被创建的时候为了去创建
一个新的共享对象,你可以在你的应用程序里面重写方法 roomStart:
Import org.red5.server.adapter.ApplicationAdapter;
Import org.red5.server.api.IScope;
Import org.red5.server.api.so.ISharedObject;
Public class SampleApplication extends ApplicationAdapter{
Public Boolean roomStart(IScope room){
If(!super.roomStart(room))
Return false;
createSharedObject(room,”sampleSO”,true);
ISharedObject so=getSharedObject(room,”sampleSO”);
//.现在你可以用这个共享对象作一些事情。
Return true;
}
每次当第一个用户一个应用程序房间,例如:通过 rtmp://server/application/room1,一
个共享对象 sampleSO 被服务创建。
如果一个连向主程序的共享对象要是被创建,例如 rtmp://server/application,相同的操
作在方法 appStart 中被执行。
一个共享对象提供的更多方法的信息请参考接口 ISaredObjec 的 API 文档。
为 了 得 到 共 享 对 象 的 改 变 通 报 类 似 fcs/fms 的 onSync , 监 听 器 必 须 继 承 接 口
ISharedObjectListener.
Import org.red5.server.api.so.ISharedObject;
Import org.red5.server.api.so.ISharedObjectListener;
此外,监听在共享对象里必许获得注册。
ISharedObject so=getSharedObject(scope,”sampleSO”);
So.addSharedObjectListener(new SampleSharedObjectListener());
应用程序代码里的改变
一个共享对象也能被服务端改变。
ISharedObject os=getSharedObject(scope,”sampleSO”);
So.setAttribute(“fullname”,”Sample user”);
这里所有被署名的客户端得属性新建和改变最好作为注册句柄被通报。
如果关于共享对象的若干动作在一个客户端更新事件中被结合,方法 beginUpdate 和
endUpdate 必须被用到:
ISharedObject so=getSharedObject(scope,”sampleSO”);
So.beginUpdate();
So.setAttribute(“One”,” 1”);
So.setAttribute(“Two”,”2”);
So.removeAttribute(“Three”);
So.endUpdate();
通过下面的代码事件函数被客户端执行:
nc=new NetConnection();
nc.connect(“rtmp://localhost/myapp”);
nc.call(“handler.method”,nc,”Hello world!”);
如果一个事件被注册。Red5 总是在检查上下文配备文件以前在定义范围内寻找他。
配备文件里的事件函数
方法最适合事件处理在应用程序运行范围,他们在应用程序寿命期间是不需要改变的。
注册类 com.fancycode.red5.HandlerSample 为事件 sample,下面的 bean 需要加到
web-inf/red5-web.xml 中。
<bean id=”sample.service”
Class=”com.fancycode.red5.HandlerSample”
Singleton=”true”/>
注:bean 的 id 是由事件名称和关键字 service 构成。
应用程序代码里的事件
所有使用事件处理的应用程序在各种范围或者想要改变事件处理是不同的。从服务段代码需
要一个方法注册他们。这些事件总是凌驾于在 red5-web.xml 中配置的事件。需要注册的方
法在接口 IServiceHnadlerProvider 中被描述,通过 ApplicationAdapter 被执行。
public boolean appStart(IScope app){
if(!super.appStart(scope))
return false;
object handler=new com.fancycode.red5.HandlerSample();
app.registerServiceHandler(“sample”,handler);
return true;
}
注:在这个例子中,仅仅应用程序范围有 sample 事件,不适合子范围.如果事件在 room
中一样可用,必须在 roomStart 时在 room 范围内注册。
本 文 来 源 于 冰 山 上 的 播 客 http://xinsync.xju.edu.cn , 原 文 地 址 :
http://xinsync.xju.edu.cn/index.php/archives/1205
透 過之前介紹過的線上影音教學: http://www.flashextensions.com/tutorials.php,
不懂 Eclipse 的人可以稍微認識一下基本開發環境,以及如何在 Eclipse 中設定開發
Red5 程式的環境。然後要順便認識一下 Ant 開發工具,了解如何 Compile 開發好的
Java 程式並啟動 Red5 Server。
我的資料夾放在這:{Red5}\webapps\FirstRed5App
依照 HOWTO-NewApplications.txt 的說明,修改以下檔案
{Red5}\webapps\FirstRed5App\WEB-INF\web.xml
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>/FirstRed5App</param-value>
</context-param>{Red5}\webapps\FirstRed5App\WEB-INF\red5-web.xml
<bean id="web.handler"
class="idv.ben.red5.Application"
singleton="true" />{Red5}\webapps\FirstRed5App\WEB-INF\red5-
web.properties
webapp.contextPath=/FirstRed5App
webapp.virtualHosts=localhost, 127.0.0.1 所 以 , 我 這 個 Context 的 位 置 在
/FirstRed5App,並且將會由 idv.ben.red5.Application 這個類別作處理!
以下,是這支 Java 程式,會處理來自 Flash 送來的資訊,並廣播到所有連線端!
package idv.ben.red5;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.scheduling.IScheduledJob;
import org.red5.server.api.scheduling.ISchedulingService;
import org.red5.server.api.service.IPendingServiceCall;
import org.red5.server.api.service.IPendingServiceCallback;
import org.red5.server.api.service.IServiceCapableConnection;
@Override
public boolean appStart(IScope app) {
// TODO Auto-generated method stub
if(!super.appStart(app)){
return false;
}
return true;
}
@Override
public void appStop(IScope app) {
// TODO Auto-generated method stub
this.removeScheduledJob(jobId);
super.appStop(app);
}
//新加入
public void join(String myName){
log.info("join(" + myName + ")");
//加到名單
connNameList.add(new MyConn(current, myName));
Iterator<IConnection> it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
//說話
public void talk(String myName, String msg){
log.info("talk(" + myName + ", " + msg + ")");
Iterator<IConnection> it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
//移動
public void walk(String myName, double x, double y){
log.info("walk(" + myName + ", " + x + ", " + y + ")");
Iterator<IConnection> it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
//檢查斷線
public void checkDisconnection(){
log.info("checkDisconnection()");
Iterator<IConnection> it = scope.getConnections();
for(int i=connNameList.size()-1; i>=0; i--){
MyConn myConn = (MyConn)connNameList.get(i);
if(isDisconnect){
//通知所有人 有人斷線
notifyDisconnect(myConn.name);
//從名單移除
connNameList.remove(i);
}
}
//通知所有人 有人斷線
private void notifyDisconnect(String myName){
log.info("notifyDisconnect(" + myName + ")");
Iterator<IConnection> it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
//檢查斷線
this.app.checkDisconnection();
}
}在這個 Java 程式中,我存了一個 connNameList 陣列,用來存放每個連線,我自訂了
MyConn 類別來存放 連線物件 與 登入名稱,這個 MyConn 類別如下:
package idv.ben.red5;
import org.red5.server.api.IConnection;
//----------------------------------Server to Flash
nc.onNewMemberJoined = function(name:String) {
showMsg("歡迎" + name + "加入");
if(_root[name + "_mc"]==undefined){
var mc:MovieClip = _root.createEmptyMovieClip(name + "_mc",
_root.getNextHighestDepth());
mc.lineStyle(1, 0x000000, 100);
if(name==myName){
mc.beginFill(0xFF0000);
}else{
mc.beginFill(0xFFCC00);
}
mc.lineTo(5, 0);
mc.lineTo(5, 5);
mc.lineTo(0, 5);
mc.lineTo(0, 0);
mc.endFill();
}
};
nc.onSomeoneTalking = function(myName:String, msg:String) {
showMsg(myName + "說:" + msg);
};
nc.onSomeoneWalking = function(name:String, x:Number, y:Number){
if(_root[name + "_mc"]!=undefined){
_root[name + "_mc"].targetX = x;
_root[name + "_mc"].targetY = y;
_root[name + "_mc"].onEnterFrame = function(){
this._x += (this.targetX - this._x) * 0.2;
this._y += (this.targetY - this._y) * 0.2;
if(Math.abs(this.targetX - this._x)<0){
this._x = this.targetX;
}
if(Math.abs(this.targetY - this._y)<0){
this._y = this.targetY;
}
if(this._x == this.targetX && this._y == this.targetY){
delete this.onEnterFrame;
}
}
}
}
nc.onSomeoneDisconnect = function(name:String){
showMsg(name + "離開了");
if(_root[name + "_mc"]!=undefined){
_root[name + "_mc"].removeMovieClip();
delete _root[name + "_mc"];
}
}
//----------------------------------Flash to Server
function join(myName:String){
nc.call("join", nc, myName);
}
function talk(myName:String, msg:String){
nc.call("talk", nc, myName, msg);
}
function walk(myName:String, x:Number, y:Number){
nc.call("walk", nc, myName, x, y);
}
//----------------------------------Tools
_global.showMsg = function(msg:String){
//trace(msg);
msg_txt.text = msg + "\n" + msg_txt.text;
}
//----------------------------------
//加入聊天室
var myName:String = "user" + (new Date()).getTime();
join(myName);
//亂說話
function talkSomething(){
talk(myName, "msg" + new Date());
}
setInterval(talkSomething, 5000);
//到處亂走
function workSomewhere(){
walk(myName, Math.random()*Stage.width, Math.random()*Stage.height);
}
workSomewhere();
setInterval(workSomewhere, 10000); 這個聊天室,只要一進入,我就會自動給一個
登錄名稱 myName,之後所有的動作都要傳遞此一識別字串,然後會週期性的亂走與亂
說話。
可以看到,Flash –> Server 的部份,搭配 Java 的 Server 程式,不難理解吧?
nc.call(”join”, nc, myName);
nc.call(”talk”, nc, myName, msg);
nc.call(”walk”, nc, myName, x, y);
然後是 Server –> Flash 的部份,應該也不難理解?!
nc.onNewMemberJoined = function(name:String) {…}
nc.onSomeoneTalking = function(myName:String, msg:String) {…}
nc.onSomeoneWalking = function(name:String, x:Number, y:Number){…}
nc.onSomeoneDisconnect = function(name:String){…}
大 概就這樣,該提的重點應該都提到了!不過以上的程式,判斷連線中斷並通知所有
Flash 的部份,我單機開多個瀏覽器時會測不出來,不知道是否是因為每個瀏覽器都在相
同的電腦上,所以被視同為相同的 IConnection?所以會使得其中一個瀏覽器若是重新整
理或離開時,會造成所有瀏覽器上的 MovieClip 都被移除,被視為離開!當然也有可能
是我的 Java 程式寫得有誤,這就需要進一步的測試了,不過至少週期執行的寫法是可以
學的。
http://xinsync.xju.edu.cn/index.php/archives/608
上 次 遇 到 聊 天 室 踢 掉 斷 線 的 MC 的 部 份 , 寫 錯 了 !
以下測試畫面中,第一個紅色區塊是連線,第二個是斷線,兩個紅色區塊中的藍色區塊是
IConnection 物 件 , 不 一 樣 吧 !
http://bp2.blogger.com/_HsFEtLMw5zU/RsUQQ81o8KI/AAAAAAAAAik/CeJaROW
Ra1w/s320/Clipboard01.jpg
(http://bp2.blogger.com/_HsFEtLMw5zU/RsUQQ81o8KI/AAAAAAAAAik/CeJaRO
WRa1w/s1600-h/Clipboard01.jpg)
http://bp3.blogger.com/_HsFEtLMw5zU/RsUQRM1o8LI/AAAAAAAAAis/gRgs0rb8
F9k/s320/Clipboard02.jpg
(http://bp3.blogger.com/_HsFEtLMw5zU/RsUQRM1o8LI/AAAAAAAAAis/gRgs0rb
8F9k/s1600-h/Clipboard02.jpg)
所 以 , 我 修 改 了 程 式 , 將 IConnection 改 以 IClient 處 理 。
package idv.ben.red5;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.scheduling.IScheduledJob;
import org.red5.server.api.scheduling.ISchedulingService;
import org.red5.server.api.service.IPendingServiceCall;
import org.red5.server.api.service.IPendingServiceCallback;
import org.red5.server.api.service.IServiceCapableConnection;
// 存 一 份 連 線 物 件 與 登 入 名 稱 的 對 照 表
private List connNameList = new ArrayList();
// 實 作 IPendingServiceCallback 介 面 所 要 實 作 的 method
public void resultReceived(IPendingServiceCall call) {
// TODO Auto-generated method stub
}
@Override
public boolean appStart(IScope app) {
// TODO Auto-generated method stub
if(!super.appStart(app)){
return false;
}
return true;
}
@Override
public void appStop(IScope app) {
// TODO Auto-generated method stub
this.removeScheduledJob(jobId);
super.appStop(app);
}
@Override
public boolean appConnect(IConnection conn, Object[] params) {
// TODO Auto-generated method stub
if(!super.appConnect(conn, params))return false;
return true;
}
@Override
public void appDisconnect(IConnection conn) {
// TODO Auto-generated method stub
super.appDisconnect(conn);
log.info("[appDisconnect]" + conn + ", client=" + conn.getClient());
}
@Override
public boolean appJoin(IClient client, IScope app) {
// TODO Auto-generated method stub
if(!super.appJoin(client, app))return false;
log.info("[appJoin]" + client);
return true;
}
@Override
public void appLeave(IClient client, IScope app) {
// TODO Auto-generated method stub
super.appLeave(client, app);
log.info("[appLeave]" + client);
}
// 新 加 入
public void join(String myName){
//log.info("join(" + myName + ")");
// 加 到 名 單
connNameList.add(new MyClient(current.getClient(), myName));
Iterator it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
// 說 話
public void talk(String myName, String msg){
//log.info("talk(" + myName + ", " + msg + ")");
Iterator it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
// 移 動
public void walk(String myName, double x, double y){
//log.info("walk(" + myName + ", " + x + ", " + y + ")");
Iterator it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
// 檢 查 斷 線
public void checkDisconnection(){
//log.info("checkDisconnection()");
Iterator it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
if (conn.getClient().equals(myClient.client)) {
isDisconnect = false;
break;
}
}
if(isDisconnect){
// 通 知 所 有 人 有 人 斷 線
notifyDisconnect(myClient.name);
// 從 名 單 移 除
connNameList.remove(myClient);
}
}
// 通 知 所 有 人 有 人 斷 線
private void notifyDisconnect(String myName){
//log.info("notifyDisconnect(" + myName + ")");
Iterator it = scope.getConnections();
while (it.hasNext()) {
IConnection conn = it.next();
// 檢 查 斷 線
this.app.checkDisconnection();
}
}
package idv.ben.red5;
import org.red5.server.api.IClient;
Flash 的 部 份 :
//----------------------------------Server to Flash
nc.onNewMemberJoined = function(name:String) {
showMsg(" 歡 迎 " + name + " 加 入 ");
if(_root[name + "_mc"]==undefined){
createMC(name);
}
};
nc.onSomeoneTalking = function(name:String, msg:String) {
showMsg(name + " 說 :" + msg);
};
nc.onSomeoneWalking = function(name:String, x:Number, y:Number){
if(_root[name + "_mc"]==undefined){
createMC(name);
}
_root[name + "_mc"].targetX = x;
_root[name + "_mc"].targetY = y;
_root[name + "_mc"].onEnterFrame = function(){
this._x += (this.targetX - this._x) * 0.2;
this._y += (this.targetY - this._y) * 0.2;
if(Math.abs(this.targetX - this._x)
12、 RED5 基本 概念
red5 里面,每个应用对应一个域(scope),所有的客户端(client)通过连
接(connection)连接到域当中(目前我还没有接触到复合域)。所以说,一个
域基本上就对应一个 java 主程序,所有的配置文件均指向此程序。对于单一域,
每个连接对应一个客户端,而每个客户端对应一个 id,简单的应用,操作就针
对这个 id 和连接进行。
ApplicationAdapter 是所有应用的基础,运行时候里面包含几个事件处理:
public boolean appStart(IScope app) 此应用开始的时候触发,app 为此域
public boolean appConnect(IConnection conn, Object[] params) 客户端
连接到域的时候触发,也就是 nc.connect 的时候触发,conn 为当前连接,后面
为参数
public void appDisconnect(IConnection conn) 客户端断开时触发,conn
为客户端
public boolean appJoin(IClient client, IScope app) 也是连接到应用时
触发,没搞太明白这个
使用 as3 连接服务器端的方法是 nc.call("方法名",响应器,变量),如果有返
回值则会传递到响应器的正确函数中,没有返回值依然会调用正确函数,只是
没有传参。服务器回调 as3 函数时,先判断连接是否正常,然后用 invoke("方
法名",参数)方法调用;as3 这边,nc 是首选接受回调方法的,但是 as3 种,
直接用 nc.callBackMethod=function(){}的方法 flash ide 会报错,因而通常
用 nc.client 属性来定义回调函数所在的位置。比如我用 data_model 类来组合
nc,那么就是 nc.client=this; public function callBackMethod(val){}便
没有问题。
I feel like I'm really close to getting this.. but missing something.
In flash:
myObj={one:"hello",two:"world"};
nc.connect("rtmp://my.red5.com/instance",myObj);
In red5:
13、第 一个 rso 的例 子
red5 里面怎么用 sharedObject
1>flash 端控制,
2>/ 也可以用 red 的某个事件控制 , 来触发 so 的改变事件来通知所有的 client.
第 一 种 :
conn.connect("rtmp://localhost/Presstest"); // 建 立
conn
so=SharedObject.getRemote(”d5″,nc.uri,false);// 得到
so 的引
如果是第一人这一步在 red5 生成一个 videos 的共享对象 , 其余参数 , 可以看
cs3 的 api.
so.connect(nc);//so 连 接
so.addEventListener(SyncEvent.SYNC,soSyncHandle); 为
so 增 加 同 步 信 息 出 来 函 数 soSyncHandle
so.setProperty(”x”,"111111"); // 将共享对象里面的属性值
设置为 111111, 这个 方法会触发服务器去调用所有连接了这个 so 的 client,
并 调 用 该 客 户 端 方 法 soSyncHandle.
public function soSyncHandle(e:SyncEvent):void //同步处
理 函 数
{
而服务器端共享对象是差不多的,同客户端的区别是,将改变 so 的值的步骤
so.setProperty 这一步在服务器通过事件触发来做.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
var c:NetConnection;
var myso:SharedObject;
private function init():void{
c=new NetConnection();
c.addEventListener(NetStatusEvent.NET_STATUS,sucess);
c.connect("rtmp://localhost/SOSample");
}
14、进 入房间
贴一段代码,那位高手给分析一下!
其中: if (!super.roomStart(room))
{
return false;
}
的作用是什么??
还有就是里面有个 public boolean roomStart(IScope room)
room.getName(),IScope 好像没有 getName()这个函数?老是到这就提示错误如下:
The type org.springframework.core.io.support.ResourcePatternResolver
cannot be
resolved. It is indirectly referenced from required .class files
这句的错误是什么意思???还忘高手指点!!!
俺的 QQ:43794298
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.red5.server.api.stream.IServerStream;
import org.red5.server.api.stream.IStreamCapableConnection;
import org.red5.server.api.stream.support.SimpleConnectionBWConfig;
import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IConnection;
import org.red5.server.api.IClient;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.service.IServiceCapableConnection;
import org.red5.server.api.so.ISharedObject;
import org.red5.server.api.ScopeUtils;
import org.red5.server.api.so.ISharedObjectService;
import org.red5.server.api.*;
import org.red5.server.api.scheduling.*;
import org.red5.samples.components.ClientManager;
import org.red5.server.api.service.*;
问题已基本被我给搞定啦,和我上面想的着不多,不过我要实现的东西不需要
getChildScope(SCOPE_HALL).disconnect(conn); // leave hall
boolean ok = conn.connect(roomScope); // connect to the room
这 个 功 能 是 退 出 大 厅 , 后 进 入 新 的 房 间 , 而 我 是 直 接 IScope roomScope =
getChildScope(roomName);不用经过大厅,呵,东西终于被给做出来啦,累死俺啦!
级访问,区域对象的共享的功能 .那么,对于一个作用域对象它可以有父作用域对象 ,
也可以有子作用域对象.如果一个客户端连接到了一个作用域
下 面 是 作 用 域 所 有 的 名 称 :application,room,place,lobby.
下 面 简 单 介 绍 一 下 IScope 接 口 的 方 法 :
boolean addChildScope(IBasicScope scope)
描述:添加一个子作用域 .如果添加成功返回 True, 如果添加的子作用域已经是该作
用 域 的 子 作 用 域 , 那 么 返 回 False.
参 数 :scope 一 个 子 作 用 域 对 象 .
返回值:True 添加成功,False 添加失败,添加的子作用域已经是该作用域的子作用域 .
Set<IClient> getClients()
描述 : 返回当前作用域对 象 中 所 有 子 作 用 域 对 象 的 范 型 集 合 .
返 回 值 : 所 有 子 作 用 域 对 象 的 范 型 集 合 .
Iterator<IConnection> getConnections()
描 述 : 获 得 本 作 用 域 所 有 连 接 对 象 的 范 型 迭 代 器 .
返 回 值 : 连 接 对 象 迭 代 器 .
IContext getContext()
描 述 : 返 回 本 作 用 域 上 下 文 环 境 .
返 回 值 : 返 回 本 作 用 域 上 文 对 象 .
String getContextPath()
描 述 : 返 回 上 下 文 路 径 .
返 回 值 : 上 下 文 路 径 .
IScopeHandler getHandler()
描 述 : 获 得 该 作 用 域 对 象 的 控 制 器 对 象 .
返 回 值 : 作 用 域 的 控 制 器 对 象 .
boolean hasHandler()
描 述 : 判 断 该 作 用 域 是 否 存 在 控 制 器 .
返 回 值 : 存 在 返 回 True, 反 之 返 回 False.
16、 kikachat 中的 事件
在 appconnect….join,start 等 RED5 系统事件,都需要在 myconnct 中注册回
调函数,并且定义派发。
四 . 幾 種 可 能 的 互 動 型 態 範 例
四 . 幾 種 可 能 的 互 動 型 態 範 例
Server 端 :
application.onConnect=function(newClient , pwd){
if(pwd=="ok"){
application.acceptConnection(newClient);
newClient.call("get_message" , null , message);
}else{
application.rejectConnection(newClient , errObj);
}
}
Client 端 :
........
nc.get_message=function(message){};
增 加 一 個 廣 播 的 方 法
Server 端 傳 送 給 有 getRemote 同 一 個 ShareObject 的 Client 端
Server 端 :
application.abc_so = SharedObject.get("abc_so", false);
application.abc_so.send("msgFromSrvr", msg);
Client 端 :
abc_so = SharedObject.getRemote("abc_so", abc_nc.uri, false);
abc_so.msgFromSrvr = function(msg) {
showMsg(msg);
};
服务器端:
Client.call
用法
Client.call(methodName, [resultObj, [p1, ..., pN]])
在发送的客户端或另一个服务器上执行一个方法。这个方法可以任意的返回数据,
返回的数据作为结果传递到 resultObj 参数中去。
NetConnection.call
用法:
myNetConnection.call(methodName, [resultObj, p1, ..., pN])
调用一个 Flash Communication Server 或者其他应用服务器上的命令或方法。
用法和客户端的 NetConnection.call 的用法一样。他调用一个远程服务器上的
方法。
3 个 Send,客户端有 2 个,服务器端有 1 个。
客户端:
NetStream.send
用法:
myStream.send(handlerName [,p1, ...,pN])
对所有请求某个指定流数据的客户端机器广播一个消息。这个方法只能用在发布
这个流数据的客户端。为了处理和响应这个消息,需要建立一个句柄,格式是
myStream.HandlerName。
SharedObject.send
用法:
myRemoteSharedObject.send(handlerName [,p1, ...,pN])
一种方法,把一个消息广播到所有连接到 myRemoteSharedObject 上的客户端,
包括发送消息的客户机。为了处理并相应这个消息,建立一个名称为
handlerName 的函数绑定相应的 SharedObject 上。
服务器端:
SharedObject.send
用法:
SharedObject.send(methodName, [p1, ..., pN])
执行客户端上的一个方法。可以利用 SharedObject.send 异步的执行所有连接
到 SharedObject 上的客户机上的一个方法。不管成功、失败还是返回消息的响
应值,服务器都不会接受客户机的信息。
19、再说说 onSync,SharedObject
* 最多人不懂的就是:那个 li st 参数
看代码:
name 描述被改变的属性名
说白了这个插槽信息对象大概就是这么个样子:
{name:"x",code:"success"}
表示 x 属性被修改成功
list[i] 就是插槽信息对象
if(list[i].code=="delete") trace("list[i].name"+被+"delete")
“change”是啥?“delete”是啥?
change : 表示 so 的插槽被别人修改,或填加
reject : 拒绝修改
例如发生在两个或多个客户端同时要修改一个 so 的插槽,这时候 fms 会只让一
个 client 修改,并返回"success" 其他的会收到"reject"
比如服务器端删除某个 so
so = SharedObject.get("某个 so");
so.lock( );
var names = so.getPropertyNames( );
for (i in names) {
so.setProperty(names[i], null);
}
so.unlock( );
然而这样:
so = SharedObject.get("某个 so");
so.clear( );
完。
20、视频播 放流程
1. 建 立 NetConnection 对 象
var nc:NetConnection=new NetConnection()
2. 建 立 NC 连 接
nc.connect("rtmp://xxxxx");// 注 意 你 的 代 码 里 写 成 了 rtmp:/ , 少 了 一 个 /
如 果 是 直 接 通 过 HTTP 方 式 播 放 , 则 此 处 应 为
nc.connection(null);
3. 建 立 NetStream
var ns:NetStream=new NetStream(nc);
注 意 这 里 有 一 个 必 须 参 数 nc
4. 连 接 源
xxVideo.attachVideo(ns);
5. 播 放
ns.play("xxxx");
具体代码:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" applicationComplete="init()">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
public function init():void
{
var nc:NetConnection=new
NetConnection();
nc.connect(null);
var ns:NetStream=new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR,
asyncErrorHandler);
var screen:UIComponent=new UIComponent();
screen.setActualSize(800,600);
var myVideo:Video=new Video(320,240);
myVideo.attachNetStream(ns);
screen.addChild(myVideo);
addChild(screen);
ns.play("http://xxx/video.flv");
}
}
]]>
</mx:Script>
</mx:Application>
需要把 Video 加入一个 UIComponent 里才可以 addChild,在我机器上测试通过了:
特 别 要 声 明 , 这 里 需 要 为 ns 增 加 事 件 侦 听 器
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);哪怕
只是个空函数也要增加。否则会报错(但不影响视频播放)