Flex 迷你教程 — 基于Stratus的P2P网络电话 (2)
接上篇,这一讲里我告诉大家如何链接另一个flash客户端,并且发送文字信息,在下一讲中会引入更多的内容,比如,“是否接收对方呼叫”,“语音和视频的发送” 等等。
看这篇教程时一定要把自己分成“呼叫者”和“被呼叫者”两个身份来看,否者有可能会看得头晕,呵呵。现在我们开始。
1. 首先了解一下我们会用到的变量, 这里要最主要的四个NetStream,他们的作用请看注释。
//链接Adobe stratus 服务器
private const StratusAddress: String = "rtmfp://stratus.adobe.com" ;
//Developer Key,如果没有请根据Flex 迷你教程 -- 基于Stratus的P2P网络电话 (1)中的提示申请
private const DeveloperKey: String = "xxxxxx" ;
//我们需要一个nectConnetion与stratus 服务器链接
private var netConnection: NetConnection ;
//用于对外发布自己的身份信息流
private var myStream: NetStream ;
//用于链接后对外发布自己的信息流,比如音频,视频,文字
private var outgoingStream: NetStream ;
//进入的信息流,这个流对应呼叫者的outgoingStream,用这个流尝试播放发布者发布的信息,比如音频,视频,文字
private var incomingStream: NetStream ;
//用于尝试播放被呼叫者的身份信息流myStream发送的信息,被呼叫者在发送自己的身份信息流时会监听呼叫者对myStream的播放请求
private var controlStream: NetStream ;
2. 下面是主要用到的方法。
呼叫者的主要方法/事件触发顺序,
init()
netConnectionHandler() — case “NetConnection.Connect.Success”:
initSendStream();
call();
onConnectSuccess()
onIm()
被呼叫者主要方法/事件的调用触发顺序
init()
netConnectionHandler() — case “NetConnection.Connect.Success”:
initSendStream();
onPeerConnect();
onIncomingCall();
onIm();
3. Demo (请打开两个浏览器进行Demo) :
操作方法:
任意填写用户名,点击链接。
在另一个浏览器同样登陆。
用其中一个的peerid作为链接ID呼叫对方。
链接成功后可以信息聊天。
4. 源代码下载与主要方法代码,学习请看注释,写的很清楚
输入用户名,点击 “链接”时触发下面的代码
//链接 adobe stratus 服务器
private function init ( ) : void {
if ( userName. text == "" )
{
Alert. show ( "请输入任意用户名" , "错误" )
return
}
netConnection = new NetConnection ( ) ;
netConnection. addEventListener ( NetStatusEvent . NET_STATUS , netConnectionHandler) ;
netConnection. connect ( StratusAddress + "/" + DeveloperKey) ; //服务器地址里需要开发者Key
}
与stratus链接成功后在case “NetConnection.Connect.Success”里调用下面的方法
private function initSendStream( ) : void {
//这三行表示我对外发布一个名称为netConnection.nearID的流,呼叫者通过我的nearID与我链接后,如果播放
//这个名称为netConnection.nearID的流,会出发onPeerConnect事件,这样我就知道有人链接我
myStream = new NetStream ( netConnection, NetStream . DIRECT_CONNECTIONS) ;
myStream. addEventListener ( NetStatusEvent . NET_STATUS , netStreamHandler) ;
myStream. publish ( netConnection. nearID) ;
//监听onPeerConnect事件
var o: Object = new Object ( ) ;
o. onPeerConnect = function ( subscriberStream: NetStream ) : Boolean
{
//当我收到呼叫者的链接请求后,尝试播放呼叫者的流名为"caller"的流,farID代表呼叫者的唯一ID,也就是前面提到的nearID
//我通过farID找到呼叫者
incomingStream = new NetStream ( netConnection, subscriberStream. farID) ;
incomingStream. addEventListener ( NetStatusEvent . NET_STATUS , incomingStreamHandler) ;
incomingStream. play ( "caller" ) ;
//监听onIm事件,用于收取文字信息
var i: Object = new Object ;
i. onIm = function ( name : String , value : String ) : void
{
info . text + = name + ": " + value + "\n " ;
}
//监听onIncomingCall事件,用于确定链接成功
i. onIncomingCall = function ( name : String ) : void
{
//显示链接成功后,对呼叫者发布我的信息流,名称为callee
info . text + = name + " 已经与你链接\n " ;
outgoingStream = new NetStream ( netConnection, NetStream . DIRECT_CONNECTIONS) ;
outgoingStream. addEventListener ( NetStatusEvent . NET_STATUS , outgoingStreamHandler) ;
outgoingStream. publish ( "callee" ) ;
outgoingStream. send ( "onConnectSuccess" , userName. text ) ;
}
incomingStream. client = i;
return true ;
}
myStream. client = o;
}
呼叫者输入对方Peerid,点击呼叫后执行下面的代码
private function call ( ) : void {
//通过对方的peerId链接被呼叫者,播放对方的身份信息流
controlStream = new NetStream ( netConnection, peerId. text ) ;
controlStream. addEventListener ( NetStatusEvent . NET_STATUS , netStreamHandler) ;
controlStream. play ( peerId. text ) ;
//同时对外发布呼叫者的信息流
outgoingStream = new NetStream ( netConnection, NetStream . DIRECT_CONNECTIONS) ;
outgoingStream. addEventListener ( NetStatusEvent . NET_STATUS , outgoingStreamHandler) ;
outgoingStream. publish ( "caller" ) ;
//尝试播放被呼叫者的信息流
incomingStream = new NetStream ( netConnection, peerId. text ) ;
incomingStream. addEventListener ( NetStatusEvent . NET_STATUS , incomingStreamHandler) ;
incomingStream. play ( "callee" ) ;
info . text + = "正在呼叫,请稍候...... \n \n "
//监听信息发布事件
var i: Object = new Object ;
i. onIm = function ( name : String , value : String ) : void
{
info . text + = name + ": " + value + "\n " ;
}
//监听onConnectSuccess事件,确定链接成功
i. onConnectSuccess = function ( name : String ) : void
{
info . text + = "与" + name + "链接成功\n " ;
}
incomingStream. client = i
}
被呼叫者在收到呼叫时执行
o. onPeerConnect = function ( subscriberStream: NetStream ) : Boolean
{
//当我收到呼叫者的链接请求后,尝试播放呼叫者的流名为"caller"的流,farID代表呼叫者的唯一ID,也就是前面提到的nearID
//我通过farID找到呼叫者
incomingStream = new NetStream ( netConnection, subscriberStream. farID) ;
incomingStream. addEventListener ( NetStatusEvent . NET_STATUS , incomingStreamHandler) ;
incomingStream. play ( "caller" ) ;
//监听onIm事件,用于收取文字信息
var i: Object = new Object ;
i. onIm = function ( name : String , value : String ) : void
{
info . text + = name + ": " + value + "\n " ;
}
//监听onIncomingCall事件,用于确定链接成功
i. onIncomingCall = function ( name : String ) : void
{
//显示链接成功后,对呼叫者发布我的信息流,名称为callee
info . text + = name + " 已经与你链接\n " ;
outgoingStream = new NetStream ( netConnection, NetStream . DIRECT_CONNECTIONS) ;
outgoingStream. addEventListener ( NetStatusEvent . NET_STATUS , outgoingStreamHandler) ;
outgoingStream. publish ( "callee" ) ;
outgoingStream. send ( "onConnectSuccess" , userName. text ) ;
}
incomingStream. client = i;
return true ;
}
呼叫者与被呼叫者链接成功后执行
//监听onConnectSuccess事件,确定链接成功
i. onConnectSuccess = function ( name : String ) : void
{
info . text + = "与" + name + "链接成功\n " ;
}
发送信息与接收信息代码
//用outgoingStream对外发布信息流
private function send ( ) : void {
info . text + = userName. text + ": " + message . text + "\n " ;
outgoingStream. send ( "onIm" , userName. text , message . text ) ;
}
//监听信息发布事件
var i: Object = new Object ;
i. onIm = function ( name : String , value : String ) : void
{
info . text + = name + ": " + value + "\n " ;
}
源代码
P2PhoneDemo1 (878)
pretty things!
Reply
fdsafsaf
Reply
看看
Reply
一个简单的CHAT也被你整个长篇大论出来了
private var is_host:Boolean = true;
private function initSendStream():void
{
myStream = new NetStream(netConnection, NetStream.DIRECT_CONNECTIONS);
myStream.addEventListener(NetStatusEvent.NET_STATUS, netStreamHandler);
myStream.publish(netConnection.nearID);
//监听onPeerConnect事件
var o:Object = new Object();
o.onPeerConnect = function(subscriberStream:NetStream):Boolean
{
if (!is_host)
return false;
toClientStream = subscriberStream;
toClientStream.client =
{
onClient:function(txt:String):void
{
info.text += “ccccc=>:” + txt + “\n”;
}
}
info.text += “有用户连接\n”;
toClientStream.send(”onHostData”, “连接成功”);
return true;
}
myStream.client = o;
}
private function netStreamHandler(e:NetStatusEvent):void
{
trace(e.info.code);
}
private function call():void
{
is_host = false;
//通过对方的peerId链接被呼叫者,播放对方的身份信息流
fromHostStream = new NetStream(netConnection,peerId.text);
fromHostStream.addEventListener(NetStatusEvent.NET_STATUS, netStreamHandler);
fromHostStream.play(peerId.text);//告诉FMS我要连接peerId.text??
var o:Object = { };
o.onHostData=function(txt:String):void
{
info.text+=”onHostData:”+txt+”\n”;
}
fromHostStream.client=o;
}
private function send():void
{
if (is_host)
toClientStream.send(”onHostData”, message.text+” “+JString.formatTime(new Date));
else
fromHostStream.send(”onClient”, message.text+” “+JString.formatTime(new Date));
}
Reply
Kevin Reply:
March 21st, 2009 at 10:28 PM
这个实现很简单,代码也很短,我写教程的目的是告诉大家每行代码的意思
Reply
tianzhu Reply:
August 29th, 2009 at 10:42 AM
你有毛病啊,我觉得写的很好啊,我很菜的,不像你这种空心大萝卜,萝卜仔
Reply
cooerson Reply:
January 14th, 2010 at 1:34 PM
我也觉得,能把代码写得这么复杂,牛
Reply
多方通话是否可以实现呢?
Reply
这里面有插件吗?
Reply
写的很好啊,博主技术蛮厉害啊
Reply
Kevin Reply:
August 4th, 2009 at 2:53 PM
谢谢:)
Reply
See See
Reply
谢谢:)
Reply
Reply
看代码好像并不是很复杂,只是p2p很值得关注一下
Reply
Kevin Reply:
November 18th, 2009 at 10:40 PM
关注P2P的话你一定要关注Flash Player 10.1
Reply
多谢前辈
Reply
flash支持P2P,以后局域网电影共享、文件互传应该没有问题
Reply
多谢……
Reply
写得很好,不过我在实际使用中遇到了一些问题。
下载的官方代码中有个readme文档,里面要求“Specify the URL of your web service in WebServiceUrl constant in VideoPhoneLabs.mxml”,是不是要建立一个web service呢,怎么建立?
还有后面提到“The Python script should be placed in the cgi-bin location according to your web server installation. The database is an SQLite3 database. In reg.cgi, please edit the location of the database in variable dbFile. ”是不是还要建一个数据库?
我是个新手,希望不吝赐教。谢谢!
Reply
Kevin Luo Reply:
January 28th, 2010 at 10:44 AM
官方的版本中有一个python写的服务端,用于保存你的用户名等等的东西。所以使用他需要在你的机器上装一个python的环境才能运行。 我的这个和他的一样,我把python的东西去掉了。这个服务器的作用是用来做用户名和peerid之间的映射
Reply
Gluttony Reply:
January 28th, 2010 at 10:50 AM
果然是这样的,谢谢了,我还是研究你的代码吧
Reply
再次请教Kevin一个问题,经过测试peerID应该是Adobe服务器随机返回的唯一的链接标示,那么问题也随之而来了,如果是一个IM系统采用Stratus服务器进行点对点的通信,每次发起会话之前都要也就是必须发起者(A)与接受者(B)都连接Adobe服务器了。
那么A如何去获得B每次不同的随机peerID呢?毕竟ID每次都是不同的呀。
等待您的解答了,谢谢。
Reply
Kevin Luo Reply:
March 9th, 2010 at 9:53 AM
是的,每次 peerid会不一样,所以你还需要自己写一个服务器来做peerid的配对,a通过你的服务器找到b的peerid
Reply