使用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);
}
}
通过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("/
/
/
Hi /
/
");
}
建立Socket连接,需要特别处理Flash的安全策略文件。特点:通信量大,可以远程交互。
#include
#include
#include
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]=
" "
""
" ";//套接字策略文件
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," "))
{
//发送数据
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);
}
}
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;
}
诸如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的聊天应用