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 (1378)

108 Comments

  1. Michael says

    呵,终于找到了,正是我现在所需要的,谢谢

    Reply

  2. Frozen_Space says

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

    Reply

    mrwiis Reply:

    nop,you can`t make multipoint communication~

    Reply

  3. paul says

    很好的东西,谢谢

    Reply

  4. mengjie says

    学习

    Reply

  5. simon says

    学习。。。

    Reply

  6. dcccx says

    学习了~

    Reply

  7. epopos says

    非常不错

    Reply

  8. gambling casino says

    Nice site really!

    Reply

  9. vanarsenal says

    look so good

    Reply

  10. qq124294272 says

    真是好文章,但博主能不能画个流程图出来啊,看你的代码不好理解

    Reply

  11. ffdsa says

    很强大的P2P~

    Reply

  12. cs says

    very good article

    Reply

  13. tenchiwang says

    我來學習學習~感謝~

    Reply

  14. 123 says

    嘿嘿 支持!

    Reply

  15. hi says

    我來學習學習~感謝~

    Reply

  16. neike says

    很强大啊,可不可以用来做在线客服呢?我要一人对多人的那种,就像是网上跳艳舞的那种,一人跳,多人看哈哈

    Reply

    Kevin Luo Reply:

    完全可以

    Reply

  17. billleo says

    学习了,谢谢!

    Reply

  18. mawt says

    看看

    Reply

  19. yushunwu says

    太好了,真棒.

    Reply

  20. luoke says

    cool!

    Reply

  21. lintan says

    正是我想要的.

    Reply

  22. sherry says

    你人真好

    Reply

  23. madman says

    hao

    Reply

  24. waychu says

    谢谢版主,实用的东西.

    Reply

  25. jialong says

    学习一下!

    Reply

  26. ddddd says

    正想学这方面的东西

    Reply

  27. Lex says

    找了很久了的东西

    Reply

  28. dd says

    good

    Reply

  29. madman says

    good

    Reply

  30. 雪碧 says

    不错

    Reply

  31. happy says

    我要学习一下。

    Reply

  32. dnvwf says

    GOOD

    Reply

  33. 玩转VIP says

    不错,支持楼主

    Reply

  34. root says

    耶。。。。。。。。。well

    Reply

  35. pepsicool6 says

    good!

    Reply

  36. body says

    非常帮的程序,谢谢

    Reply

  37. 乐河 says

    我也要一个源码,正在找这个呢,呵呵呵,谢谢你

    Reply

  38. flasher says

    非常不错~Flash p2p

    Reply

  39. ok says

    非常感谢

    Reply

  40. mx says

    s很好

    Reply

  41. nero says

    写的不错

    Reply

  42. ken says

    看看代码

    Reply

  43. kh says

    一年前想过这个东西,但是没有搞成功

    Reply

  44. kk says

    看看

    Reply

  45. RAINDY says

    好东西

    Reply

  46. TAO says

    很好很强大.

    Reply

  47. 风之神 says

    不错啊

    Reply

  48. kevin says

    非常感谢

    Reply

  49. Hao says

    请问下,我的怎么一直提示少了p2pPhoneDemo2.html,这个文件,我是从上面网页下载的啊。

    Reply

    Kevin Luo Reply:

    你做Project -> Clean一次。会重新生成html文件

    Reply

  50. yulin says

    很不错啊。 学习了。

    Reply

  51. ligan says

    pretty things!

    Reply

  52. dsddd says

    fdsafsaf

    Reply

  53. cool says

    看看

    Reply

  54. 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

  55. mklp says

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

    Reply

  56. zz says

    这里面有插件吗?

    Reply

  57. zhuzhu says

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

    Reply

    Kevin Reply:

    谢谢:)

    Reply

  58. sloppy says

    See See

    Reply

  59. JEFF says

    谢谢:)

    Reply

    Reply

  60. 得失 says

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

    Reply

    Kevin Reply:

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

    Reply

  61. Sakuya says

    多谢前辈

    Reply

  62. linhan says

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

    Reply

  63. lingxia1du says

    多谢……

    Reply

  64. 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

  65. luming says

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

    Reply

    Kevin Luo Reply:

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

    Reply

    seamonkey Reply:

    謝謝版大細心分享
    我有個問題~
    我測試了一下~發現同一台電腦開兩個視窗 RUN它沒問題~可以溝通到
    但是我請在不同地方的朋友連它 我們彼此就無法溝通到了
    我以為Stratus的方法不會遇到這樣的狀況…..
    請問是哪邊可能出了問題??

    Reply

    Kevin Luo Reply:

    很有可能是你们双方其中一方的防火墙引起的。防火墙需要打开允许UDP的限制。

    Reply

    seamonkey Reply:

    恩~我們剛剛又測試了
    http://labs.adobe.com/technologies/stratus/samples/
    這個官方的版本 發現可以連線成功~也可以對話
    所以應該可以排除掉UDP的限制問題~
    我瀏覽了一下官方的程式碼
    版大的程式碼好像是精簡掉mic跟video的功能
    然後把peerID配對的改成人工輸入~
    那為什麼…這裡卻無法兩台電腦互連呢….
    真的很謝謝版大那麼快回我~~讓我有解決的方向OTZ
    謝謝

    seamonkey Reply:

    我發現可能是我朋友公司的防火牆有檔連線~~
    因為我跟另一位朋友連線成功了!!
    ADOBE官網那之程式大概有設在允許範圍內吧
    所以才能連線成功~
    謝謝版大~~

    Reply

    Kevin Luo Reply:

    呵呵,不客气

  66. Flex基于Stratus的P2P教程一 « Flex玩手 says

    [...] 安装好上面的东西,然后等我明天来写  “基于Stratus的P2P网络电话 (2)”  [...]

  67. IAN says

    这个例子在局域网可以,为什么放到互联网就不能连接上了??请指教

    Reply

    Kevin Luo Reply:

    互联网上可以用的,不过如果防火墙禁止UPD包的话,会被阻隔,清检查下防火墙设置

    Reply

  68. KillerNB says

    adobe stratus 申请的key连接数有限制吗?

    Reply

    Kevin Luo Reply:

    没有限制

    Reply

  69. 发烧友 says

    一对多怎么弄呀?

    Reply

  70. forestking says

    你好。我是个新手,我按照你的步骤坐下来,可是运行时发现了12个错误,但是错误类型只有两类。
    错误提示:参数个数不正确,应该不超过1个。
    myStream = new NetStream(netConnection, NetStream.DIRECT_CONNECTIONS);

    访问肯能未定义属性farID,nearID,DIRECT_CONNECTIONS。

    incomingStream = new NetStream(netConnection,subscriberStream.farID);
    myStream.publish(netConnection.nearID);
    outgoingStream = new NetStream(netConnection, NetStream.DIRECT_CONNECTIONS);

    Reply

  71. forestking says

    你好。我是个新手,我按照你的步骤坐下来,可是运行时发现了12个错误,但是错误类型只有两类。能不能帮忙解决下啊,不胜感激。
    错误提示:参数个数不正确,应该不超过1个。
    myStream = new NetStream(netConnection, NetStream.DIRECT_CONNECTIONS);

    访问肯能未定义属性farID,nearID,DIRECT_CONNECTIONS。

    incomingStream = new NetStream(netConnection,subscriberStream.farID);
    myStream.publish(netConnection.nearID);
    outgoingStream = new NetStream(netConnection, NetStream.DIRECT_CONNECTIONS);

    Reply

    Kevin Luo Reply:

    你的开发环境不对,建议看看第一篇文章。 你需要flexSDK 3.2以上,以及flash player10.0.0的最低编译设置

    Reply

    forestking Reply:

    unable to open ‘C:\Programe Fiies\Adobe\Flex builder 3\sdks\3.1.0\frameworks\libs\player\10\playerglobal.swc’是不是也是版本问题啊,难道说我的sdk的版本是3.1?可是我按照你第一篇文章里的地址下载了sdk版本是4.1了。我安装有误么。实在是很感谢你,我其实还真没想到你会恢复我,真是谢谢你。编译设置我改成10.0.0后就只有上面的问题了

    Reply

  72. forestking says

    unable to open ‘C:\Programe Fiies\Adobe\Flex builder 3\sdks\3.1.0\frameworks\libs\player\10\playerglobal.swc’是不是也是版本问题啊,难道说我的sdk的版本是3.1?可是我按照你第一篇文章里的地址下载了sdk版本是4.1了。我安装有误么。实在是很感谢你,我其实还真没想到你会恢复我,真是谢谢你。编译设置我改成10.0.0后就只有上面的问题了(真的很谢谢你的帮助,谢谢)

    Reply

    forestking Reply:

    上面的问题解决了,sdk的版本是4.1的,然后编译设置10:0:0了,但是人还有一个错误,和一个警告出现了,解决不了啊。希望你帮忙一下,拜托你解决下,再次感谢哈
    下面是错误提示:
    An internal buid error has occurred.Right-click for more information
    下面是警告提示:
    Design mode:Cannot load textLayout.swc(reason:ERROR:load Verify).It may require classes(such as Adobe AIR conponents)that are not supported by design mode.Check the Eclipse error log more details.

    Reply

    forestking Reply:

    上面的问题解决了,sdk的版本是4.1的,然后编译设置10:0:0了,但是还有一个错误,和一个警告出现了,解决不了啊。希望你帮忙了,拜托你解决下哈,再次感谢
    下面是错误提示:
    An internal buid error has occurred.Right-click for more information
    下面是警告提示:
    Design mode:Cannot load textLayout.swc(reason:ERROR:load Verify).It may require classes(such as Adobe AIR conponents)that are not supported by design mode.Check the Eclipse error log more details.

    Reply

  73. Robin says

    博主你好!小弟学习了你的代码,有些地方不明白,望请指教!在下发现中间有几处貌似有语法错误,语句后没有加分号结尾,但是编译器没有报错,我试着把这些缺的分号补上,编译也没有报任何错误。但是运行却发现两个客户端连接不上了,真是奇怪啊!不知道博主能否指点一二,谢谢!
    缺分号的语句有:
    第一段代码中
    Alert.show(“请输入任意用户名”,”错误”)
    return

    第三段代码中
    info.text += “正在呼叫,请稍候…… \n\n”
    incomingStream.client = i

    Reply

    Kevin Luo Reply:

    多谢修改,不过Flex中对大多数地方对分号是不敏感的(少数情况除外),所以不会引起错误。连接不上你看看是不是你连接的是别的网络中的用户,并且他设置了防火墙禁止UPD通讯

    Reply

    Robin Reply:

    我就是在本机上开两个Flash Player测试的,一旦加上分号编译后就彼此连接不上,去掉还原后编译,就又能连接上了,屡试不爽,真是非常奇怪啊!

    Reply

    Kevin Luo Reply:

    呵呵,我也无法解释了

  74. hillkiller says

    我刚接触flash p2p没多久 想问一下 这个能进行多对多的聊天吗 就是 同时与多个人进行聊天 就是不1个peerid是否可以同时进行通信 做法 能给我讲解一下吗 (就是 怎么实现)
    麻烦大虾了~~ 教教我这个热爱flash的人

    Reply

    Robin Reply:

    这个程序不能多对多,多对多的话好像要使用NetGroup类,进行群组P2P通信

    Reply

    Kevin Luo Reply:

    这个倒是可以多对多的,不过需要自己写代码将所有的人用这个例子中的方法连接起来,脸上后类似蜘蛛网,每个人需要和所有人有连接。 也就是说每个人有一个outgoingStream和N个incomingStream可以解决。不过这种方法很不稳定。 现在的Stratus 2中的NetGroup可以实现分组连接,用以实现多对多或者一对多,因为NetGroup是以multicast的形式传播数据,所以效率和稳定都不错。你可以看我最新一个的Stratus的例子。

    Reply

    Robin Reply:

    想和博主探讨一下,如何使用P2P开发一个基于好友通信的IM程序呢?Stratus由于托管在Adobe,自己不能架设,不能使用服务器端API,这样的话要实现好友管理,及好友上下线动态显示,只能再连接一台FMS应用服务器。好友间通信使用Stratus的P2P方式,而好友管理和上下线动态显示使用传统的FMS,提供数据库支持和SharedObject来维护用户好友的在线状态。不知道博主有没有好的方法能基于P2P实现好友在线状态动态显示呢?

    Reply

    Kevin Luo Reply:

    不需要再用FMS,只需要自己的随便什么后台就可以了(PHP, JAVA, ruby, 等等),用Stratus获取peerid,保存在自己的后台。

  75. [...] (3) – 10,873 viewsCall Me — 博客视频聊天插件 – 10,724 viewsFlex 迷你教程 — 基于Stratus的P2P网络电话 (2) – 9,872 viewsAdobe Flex/AIR 迷你教程 – 8,663 views开源代码 – 7,274 viewsAdobe [...]

  76. jane says

    replica Louis Vuitton handbags is attractive in outer appearance and exquisite in inner side. There are flat handles that are adjustable, a removable padded shoulder strap, an address holder which can be stamped with baby or mother’s initials, and a key ring holder. In addition, compared with the traditional diaper bag, LV handbag has more functions, such as a change purse, a board which could print the address of the holders by hot-stamping and the designer addsan extra band on both sides, which could save labor when you carry Louis Vuitton bag on sale

    Reply

  77. James says

    拜读了

    Reply

  78. [...] 原文:http://www.flextheworld.com/2009/01/flex-stratus-phone-2.html [...]

Leave a Reply

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