ソケット通信

(サンプル一覧を表示する)

FlashDevelop を使って、ソケット通信のテストをしてみました。

サーバのコードもサンプルに入っています。
サーバ側は Visual C# 2008 Express Edition で作成しました。
Windows 以外の環境でも、 Mono 上で動くと思います。
Ubuntu + Mono でテストしました。


サーバ側動作イメージ


クライアント側動作イメージ

ソースコード

package 
{
	import flash.display.ActionScriptVersion;
	import flash.display.Graphics;
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.errors.IOError;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFieldType;
	import flash.ui.Keyboard;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import flash.system.Security;
	import flash.system.SecurityPanel;
	
	/**
	 * ソケット通信のサンプル
	 * @author Hikipuro
	 */
	public class Main extends Sprite 
	{
		/**
		 * 接続先サーバ名
		 */
		private const server:String = "hiroshi.dnip.net";
		
		/**
		 * 接続先ポート
		 */
		private const port:int = 3200;
		
		/**
		 * セキュリティポリシーファイルを取得するポート
		 */
		private const policyPort:int = 3210;
		
		/**
		 * 図形描画用スプライト
		 */
		private var sprite:Sprite;
		
		/**
		 * 結果表示用テキストフィールド
		 */
		private var textField:TextField;
		
		/**
		 * テキスト入力欄
		 */
		private var textInput:TextField;
		
		/**
         * ボタン
         */
        private var button1:SimpleButton;  
       
        /**
         * ボタンのラベル
         */
        private var buttonText1:TextField;
		
		/**
		 * ソケットオブジェクト
		 */
		private var socket:Socket;
		
		/**
		 * ソケットで受信したデータのタイプ
		 */
		private var packetType:int = 0;
		
		/**
		 * 文字列を受信した時の文字列長さ
		 */
		private var stringSize:int = 0;
		
		/**
		 * 文字列サイズ受信済みフラグ
		 */
		private var stringSizeReaded:Boolean = false;
		
		// --------------------------------------------------------------------
		
		/**
		 * コンストラクタ
		 */
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		/**
		 * 初期化イベント
		 * @param	e
		 */
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			
			// ポリシーファイルを取得する
			// Flash Player 9 以降? では、ポリシーファイルを xmlsocket:// で取得しないと
			// ソケット通信が開始できないようになったそうです。
			// http:// で、crossdomain.xml を取得するだけでは
			// ソケット通信が許可できないようになっています。
			// これは、同一ドメイン内のソケットに接続する場合でも必要になります。
			Security.loadPolicyFile("xmlsocket://" + server + ":" + policyPort);

			// テキスト入力欄の作成
			textInput = new TextField();
			textInput.type = TextFieldType.INPUT;
			textInput.x = 10;
			textInput.y = 210;
			textInput.width = 230;
			textInput.height = 20;
			textInput.border = true;
			textInput.borderColor = 0xcccccc;
			textInput.addEventListener(KeyboardEvent.KEY_DOWN, onTextInputKeyDown);
			addChild(textInput);
			
			// ボタンのラベルの作成
			buttonText1 = new TextField();
			buttonText1.autoSize = TextFieldAutoSize.CENTER;
			buttonText1.selectable = false;
			buttonText1.x = 278;
			buttonText1.y = 210;
			buttonText1.text = "送信";
			addChild(buttonText1);
			
			// ボタンの作成
			button1 = new SimpleButton();
			button1.x = 250;
			button1.y = 210;
			button1.upState = makeRoundRect(0xDDDDDD, 60, 20, 10);
			button1.overState = makeRoundRect(0xFFFFFF, 60, 20, 10);
			button1.downState = makeRoundRect(0xBBBBBB, 60, 20, 10);
			button1.hitTestState = button1.upState;
			button1.addEventListener(MouseEvent.MOUSE_DOWN, onButtonMouseDown);
			addChild(button1);
			
			// 結果表示用テキストフィールドの準備
			textField = new TextField();
			textField.wordWrap = true;
			textField.x = 0;
			textField.y = 0;
			textField.width = 320;
			textField.height = 200;
			textField.border = true;
			textField.borderColor = 0xcccccc;
			addChild(textField);
			
			// 図形描画領域の準備
			sprite = new Sprite();
			sprite.x = 320;
			addChild(sprite);
			
			// ソケットオブジェクトを作成
			socket = new Socket();
			
			// イベントハンドラ登録
			socket.addEventListener(Event.CONNECT, connectHandler);
			socket.addEventListener(Event.CLOSE, closeHandler);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
			socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			
			// データの並び順を設定
			// .net のサーバはリトルエンディアンでデータを扱うので、
			// サーバに合わせてリトルエンディアンに設定
			socket.endian = Endian.LITTLE_ENDIAN;
			
			// セキュリティサンドボックスタイプの表示
			appendText("セキュリティサンドボックスタイプ : " + Security.sandboxType);
			
			// サーバに接続
			try {
				appendText("接続を開始します。");
				socket.connect(server, port);
			}
			catch (e:Error)
			{
				appendText("socket.connect() で例外が発生しました。");
				appendText(e.toString());
			}
		}
		
		/**
		 * テキストフィールドに文字列を追加
		 */
		private function appendText(text:String):void
		{
			textField.appendText(text + "\n");
			textField.scrollV = textField.maxScrollV;
		}
		
		/**
		 * サーバに、テキスト入力欄のメッセージを送信
		 */
		private function sendTextMessage():void 
		{
			// 何も入力されてない時はそのまま終了
			if (textInput.text == "")
				return;
				
			// 未接続の場合はそのまま終了
			if (socket.connected == false)
				return;
			
			// 入力欄の文字列を ByteArray に入れる
			var byteArray:ByteArray = new ByteArray();
			byteArray.writeUTFBytes(textInput.text);
			byteArray.position = 0;
			
			// 入力されたメッセージをサーバに送信
			var string:String = byteArray.readUTFBytes(byteArray.length);
			socket.writeByte(2);
			socket.writeInt(byteArray.length);
			socket.writeUTFBytes(string);
			socket.flush();
			
			// 入力欄クリア
			textInput.text = "";
		}
		
		/**
		 * 角丸の図形を描いたスプライトを作って返す
		 * @param	color	色
		 * @param	width	幅
		 * @param	height	高さ
		 * @param	round	角丸の大きさ
		 * @return	スプライト
		 */
		private function makeRoundRect(color:uint, width:int, height:int, round:int):Sprite
		{
			var s:Sprite = new Sprite();
			s.graphics.lineStyle(2);
			s.graphics.beginFill(color);
			s.graphics.drawRoundRect(0, 0, width, height, round);
			s.graphics.endFill();
			s.alpha = 0.3;
			return s;
		}
		
		// --------------------------------------------------------------------
		
		/**
		 * テキスト入力欄でキーが押された時のイベント
		 * @param	event
		 */
		private function onTextInputKeyDown(event:KeyboardEvent):void
		{
			if (event.keyCode == Keyboard.ENTER)
				sendTextMessage();
		}

		/**
		 * 送信ボタンが押された時のイベント
		 * @param	event
		 */
		private function onButtonMouseDown(event:MouseEvent):void
		{
			sendTextMessage();
		}
		
		/**
		 * サーバに接続された時のイベント
		 * @param	event
		 */
		private function connectHandler(event:Event):void
		{
			appendText("サーバに接続しました。");
		}
		
		/**
		 * ソケットが閉じられた時のイベント
		 * @param	event
		 */
		private function closeHandler(event:Event):void
		{
			appendText("接続が閉じられました。");
		}

		/**
		 * データ受信イベント
		 * @param	event
		 */
		private function socketDataHandler(event:ProgressEvent):void
		{
			var stopFlag:Boolean = false;
			
			// データが 1 バイトでも残っている時はループ
			while (socket.bytesAvailable > 0)
			{
				// データのタイプをチェック
				if (packetType == 0)
					packetType = socket.readByte();
				
				// タイプごとに処理方法を変える
				switch (packetType) 
				{
					// 1 番は、テキスト受信メッセージ
					case 1:
						stopFlag = onRecieveType1Packet();
						break;
					// 2 番は、図形描画メッセージ
					case 2:
						stopFlag = onRecieveType2Packet();
						break;
				}
				
				// メッセージ受信終了の場合はループを抜ける
				if (stopFlag)
					break;
			}		
			//trace("bytesAvailable : " + socket.bytesAvailable);
		}
		
		/**
		 * IO エラーイベント
		 * @param	event
		 */
		private function ioErrorHandler(event:IOErrorEvent):void
		{
			trace("ioErrorHandler: " + event);
			appendText("サーバに接続できませんでした。");
		}
		
		/**
		 * セキュリティエラーイベント
		 * @param	event
		 */
		private function securityErrorHandler(event:SecurityErrorEvent):void
		{
			trace("securityErrorHandler: " + event);
			appendText("セキュリティエラーが発生しました。");
			appendText(event.text);
		}
		
		// --------------------------------------------------------------------
		
		/**
		 * テキストメッセージを受信した時
		 * @return
		 */
		private function onRecieveType1Packet():Boolean
		{
			// 文字数が決定していない時は
			// 文字数を受信する
			if (stringSizeReaded == false)
			{
				// 未受信分のデータが 4 バイトより小さい場合は、そのまま終了
				if (socket.bytesAvailable < 4)
					return true;
				
				// 文字数受信
				stringSize = socket.readInt();
				stringSizeReaded = true;
			}
			
			// 未受信分のデータのバイト数が文字数より小さい場合は、そのまま終了
			if (socket.bytesAvailable < stringSize)
				return true;
			
			// 指定された文字数分、テキストメッセージを受信
			var str:String = socket.readUTFBytes(stringSize);
			appendText(str);
			stringSizeReaded = false;
			stringSize = 0;
			
			packetType = 0;
			return false;
		}
		
		/**
		 * 図形描画メッセージを受け取った時
		 * @return
		 */
		private function onRecieveType2Packet():Boolean
		{
			// 未受信分のデータが 20 バイトより小さい場合は、そのまま終了
			if (socket.bytesAvailable < 20)
				return true;
			
			// 図形の情報を受信
			var x:int = socket.readInt();
			var y:int = socket.readInt();
			var width:int = socket.readInt();
			var height:int = socket.readInt();
			var color:int = socket.readInt();
			
			// 受信した位置・サイズ・色の図形を描く
			var g:Graphics = sprite.graphics;
			g.beginFill(color, 0.8);
			g.drawRect(x, y, width, height);
			g.endFill();
			
			packetType = 0;
			return false;
		}
	}
	
}