Skip to content

Instantly share code, notes, and snippets.

@kanreisa
Created March 30, 2011 09:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kanreisa/894125 to your computer and use it in GitHub Desktop.
Save kanreisa/894125 to your computer and use it in GitHub Desktop.
<?php
/*
/ >、
厶/vvゝ
|| ・_・リ
川゚O O゚
``
*/
/*
- 定数定義 -
*/
define('IKA_ADDR', '192.168.12.33');
define('IKA_PORT', 10080);
define('IKA_BACKLOG', 32);
define('IKA_ERR_LV', E_ALL); // 全てのエラーを出力
define('IKA_NAME', 'Test');
/*
- 変数定義 -
*/
$master = NULL;
$sockets = array();
$clients = array();
/*
- 特殊定数 -
*/
if(defined('__DIR__')==false){
define('__DIR__', dirname(__FILE__));
}
<?php
/*
- func.system -
*/
function make_log($type,$message,$debug=false)
{
$error = NULL;
if($debug==true) $error = ' #'.__LINE__.':'.socket_strerror(socket_last_error());
return '['.date('H:i:s').']['.$type.'] '.$message.$error.PHP_EOL;
}
function make_id($ip,$port)
{
return $ip.':'.$port;
}
/*
- func.websocket -
*/
function realize_header($buffer)
{
$resource = $code = null;
preg_match('/GET (.*?) HTTP/', $buffer, $match) && $resource = $match[1];
preg_match("/\r\n(.*?)\$/", $buffer, $match) && $code = $match[1];
$headers = array();
foreach(explode("\r\n", $buffer) as $line)
{
if (strpos($line, ': ') !== false)
{
list($key, $value) = explode(': ', $line);
$headers[trim($key)] = trim($value);
}
}
return array($resource, $headers, $code);
}
function make_security_key($key1, $key2, $code)
{
return md5(
pack('N', cook_security_key($key1)).
pack('N', cook_security_key($key2)).
$code,
true
);
}
function cook_security_key($key)
{
preg_match_all('/[0-9]/', $key, $number);
preg_match_all('/ /', $key, $space);
if ($number && $space) {
return @implode('', $number[0]) / count($space[0]);
}
return '';
}
#!/usr/bin/php -q
<?php
/*
/ >、
厶/vvゝ WebSocket Server Daemon
|| ・_・リ - Ikasumi - ver.1 By: kanreisa
川゚O O゚
``
Usage:
(launch)
php ikasumi.php
php ikasumi.php >> /dev/null &
php ikasumi.php >> ../log/ikasumi/ikasumi.log &
(access)
ws://host:port/resource
*/
require 'def.inc.php'; /* 変数定数定義 */
require 'func.inc.php'; /* 関数定義 */
/*
- PHP実行環境設定 -
*/
error_reporting(IKA_ERR_LV); // エラー表示設定
set_time_limit(0); // タイムアウト無効
ob_implicit_flush(1); // php_cli用設定
/*
- 起動メッセージ -
*/
echo ' / >、', PHP_EOL,
"厶/\033[36mvv\033[0mゝ WebSocket Server Daemon for {".IKA_NAME.'}', PHP_EOL,
"\033[36m||\033[0m ・_・\033[36mリ\033[0m \033[46m Ikasumi \033[0m ver.1 By: kanreisa", PHP_EOL,
"\033[36m川\033[0m゚O O゚", PHP_EOL;
/*
- Socket通信初期化 -
*/
require 'init_sock.inc.php';
/*
- メインルーチン -
*/
echo make_log('system','Ikasumi server is started. PORT='. IKA_PORT);
echo make_log('system','Ikasumi directory is '. __DIR__);
/* run loop */
while(true):
$changed = $sockets;
socket_select($changed, $write=NULL, $except=NULL, NULL);
foreach($changed as $socket)
{
if($socket==$master)
gosub('accept connection');//新規接続
else
{
gosub('init client_id');
if(($bytes=socket_recv($socket, $buffer, 2048, 0))==0)//なにか受信
gosub('close connection');//なんもなかったら切る
else
{
/* do handshake or other */
if(isset($clients[$client_id]['handshake'])==false)
{
list($resource, $headers, $securityCode) = realize_header($buffer);//ヘッダ解析
if(trim($buffer)=='<policy-file-request/>')
{
gosub('response policy for flash');//Flash互換対応
gosub('close connection');//done
}
else if(isset($headers['Origin'])==false)
{
echo make_log('info', '<denied> '. $client_id);
gosub('close connection');//WSプロトコルでないので切る
}
else
gosub('response handshake');//接続要求に応答
}
else
{
// do
$buffer = str_replace('・€・€', '', $buffer);
echo make_log('test', '<read> '.$client_id.' >'. $buffer);
}
}
}
}
usleep(5000); // 0.005s
endwhile;
/*
- sub.routine -
*/
function gosub($do)
{
global $master, $sockets, $clients, $socket, $resource, $headers, $securityCode;
global $client_id, $client_ip, $client_port;
/* switch */
switch($do):
case 'accept connection':
/* try accept connection */
if(($accepted=socket_accept($master))===false)
{
// failed
echo make_log('warn', 'socket_accept() failed', true);
return;
}
socket_getpeername($accepted, $client_ip, $client_port);
$client_id = make_id($client_ip,$client_port);
/* decide */
if(isset($clients[$client_id]['accepted'])==true)
{
// rejected
socket_close($accepted);
echo make_log('info', '<rejected> '.$client_id, true);
}
else
{
// accept
echo make_log('info', '<accepted> '.$client_id, true);
// add
$clients[$client_id]['accepted'] = true;
$clients[$client_id]['socket'] = $accepted;
$sockets[] = $accepted;
}
return;
case 'init client_id':
/* init client_id from ip,port */
socket_getpeername($socket, $client_ip, $client_port);
$client_id = make_id($client_ip,$client_port);
return;
case 'response policy for flash':
/* responce cross-domain-policy */
$hdr = '<?xml version="1.0"?>'.
'<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">'.
'<cross-domain-policy>'.
' <allow-access-from domain="*" to-ports="'.IKA_PORT.'"/>'.
'</cross-domain-policy>';
socket_write($socket, $hdr.chr(0), strlen($hdr.chr(0)));
echo make_log('info', '<response> '.$client_id.' [GET]policy-file',true);
return;
case 'response handshake':
/* process for handshake */
if (isset($headers['Sec-WebSocket-Key1']) && isset($headers['Sec-WebSocket-Key2']))
{
$securityResponse = make_security_key($headers['Sec-WebSocket-Key1'], $headers['Sec-WebSocket-Key2'], $securityCode);
$hdr = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: WebSocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Origin: " . $headers['Origin'] . "\r\n" .
"Sec-WebSocket-Location: ws://" . $headers['Host'] . $resource . "\r\n" .
"\r\n".$securityResponse;
}
else
{
$hdr = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: WebSocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: " . $headers['Origin'] . "\r\n" .
"WebSocket-Location: ws://" . $headers['Host'] . $resource . "\r\n" .
"\r\n";
}
socket_write($socket, $hdr.chr(0), strlen($hdr.chr(0)));
echo make_log('info', '<response> '.$client_id.' [GET]'.$resource, true);
$clients[$client_id]['handshake'] = true; // save handshake status
return;
case 'close connection':
/* close connection */
unset($clients[$client_id]);
if(($index=array_search($socket, $sockets))>=0) array_splice($sockets, $index, 1);
socket_close($socket);
echo make_log('info', '<closed> '.$client_id, true);
return;
endswitch;
}
<?php
/*
- Socket通信初期化 -
*/
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die(make_log('error','socket_create():', true));
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die(make_log('error','socket_option():', true));
socket_bind($master, IKA_ADDR, IKA_PORT) or die(make_log('error','socket_bind():', true));
socket_listen($master, IKA_BACKLOG) or die(make_log('error','socket_listen():', true));
$sockets[] = $master; // add
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment