您的位置:首页 > 编程语言 > C语言/C++

谈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的聊天应用



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++