一,Flash 8以前时代:

        使用AS2 API根据fscommand,以及SetVariable实现双工简单通信。当时这些小儿科的东西,我玩的不亦乐乎,还用在学校学生实验平台上。简单明了的SetVariable方法在AS3里面不能用了。特点:通信量小。

    Flash给C++传递参数:

fscommand("MsgBox", "helloword");  
C++获取Flash传递的参数通过添加FSCommand消息函数来实现:
  1. void CCppFlashDlg::FSCommandShockwaveflash1(LPCTSTR command, LPCTSTR args)
  2. {
  3. if (0 == strcmp("MsgBox", command))
  4. {
  5. MessageBox(args);
  6. }
  7. }

二,Flash 9以上AS3的ExternalInterface:

       通过AS3的ExternalInterface调用C++中的函数,C++端通过Flash Player ActiveX控件中接口CallFunction调用AS3中的函数,值得注意的是,通信字符串格式都是XML格式,需要对XML文档进行解析。C++类的XML解析库有CMarkup和tinyXML。刚会了这些内容,我就用在了工业测控程序上,效果不错。特点:通信量小。

    C++调用Flash的函数必须先在Flash客户端里面注册,方法如下:

  1. import mx.controls.Alert;
  2. import flash.external.*;
  3. ExternalInterface.addCallback("MsgBox2", this, MsgBox2);
  4. function MsgBox2(msg:String)
  5. {
  6. Alert.show(msg);
  7. }
      注册好以后VC即可调用Flash中的函数了,慢,不要着急,调用格式是XML的,所以需要多多学习XML文档的解析与生成:

  1. void CCppFlashDlg::OnOK()
  2. {
  3. swfUI.CallFunction("/
  4. <invoke name=/"MsgBox2/">/
  5. <arguments>/
  6. <string>Hi</string>/
  7. </arguments>/
  8. </invoke>");
  9. }

三,通过Socket通信

       建立Socket连接,需要特别处理Flash的安全策略文件。特点:通信量大,可以远程交互。

安全策略文件参考Flex Socket 与 C++ 通讯 --- 安全沙箱问题解决 

     其代码如下:

  1. #include <winsock2.h>
  2. #include <windows.h>
  3. #include <iostream>
  4. using namespace std;
  5. #pragma comment(lib,"ws2_32.lib")
  6. void main()
  7. {
  8. WORD wVersionRequested;
  9. WSADATA wsaData;
  10. int err;
  11. short port=1800;//端口号
  12. wVersionRequested = MAKEWORD( 1, 1 );
  13. err = WSAStartup( wVersionRequested, &wsaData );//初始化套接字
  14. if ( err != 0 )
  15. {
  16. return;
  17. }
  18. if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
  19. {
  20. WSACleanup( );
  21. return;
  22. }
  23. SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字
  24. SOCKET sockConn;//用来和客户端通信的套接字
  25. SOCKADDR_IN addrSrv;//用来和客户端通信的套接字地址
  26. addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
  27. addrSrv.sin_family=AF_INET;
  28. addrSrv.sin_port=htons(port);
  29. bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//绑定端口
  30. listen(sockSrv,5);//侦听
  31. printf("Server %d is listening....../n",port);
  32. SOCKADDR_IN addrClient;
  33. int len=sizeof(SOCKADDR);
  34. char buf[4096];//接收的数据
  35. char rbuf[100]=
  36. "<cross-domain-policy> "
  37. "<allow-access-from domain=/"*/" to-ports=/"*/"/>"
  38. "</cross-domain-policy> ";//套接字策略文件
  39. while(1)
  40. {
  41. //接受连接
  42. sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
  43. printf("Accept connection from %s/n",inet_ntoa(addrClient.sin_addr));
  44. recv:
  45. //接收数据
  46. int bytes;
  47. if((bytes=recv(sockConn,buf,sizeof(buf),0))==SOCKET_ERROR)
  48. {
  49. printf("接收数据失败!/n");
  50. exit(-1);
  51. }
  52. buf[bytes]='/0';
  53. printf("Message from %s: %s/n",inet_ntoa(addrClient.sin_addr),buf);
  54. if (0 == strcmp(buf,"<policy-file-request/>"))
  55. {
  56. //发送数据
  57. if(send(sockConn,rbuf,strlen(rbuf)+1,0)==SOCKET_ERROR)
  58. {
  59. printf("发送数据失败!");
  60. exit(-1);
  61. }
  62. printf("Message to %s: %s/n",inet_ntoa(addrClient.sin_addr),rbuf);
  63. }
  64. else
  65. {
  66. //Echo
  67. if(send(sockConn,buf,strlen(buf)+1,0)==SOCKET_ERROR)
  68. {
  69. printf("发送数据失败!");
  70. exit(-1);
  71. }
  72. printf("Message to %s: %s/n",inet_ntoa(addrClient.sin_addr),buf);
  73. goto recv;
  74. }
  75. //清理套接字占用的资源
  76. closesocket(sockConn);
  77. }
  78. }

四,通过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的消息发送:

  1. protected:
  2. LocalConnEmulator lcSim;
    启动LocalConnection仿真程序:

  1. lcSim.InitEmulator(this);
  2. lcSim.StartEmulator();
    Flash客户端发送的信息的获取是通过仿真程序派发自定义事件捕获得到,定义自定义消息函数:
  1. // Generated message map functions
  2. virtual BOOL OnInitDialog();
  3. afx_msg LONG OnMessageIn(WPARAM arrayPtr, LPARAM len);
  4. afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
  5. afx_msg void OnPaint();
  6. afx_msg HCURSOR OnQueryDragIcon();
  7. DECLARE_MESSAGE_MAP()
    添加消息映射:
  1. BEGIN_MESSAGE_MAP(CCppLocalConnDlg, CDialogEx)
  2. ON_MESSAGE(WM_LOCAL_CONN_MESSAGE_IN, OnMessageIn)
  3. ON_WM_SYSCOMMAND()
  4. ON_WM_PAINT()
  5. ON_WM_QUERYDRAGICON()
  6. ON_BN_CLICKED(IDC_BUTTON1, &CCppLocalConnDlg::OnBnClickedButton1)
  7. END_MESSAGE_MAP()
    实现自定义消息函数:

  1. LONG CCppLocalConnDlg::OnMessageIn(WPARAM arrayPtr, LPARAM len)
  2. {
  3. char msgRcv[256];
  4. memcpy(msgRcv,(char*)arrayPtr,len);
  5. msgRcv[len]=0;
  6. this->SetWindowText(msgRcv);
  7. return true;
  8. }

五,开源软件实现RTMP Server:

      诸如CRTMP Server,openRTMFP。特点:很强大,易于实现音视频聊天类应用。总体而言CRTMP Server还支持远程共享对象的,功能更加强大,而且内存和CPU资源占用极小,一句话,好的没法说,但是openRTMFP可以通过lua脚本配置服务器,实现与Adobe Flash Media的类似脚本,也相当不错,这一点由于crtmpServer。

    如下为crtmpServer实现视频直播的demo,通过服务器直播摄像头内容(我使用虚拟摄像头,就是专门由来视频骗别人借钱汇款之类的软件),客户端为Adobe AS3API 文档中的例子:

  1. package
  2. {
  3. import flash.display.Sprite;
  4. import flash.events.*;
  5. import flash.media.Video;
  6. import flash.media.Camera;
  7. import flash.net.NetConnection;
  8. import flash.net.NetStream;
  9. import fl.controls.Button;
  10. import fl.controls.Label;
  11. public class NetStream_publish extends Sprite
  12. {
  13. private var connectionURL:String = "rtmp://127.0.0.1/live/";
  14. private var videoURL:String = "liveVideo";
  15. private var nc:NetConnection;
  16. private var ns_publish:NetStream;
  17. private var ns_playback:NetStream;
  18. private var video_publish:Video;
  19. private var video_playback:Video;
  20. private var cam:Camera;
  21. private var b:Button;
  22. private var l:Label;
  23. public function NetStream_publish() {
  24. setUpUI();
  25. nc = new NetConnection();
  26. nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
  27. // Add bandwidth detection handlers on the NetConnection Client to
  28. // prevent Reference Errors at runtime when using the "live" and "vod" applications.
  29. var clientObj:Object = new Object();
  30. clientObj.onBWDone = onBWDone;
  31. clientObj.onBWCheck = onBWCheck;
  32. nc.client = clientObj;
  33. // Connect to the "live" application on Flash Media Server.
  34. nc.connect(connectionURL);
  35. }
  36. private function netStatusHandler(event:NetStatusEvent):void {
  37. trace(event.info.code + " | " + event.info.description);
  38. switch (event.info.code) {
  39. case "NetConnection.Connect.Success":
  40. // Enable the "Publish" button after the client connects to the server.
  41. b.enabled = true;
  42. break;
  43. case "NetStream.Publish.Start":
  44. playbackVideo();
  45. break;
  46. }
  47. }
  48. private function publishVideo(event:MouseEvent):void{
  49. // Disable the button so that you can only publish once.
  50. b.enabled = false;
  51. // Create a NetStream to send video to FMS.
  52. ns_publish = new NetStream(nc);
  53. ns_publish.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
  54. // Publish (send) the video to FMS.
  55. cam = Camera.getCamera();
  56. ns_publish.attachCamera(cam);
  57. ns_publish.publish(videoURL);
  58. }
  59. private function playbackVideo():void {
  60. // Create the Video object to show the video on the stage
  61. video_playback = new Video(cam.width, cam.height);
  62. video_playback.x = cam.width + 20;
  63. video_playback.y = 10;
  64. addChild(video_playback);
  65. // Create a NetStream to receive the video from FMS.
  66. ns_playback = new NetStream(nc);
  67. ns_playback.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
  68. // Display the video that was published to FMS.
  69. video_playback.attachNetStream(ns_playback);
  70. ns_playback.play(videoURL);
  71. }
  72. private function setUpUI():void {
  73. b = new Button();
  74. b.addEventListener(MouseEvent.CLICK, publishVideo);
  75. b.width = 150;
  76. b.label = "Publish video to server";
  77. b.move(10, 150);
  78. b.enabled = false;
  79. l = new Label();
  80. l.width = 150;
  81. l.text = "Playing back from server"
  82. l.move(190, 150);
  83. addChild(b);
  84. addChild(l);
  85. }
  86. // Handlers called by the Flash Media Server "live" and "vod" applications.
  87. public function onBWDone(... rest):Boolean {
  88. return true;
  89. }
  90. public function onBWCheck(... rest):Number {
  91. return 0;
  92. }
  93. }
  94. }

MFC&Flash交互的几种方式

    多个客户端连接,同时实现点播视频与在线视频流播放:

MFC&Flash交互的几种方式

如下为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聊天客户端软件下载:

MFC&Flash交互的几种方式

基于openRTMFP的聊天应用


MFC&Flash交互的几种方式

相关文章: