谈Flash客户端与C++后台的几种通信方法
2013-10-23 17:02
441 查看
一,Flash 8以前时代:
使用AS2 API根据fscommand,以及SetVariable实现双工简单通信。当时这些小儿科的东西,我玩的不亦乐乎,还用在学校学生实验平台上。简单明了的SetVariable方法在AS3里面不能用了。特点:通信量小。Flash给C++传递参数:
fscommand("MsgBox", "helloword");C++获取Flash传递的参数通过添加FSCommand消息函数来实现:
void CCppFlashDlg::FSCommandShockwaveflash1(LPCTSTR command, LPCTSTR args) { if (0 == strcmp("MsgBox", command)) { MessageBox(args); } }
二,Flash 9以上AS3的ExternalInterface:
通过AS3的ExternalInterface调用C++中的函数,C++端通过Flash Player ActiveX控件中接口CallFunction调用AS3中的函数,值得注意的是,通信字符串格式都是XML格式,需要对XML文档进行解析。C++类的XML解析库有CMarkup和tinyXML。刚会了这些内容,我就用在了工业测控程序上,效果不错。特点:通信量小。C++调用Flash的函数必须先在Flash客户端里面注册,方法如下:
import mx.controls.Alert; import flash.external.*; ExternalInterface.addCallback("MsgBox2", this, MsgBox2); function MsgBox2(msg:String) { Alert.show(msg); }注册好以后VC即可调用Flash中的函数了,慢,不要着急,调用格式是XML的,所以需要多多学习XML文档的解析与生成:
void CCppFlashDlg::OnOK() { swfUI.CallFunction("/ <invoke name=/"MsgBox2/">/ <arguments>/ <string>Hi</string>/ </arguments>/ </invoke>"); }
三,通过Socket通信
建立Socket连接,需要特别处理Flash的安全策略文件。特点:通信量大,可以远程交互。安全策略文件参考Flex Socket 与 C++ 通讯 --- 安全沙箱问题解决
其代码如下:#include <winsock2.h>
#include <windows.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
short port=1800;//端口号
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );//初始化套接字
if ( err != 0 )
{
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup( );
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字
SOCKET sockConn;//用来和客户端通信的套接字
SOCKADDR_IN addrSrv;//用来和客户端通信的套接字地址
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(port);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//绑定端口
listen(sockSrv,5);//侦听
printf("Server %d is listening....../n",port);
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
char buf[4096];//接收的数据
char rbuf[100]=
"<cross-domain-policy> "
"<allow-access-from domain=/"*/" to-ports=/"*/"/>"
"</cross-domain-policy> ";//套接字策略文件
while(1)
{
//接受连接
sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
printf("Accept connection from %s/n",inet_ntoa(addrClient.sin_addr));
recv:
//接收数据
int bytes;
if((bytes=recv(sockConn,buf,sizeof(buf),0))==SOCKET_ERROR)
{
printf("接收数据失败!/n");
exit(-1);
}
buf[bytes]='/0';
printf("Message from %s: %s/n",inet_ntoa(addrClient.sin_addr),buf);
if (0 == strcmp(buf,"<policy-file-request/>"))
{
//发送数据
if(send(sockConn,rbuf,strlen(rbuf)+1,0)==SOCKET_ERROR)
{
printf("发送数据失败!");
exit(-1);
}
printf("Message to %s: %s/n",inet_ntoa(addrClient.sin_addr),rbuf);
}
else
{
//Echo
if(send(sockConn,buf,strlen(buf)+1,0)==SOCKET_ERROR)
{
printf("发送数据失败!");
exit(-1);
}
printf("Message to %s: %s/n",inet_ntoa(addrClient.sin_addr),buf);
goto recv;
}
//清理套接字占用的资源
closesocket(sockConn);
}
}
四,通过LocalConnection实现AS3与C++交互:
Flash的LocalConnection是基于内存文件读写实现的。在C++客户端开启线程监视内存文件,获取Flash建立的LocalConnection,解析其发送的数据,需要了解Adobe的AMF格式。特点:限于一台电脑上通信。假设仿真类已完成,并定义了消息:
#define WM_LOCAL_CONN_MESSAGE_IN WM_USER+7 // A character was received and placed in the input buffer. 当线程检测到Flash发送消息时 ,该仿真类派发该消息:
::SendMessage(pOwner->m_hWnd, WM_LOCAL_CONN_MESSAGE_IN, (WPARAM) msgRcv, (LPARAM) len);
在对话框类定义LocalConnectionEmulator实例,监测Flash的消息发送:
protected: LocalConnEmulator lcSim;启动LocalConnection仿真程序:
lcSim.InitEmulator(this); lcSim.StartEmulator();Flash客户端发送的信息的获取是通过仿真程序派发自定义事件捕获得到,定义自定义消息函数:
// Generated message map functions virtual BOOL OnInitDialog(); afx_msg LONG OnMessageIn(WPARAM arrayPtr, LPARAM len); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP()添加消息映射:
BEGIN_MESSAGE_MAP(CCppLocalConnDlg, CDialogEx) ON_MESSAGE(WM_LOCAL_CONN_MESSAGE_IN, OnMessageIn) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON1, &CCppLocalConnDlg::OnBnClickedButton1) END_MESSAGE_MAP()实现自定义消息函数:
LONG CCppLocalConnDlg::OnMessageIn(WPARAM arrayPtr, LPARAM len) { char msgRcv[256]; memcpy(msgRcv,(char*)arrayPtr,len); msgRcv[len]=0; this->SetWindowText(msgRcv); return true; }
五,开源软件实现RTMP Server:
诸如CRTMP Server,openRTMFP。特点:很强大,易于实现音视频聊天类应用。总体而言CRTMP Server还支持远程共享对象的,功能更加强大,而且内存和CPU资源占用极小,一句话,好的没法说,但是openRTMFP可以通过lua脚本配置服务器,实现与Adobe Flash Media的类似脚本,也相当不错,这一点由于crtmpServer。如下为crtmpServer实现视频直播的demo,通过服务器直播摄像头内容(我使用虚拟摄像头,就是专门由来视频骗别人借钱汇款之类的软件),客户端为Adobe AS3API 文档中的例子:
package
{
import flash.display.Sprite;
import flash.events.*;
import flash.media.Video;
import flash.media.Camera;
import flash.net.NetConnection;
import flash.net.NetStream;
import fl.controls.Button;
import fl.controls.Label;
public class NetStream_publish extends Sprite
{
private var connectionURL:String = "rtmp://127.0.0.1/live/";
private var videoURL:String = "liveVideo";
private var nc:NetConnection;
private var ns_publish:NetStream;
private var ns_playback:NetStream;
private var video_publish:Video;
private var video_playback:Video;
private var cam:Camera;
private var b:Button;
private var l:Label;
public function NetStream_publish() {
setUpUI();
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
// Add bandwidth detection handlers on the NetConnection Client to
// prevent Reference Errors at runtime when using the "live" and "vod" applications.
var clientObj:Object = new Object();
clientObj.onBWDone = onBWDone;
clientObj.onBWCheck = onBWCheck;
nc.client = clientObj;
// Connect to the "live" application on Flash Media Server.
nc.connect(connectionURL);
}
private function netStatusHandler(event:NetStatusEvent):void {
trace(event.info.code + " | " + event.info.description);
switch (event.info.code) {
case "NetConnection.Connect.Success":
// Enable the "Publish" button after the client connects to the server.
b.enabled = true;
break;
case "NetStream.Publish.Start":
playbackVideo();
break;
}
}
private function publishVideo(event:MouseEvent):void{
// Disable the button so that you can only publish once.
b.enabled = false;
// Create a NetStream to send video to FMS.
ns_publish = new NetStream(nc);
ns_publish.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
// Publish (send) the video to FMS.
cam = Camera.getCamera();
ns_publish.attachCamera(cam);
ns_publish.publish(videoURL);
}
private function playbackVideo():void {
// Create the Video object to show the video on the stage
video_playback = new Video(cam.width, cam.height);
video_playback.x = cam.width + 20;
video_playback.y = 10;
addChild(video_playback);
// Create a NetStream to receive the video from FMS.
ns_playback = new NetStream(nc);
ns_playback.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
// Display the video that was published to FMS.
video_playback.attachNetStream(ns_playback);
ns_playback.play(videoURL);
}
private function setUpUI():void {
b = new Button();
b.addEventListener(MouseEvent.CLICK, publishVideo);
b.width = 150;
b.label = "Publish video to server";
b.move(10, 150);
b.enabled = false;
l = new Label();
l.width = 150;
l.text = "Playing back from server"
l.move(190, 150);
addChild(b);
addChild(l);
}
// Handlers called by the Flash Media Server "live" and "vod" applications.
public function onBWDone(... rest):Boolean {
return true;
}
public function onBWCheck(... rest):Number {
return 0;
}
}
}
多个客户端连接,同时实现点播视频与在线视频流播放:
如下为openRTMFP(目前尚不支持远程共享对象)的使用实例,需要配置服务器(CumulusServer.ini)及基于lua脚本语言的服务器脚本(main.lua):
;CumulusServer.ini port = 1985 udpBufferSize = 114688 keepAlivePeer = 10 keepAliveServer = 15 [logs] name=log directory=logs服务器lua脚本如下:
function onStart(path) NOTE("Application '"..path.."' started") end function onStop(path) NOTE("Application '"..path.."' stopped") end function onConnection(client, userName, meeting) client.userName = userName; client.meeting = meeting; INFO("User connected: ", client.userName , "meeting: ", client.meeting); function client:getParticipants(meeting) result = {} i = 0; for key, cur_client in cumulus.clients:pairs() do if (cur_client.meeting == meeting) then i = i+1; participant = {}; participant.userName = cur_client.userName; participant.meeting = cur_client.meeting; if cur_client.id then participant.protocol = 'rtmfp'; end participant.farID = cur_client.id; result[i] = participant; end end return result; end function client:sendMessage(meeting, from, message) for key, cur_client in cumulus.clients:pairs() do if (cur_client.meeting == meeting) then cur_client.writer:writeAMFMessage("onMessage", from, message); end end end sendParticipantUpdate(client.meeting); end function onDisconnection(client) INFO("User disconnecting: "..client.userName); sendParticipantUpdate(client.meeting); end function sendParticipantUpdate(meeting) for key, cur_client in cumulus.clients:pairs() do if (cur_client.meeting == meeting) then cur_client.writer:writeAMFMessage("participantChanged"); end end end
Flash聊天客户端软件下载:
基于openRTMFP的聊天应用
相关文章推荐
- **2.0** FLASH 与后台的几种交互方法
- flash与ASP通信的几种方法
- 使用xml--flash与ASP通信的几种方法
- 查找后台的几种方法
- Linux 技巧:让进程在后台可靠运行的几种方法
- Ajax调用后台方法的几种写法(三)UpdatePanel
- c++ 创建二维数组的几种方法
- [转]Linux 技巧:让进程在后台可靠运行的几种方法
- 定义C/C++全局变量/常量几种方法的…
- Linux 技巧:让进程在后台可靠运行的几种方法
- SSL握手通信详解及linux下c/c++ SSL Socket(另附SSL双向认证客户端代码)
- 从 int 到 string 的几种方法(C++)
- C/C++定义全局变量/常量几种方法的区别
- 获取客户端网卡MAC地址的几种方法
- php获得客户端ip的几种方法
- Socket通信——C++服务器端和Java客户端
- C++实现几种常用的时间复杂度为O(nlogn)的排序方法:归并排序、快速排序、堆排序、希尔排序
- 让进程在后台可靠运行的几种方法
- C#向客户端注册脚本的几种方法
- Linux学习笔记之<让进程在后台可靠运行的几种方法>