谈Flash客户端与C++后台的几种通信方法

11 篇文章 0 订阅

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


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要实现客户客户之间的聊天通信,可以使用Qt和C++结合进行开发。以下是一个基本的示例: 首先,你需要创建一个TCP客户来与服务器进行连接和通信。你可以使用QTcpSocket类来实现。在你的客户代码中,你需要先创建一个QTcpSocket对象,并连接到服务器: ```cpp QTcpSocket* socket = new QTcpSocket(this); socket->connectToHost("server_ip", port); ``` 在上述代码中,你需要将"server_ip"替换为实际的服务器IP地址,port替换为服务器的口号。 接下来,你可以使用socket对象的信号和槽机制来处理与服务器的通信。例如,当接收到服务器发送的消息时,可以使用readyRead()信号来处理: ```cpp connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); ``` 然后,在onReadyRead()槽函数中,你可以读取服务器发送的消息: ```cpp void YourClass::onReadyRead() { QByteArray data = socket->readAll(); // 处理接收到的消息 } ``` 对于客户之间的聊天通信,你可以通过socket对象发送消息给其他客户。例如,在发送按钮的点击事件中,你可以使用socket对象的write()方法将消息发送给服务器: ```cpp void YourClass::onSendButtonClicked() { QString message = "Your message"; socket->write(message.toUtf8()); } ``` 同样地,你需要在服务器接收到这些消息后将其发送给目标客户。 这只是一个基本的示例,实际的实现可能需要更多的逻辑和处理。你还需要处理连接错误、断开连接、处理接收到的消息等情况。同时,你可能需要设计协议来指定消息的格式和处理规则。 希望这个示例能帮助你开始实现客户客户之间的聊天通信
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值