アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

PHPとFlashでチャット

こんにちは、中川です。
PHPでチャットサーバが作れるか試してみました。

結論からいいますと、簡単なものでしたら、「PHPlet」というライブラリを使えば、
マルチプロセスで動作するものが、意外にあっさり作ることができました。

まず、【PHPlet】から、ライブラリをダウンロードして、「ext」フォルダのみを配置します。
そして、サーバ側プログラムは、以下のようになっています。
(※PEAR::Net_Serverを使えば実装できるみたいです。)

[chatd.php]


<?php
require_once 'ext/Server.php';
require_once 'ext/Server/Handler.php';

class Net_Server_Handler_Talkback extends Net_Server_Handler {
    var $clientStatus = array();
    
    function onConnect($clientId = 0)
    {
        $this->setClientStatus($clientId);
        $this->_server->sendData($clientId, "Hello. [quit] to exit\r\n");
        $this->_server->broadcastDataToNeighbors($clientId, "\r\nclient {$clientId} entry!!\r\n");
    }
    
    function onReceiveData($clientId = 0, $data = '')
    {
        $client = $this->getClientStatus($clientId);
        $data = trim($data);
        if($data=='') return;
        switch($data){
            case "quit":
                $this->_server->closeConnection($clientId);
                break;
            default:
                $data = htmlspecialchars($data);
                $res = "USER: " . $clientId . " : " . $data . "\r\n";
                $this->_server->broadcastData($res);
                break;
        }
    }

    function onIdle(){
        $this->_server->sendData(0,  "Please say something!\r\n");
    }

    function onClose($clientId = 0){
        $this->_server->sendData($clientId, "Bye!\r\n");
    }

    function setClientStatus($clientId) {
        if(isset($this->clientStatus[$clientId])) return;
        $this->clientStatus[$clientId] = new clientStatus();
    }
    
    function getClientStatus($clientId) {
        return $this->clientStatus[$clientId];
    }
}

class clientStatus {
    var $name;
    function __construct() {
    	
    }
}

$server =  &Net_Server::create('Multiprocess', "192.168.1.10", 8080);
$server->setMaxClients(1);
$server->_debug = false;
$handler =  &new Net_Server_Handler_Talkback();
$server->setCallbackObject($handler);
$server->start();
?>


上記のソースでは、接続しているユーザ全てにデータを送信します。

次にクライアント側ですが、AS3の勉強もかねてFlashを使っています。

[clientsample.as]


package  {	
	import flash.display.*;
	import flash.events.*;
	import flash.net.*;
	import flash.ui.*;
	import flash.utils.*;
	import flash.text.*
	
	public class ClientSample extends Sprite {
		
		private var txt:TextField;
		private var socket:Socket;
		private var sendText:TextField;
				
		public function ClientSample() {
			txt = addTextField(10, 50, 300, 300);
			this.addChild(txt);
			
			sendText = addTextField(10,10,300,20);
			sendText.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHander);
			this.addChild(sendText);
			
			socket = new Socket("192.168.1.10", 8080);
			socket.addEventListener(Event.CONNECT, connectHandler);
			socket.addEventListener(Event.CLOSE, closeHandler);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
		}
		private function addTextField(x:int,y:int,w:uint,h:uint):TextField {
			var textField:TextField=new TextField();
			addChild(textField);
			textField.x=x;
			textField.y=y;
			textField.width=w;
			textField.height=h;
			textField.text="";
			textField.selectable=true;
			textField.border=true;
			textField.type=TextFieldType.INPUT;
			return textField;
    }
		
		private function keyDownHander(evt:KeyboardEvent):void {
			if(!socket.connected) return;
			if (evt.keyCode == Keyboard.ENTER) {
				socket.writeUTFBytes(sendText.text + "\n");
				socket.flush();
				sendText.text = "";
			}
		}
		
		private function connectHandler(evt:Event):void {
			txt.text = "接続開始";
		}
		
		private function closeHandler(evt:Event):void {
			txt.text = "接続終了";
		}
		
		private function socketDataHandler(evt:ProgressEvent):void {
			var data:String=socket.readUTFBytes(socket.bytesAvailable);
            txt.text=data+"\n"+txt.text;
		}
	
	}
}



最後に、Flash表示用のHTMLをひとつ作成します。

[index.html]


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<title>php-chat-sample</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>php-chat-sample</h1>
<object 
 id="clientsample" 
 classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" 
 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" 
> 
  <param name="movie"   value="clientsample.swf" />
  <param name="quality" value="high" />
  <param name="width" value="500" />
  <param name="height" value="500" />
  <param name="allowScriptAccess" value="always" />
  <embed 
   name="clientsample" 
   src="clientsample.swf" 
   width="500"
   height="500"
   FlashVars="" 
   quality="high" 
   type="application/x-shockwave-flash" 
   pluginspage="http://www.macromedia.com/go/getflashplayer" 
   allowScriptAccess="always" 
  />
</object>
</body>
</html>


IPやポートはとりあえず、スクリプト内で指定していますので、
あとは、サーバ側でchatd.phpを実行して、index.htmlにアクセスします。


案外簡単に実装はできました。(スクリプトはやっつけっぽいですが・・・)
本番投入には問題がいろいろあるとは思いますが、PHPでもやろうと思えばできるものですね。

※今回の記事のスクリプトは以下のサイトを非常に参考にさせていただきました。
・サーバ側 
  ・【JavaScriptとFlash(socketjsのSocketConnect)でリアルタイムチャットを作ってみる】
  ・【PEAR::Net_Serverでサーバデーモンを作ろう - PHPプロ!TIPS+】
・クライアント側
  ・【ActionScript 3.0メモ(ソケットの使用) 】