一,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(<span class="hljs-string">"User connected: "</span>, client.userName , <span class="hljs-string">"meeting: "</span>, client.meeting);
<span class="hljs-keyword">function</span> client:getParticipants(meeting)
result = {}
i = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> key, cur_client <span class="hljs-keyword">in</span> cumulus.clients:pairs() <span class="hljs-keyword">do</span>
<span class="hljs-keyword">if</span> (cur_client.meeting == meeting) <span class="hljs-keyword">then</span>
i = i+<span class="hljs-number">1</span>;
participant = {};
participant.userName = cur_client.userName;
participant.meeting = cur_client.meeting;
<span class="hljs-keyword">if</span> cur_client.id <span class="hljs-keyword">then</span>
participant.protocol = <span class="hljs-comment">'rtmfp';</span>
<span class="hljs-keyword">end</span>
participant.farID = cur_client.id;
result[i] = participant;
<span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
return result;
<span class="hljs-keyword">end</span>
<span class="hljs-keyword">function</span> client:sendMessage(meeting, from, message)
<span class="hljs-keyword">for</span> key, cur_client <span class="hljs-keyword">in</span> cumulus.clients:pairs() <span class="hljs-keyword">do</span>
<span class="hljs-keyword">if</span> (cur_client.meeting == meeting) <span class="hljs-keyword">then</span>
cur_client.writer:writeAMFMessage(<span class="hljs-string">"onMessage"</span>, from, message);
<span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
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的聊天应用