当前位置: 代码迷 >> Web前端 >> 从头开始构建一个web即时通讯系统(3) - 客户端 - flash socket
  详细解决方案

从头开始构建一个web即时通讯系统(3) - 客户端 - flash socket

热度:467   发布时间:2013-01-04 10:04:14.0
从头开始构建一个web即时通讯系统(三) - 客户端 - flash socket

摘要

在上一篇"基础-web即时通讯系统的四种实现"中,我们讨论了socket/tcp相对于其他基于http的通讯方式的优越。本文则详细描述如何使用flash/actionscript,来实现socket/tcp,并使用开源的haxe来编译它。

?

flash中的socket

?

socket的构造

实例化一个socket对象,只需要一行代码:

static var socket = new flash.net.Socket();

?

主动方法和被动方法

而剩下的工作便是把这个对象包装成合适接口供javascript调用了。这时候主要有两类方法,一类是主动性方法,由客户端直接发起的便是主动方法,比如connect、send就是主动方法。

另一类是被动方法,也就是服务器端发起,然后在客户端触发事件,并被调用的回调方法,比如connected、receive就是被动方法。

主动方法和被动方法直接决定了代码的结构,决定了javascript和flash的互操作方式。

?

事件时序

我们从事件时序上来看一下flash中socket的可能的一个过程:

?

flash加载完毕

我们先注册一个回调,在flash加载完毕的时候,获得一个通知,以便为下面的连接做一些设置和准备。

flash.external.ExternalInterface.call("setTimeout", jsScope + "loaded()");
?

建立一个socket连接

接着我们便使用下面的方法来建立一个socket连接

static function connect(host : String, port : String) {
	trace("Connecting to socket server at " + host + ":" + port);
	socket.connect(host, Std.parseInt(port)); 
}
?

这里的两个形参:host为ip或者域名,port为通讯的端口。trace为输入到flash控制台的方法。

?

flash socket在端口上是有限制的,仅被允许访问4502-4534这个网段里的端口。

?

flash socket在执行socket.connect(host, Std.parseInt(port))时,无论如何,会先访问843端口,如果843端口被监听,并且在连接上后接收到一个policy-file.xml的话,才会断开与843端口的连接,并与调用方法时指定的端口连接。

?

如果flash没有正常得到这个policy-file.xml的话,从调用方法时指定的端口连接时刚连接上便获得这个policy-file.xml也是可以的。如果这两种情况下都没有获得policy-file.xml,那么通讯是不能继续的。这是flash特有的安全机制。

?

关于这个安全机制,以及提供这个policy-file.xml服务器程序policyserver,我会在稍后的教程里详细介绍。

?

连接失败

如果flash没有顺利连接上服务器,或者超时,我们可以注册一个回调来获得连接失败的通知,已准备重连。

socket.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR, function(e) : Void {
		trace("SECURITY ERROR : " +  e.text);
		flash.external.ExternalInterface.call("setTimeout", jsScope + "securityError('" +e.text+ "')", 0);
	}
);
?

连接成功

如果顺利连接上了,这时候,我们可以通过下面的代码来注册一个回调,得到连接状态更新的通知。

socket.addEventListener(flash.events.Event.CONNECT, function(e) : Void { 
		trace("Connected to server");
		flash.external.ExternalInterface.call("setTimeout", jsScope + "connected()", 0);
	}
);
?

发送消息

下面我们便可以从flash发送消息给服务器了

static function send(msg) {

	if (socket.connected) {

		trace("Writing '" + msg + "' to server");

		//socket.writeMultiByte(msg,"gb2312");

		socket.writeUTFBytes(msg);

		socket.flush();

	} else {

		trace("Cannot write to server because there is no connection!");

	}

} 
?

我们可以看到,这段代码里,我注释掉了socket.writeMultiByte(msg,"gb2312");此句代码。从字面意思上也可以了解,这里是使用了"gb2312"的编码来发送消息。

?

flash socket提供了丰富的方法,在以后的教程里,我会详细描述flash.net.Socket的每一个方法。

?

获取消息

如果服务器发送消息给客户端,我们也可注册一个回调来获取消息的内容

socket.addEventListener(flash.events.ProgressEvent.SOCKET_DATA, function(e) : Void {

		//var msg = socket.readMultiByte(socket.bytesAvailable,"gb2312");

		var msg = socket.readUTFBytes(socket.bytesAvailable);

		trace("Received : " + msg );

		flash.external.ExternalInterface.call("setTimeout", jsScope + "receive('" + msg + "')", 0);

	}

);
?

这里和发送消息类似,我们可以使用不同的编码标准来解码我们获得的字节流。

?

关闭连接

在完成通讯以后,如果我们需要关闭这个socket连接的话,可以使用下面的主动方法。

?

static function close() {

	if (socket.connected) {

   		trace("Closing current connection");

		socket.close();

	} else {

		trace("Cannot disconnect to server because there is no connection!");

	}

}
?

连接断开

在socket连接断开以后,我们可以注册一个回调来获得通知

?

socket.addEventListener(flash.events.Event.CLOSE, function(e) : Void {

		trace("Disconnected from server");

		flash.external.ExternalInterface.call("setTimeout", jsScope + "disconnected()", 0);

	}

);
?

IO错误

在通信过程中,如果有错误发生的话,我们可以这样来获得通知

socket.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e) : Void {

		trace("IOERROR : " +  e.text);

		flash.external.ExternalInterface.call("setTimeout", jsScope + "ioError('" + e.text + "')" ,0);

	}

);
?

完整的代码

//SocketBridge.hx
class SocketBridge {

	static var socket = new flash.net.Socket();
	static var jsScope;

	static function main() {

		if (flash.external.ExternalInterface.available) {

			jsScope = flash.Lib.current.loaderInfo.parameters.scope;

			if (jsScope == null) {
				jsScope = "";
			} else {
				jsScope += ".";
			}

			/* Calls the javascript load method once the SWF has loaded */
			flash.external.ExternalInterface.call("setTimeout", jsScope + "loaded()");

			// Set event listeners for socket
			// CONNECT
			socket.addEventListener(flash.events.Event.CONNECT, function(e): Void {

				trace("Connected to server");

				flash.external.ExternalInterface.call("setTimeout", jsScope + "connected()", 0);

			}

			// CLOSE
			socket.addEventListener(flash.events.Event.CLOSE, function(e): Void {

				trace("Disconnected from server");

				flash.external.ExternalInterface.call("setTimeout", jsScope + "disconnected()", 0);

			}

			// IO ERROR
			socket.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e): Void {

				trace("IOERROR : " + e.text);

				flash.external.ExternalInterface.call("setTimeout", jsScope + "ioError('" + e.text + "')", 0);

			}

			// SECURITY ERROR
			socket.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR, function(e): Void {

				trace("SECURITY ERROR : " + e.text);

				flash.external.ExternalInterface.call("setTimeout", jsScope + "securityError('" + e.text + "')", 0);

			}

			// SOCKET DATA
			socket.addEventListener(flash.events.ProgressEvent.SOCKET_DATA, function(e): Void {

				//var msg = socket.readMultiByte(socket.bytesAvailable,"gb2312");
				var msg = socket.readUTFBytes(socket.bytesAvailable);

				trace("Received : " + msg);

				flash.external.ExternalInterface.call("setTimeout", jsScope + "receive('" + msg + "')", 0);

			}

			/* Set External Interface Callbacks */

			// connect(host, port)
			flash.external.ExternalInterface.addCallback("connect", connect);

			// disconnect()
			flash.external.ExternalInterface.addCallback("close", close);

			// send()
			flash.external.ExternalInterface.addCallback("send", send);

			// log()
			flash.external.ExternalInterface.addCallback("log", log);

		} else {

			trace("Flash external interface not available");

		}

	}

	/**
     * Connect to new socket server
     * @param host The host the socket server resides on
     * @param port The socket servers port
     */

	static function connect(host: String, port: String) {

		trace("Connecting to socket server at " + host + ":" + port);

		socket.connect(host, Std.parseInt(port));

	}

	/**
     * Disconnect the socket
     */

	static function close() {

		if (socket.connected) {

			trace("Closing current connection");

			socket.close();

		} else {

			trace("Cannot disconnect to server because there is no connection!");

		}

	}

	/**
     * Write string to the socket
     */

	static function send(msg) {

		if (socket.connected) {

			trace("Writing '" + msg + "' to server");

			//socket.writeMultiByte(msg,"gb2312");

			socket.writeUTFBytes(msg);

			socket.flush();

		} else {

			trace("Cannot write to server because there is no connection!");

		}

	}

	/**
     * Log
     */
     
	static function log(msg) {

		trace("log : " + msg);

	}

}
?

编译

我使用了开源的haxe来编译,你也可以通过http://haxe.org/file/hxinst-win.exe获取它。

编写完SocketBridge.hx以后,你需要在同一个目录下建一个compile.hxml文本文件,它的内容是:

-swf socket_bridge.swf

-swf-version 9

-main SocketBridge
?

在安装过haxe的情况下,只要你双击执行compile.hxml,便可以在同一目录下获得编译出的socket_bridge.swf 。

?

?

在下一篇教程"客户端-javascript和flash互操作"里,我们将了解如何使用socket_bridge.swf来操作javascript进行socket/tcp通讯。

?

?

?

  相关解决方案