Flex 迷你教程 — 基于Stratus的P2P网络电话 (2)

Posted by Kevin Luo at 10 January 2009

Category: FMS, Flex 迷你教程, P2P

Tags: , , , ,

接上篇,这一讲里我告诉大家如何链接另一个flash客户端,并且发送文字信息,在下一讲中会引入更多的内容,比如,“是否接收对方呼叫”,“语音和视频的发送” 等等。

看这篇教程时一定要把自己分成“呼叫者”和“被呼叫者”两个身份来看,否者有可能会看得头晕,呵呵。现在我们开始。

1. 首先了解一下我们会用到的变量, 这里要最主要的四个NetStream,他们的作用请看注释。

?View Code ACTIONSCRIPT3
//链接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) :

操作方法:

  1. 任意填写用户名,点击链接。
  2. 在另一个浏览器同样登陆。
  3. 用其中一个的peerid作为链接ID呼叫对方。
  4. 链接成功后可以信息聊天。

4. 源代码下载与主要方法代码,学习请看注释,写的很清楚

输入用户名,点击 “链接”时触发下面的代码

?View Code ACTIONSCRIPT3
//链接 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”里调用下面的方法

?View Code ACTIONSCRIPT3
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,点击呼叫后执行下面的代码

?View Code ACTIONSCRIPT3
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
 
}

被呼叫者在收到呼叫时执行

?View Code ACTIONSCRIPT3
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;
}

呼叫者与被呼叫者链接成功后执行

?View Code ACTIONSCRIPT3
//监听onConnectSuccess事件,确定链接成功
i.onConnectSuccess = function(name:String):void
{
    info.text += "与"+name + "链接成功\n";
}

发送信息与接收信息代码

?View Code ACTIONSCRIPT3
//用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)

76 Comments

  1. ligan says

    pretty things!

    Reply

  2. dsddd says

    fdsafsaf

    Reply

  3. cool says

    看看

    Reply

  4. jubupx says

    一个简单的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:

    这个实现很简单,代码也很短,我写教程的目的是告诉大家每行代码的意思

    Reply

    tianzhu Reply:

    你有毛病啊,我觉得写的很好啊,我很菜的,不像你这种空心大萝卜,萝卜仔

    Reply

    cooerson Reply:

    我也觉得,能把代码写得这么复杂,牛

    Reply

  5. mklp says

    多方通话是否可以实现呢?

    Reply

  6. zz says

    这里面有插件吗?

    Reply

  7. zhuzhu says

    写的很好啊,博主技术蛮厉害啊

    Reply

    Kevin Reply:

    谢谢:)

    Reply

  8. sloppy says

    See See

    Reply

  9. JEFF says

    谢谢:)

    Reply

    Reply

  10. 得失 says

    看代码好像并不是很复杂,只是p2p很值得关注一下

    Reply

    Kevin Reply:

    关注P2P的话你一定要关注Flash Player 10.1

    Reply

  11. Sakuya says

    多谢前辈

    Reply

  12. linhan says

    flash支持P2P,以后局域网电影共享、文件互传应该没有问题

    Reply

  13. lingxia1du says

    多谢……

    Reply

  14. Gluttony says

    写得很好,不过我在实际使用中遇到了一些问题。
    下载的官方代码中有个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:

    官方的版本中有一个python写的服务端,用于保存你的用户名等等的东西。所以使用他需要在你的机器上装一个python的环境才能运行。 我的这个和他的一样,我把python的东西去掉了。这个服务器的作用是用来做用户名和peerid之间的映射

    Reply

    Gluttony Reply:

    果然是这样的,谢谢了,我还是研究你的代码吧

    Reply

  15. luming says

    再次请教Kevin一个问题,经过测试peerID应该是Adobe服务器随机返回的唯一的链接标示,那么问题也随之而来了,如果是一个IM系统采用Stratus服务器进行点对点的通信,每次发起会话之前都要也就是必须发起者(A)与接受者(B)都连接Adobe服务器了。
    那么A如何去获得B每次不同的随机peerID呢?毕竟ID每次都是不同的呀。
    等待您的解答了,谢谢。

    Reply

    Kevin Luo Reply:

    是的,每次 peerid会不一样,所以你还需要自己写一个服务器来做peerid的配对,a通过你的服务器找到b的peerid

    Reply

Leave a Reply

Leave a Reply
  • (required)
  • (required) (will not be published)