Last active
March 3, 2016 09:53
-
-
Save maple-nishiyama/0cb6304070fa7d170daa to your computer and use it in GitHub Desktop.
PHP でチャットサーバー
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
error_reporting(E_ALL); | |
set_time_limit(0); | |
ob_implicit_flush(); | |
declare(ticks = 1); | |
$sock = null; | |
function main() { | |
global $sock; | |
// シグナルハンドラのセット | |
pcntl_signal(SIGTERM, "sig_handler"); | |
pcntl_signal(SIGHUP, "sig_handler"); | |
pcntl_signal(SIGUSR1, "sig_handler"); | |
// デーモン化 | |
// daemonize(); | |
$address = '127.0.0.1'; | |
$port = 10000; | |
// サーバーソケット作成 | |
$sock = server_socket($address, $port); | |
// 接続待ち受けループに入る | |
accept_loop($sock); | |
socket_close($sock); | |
} | |
// シグナルハンドラ関数 | |
function sig_handler($signo) { | |
global $sock; | |
switch ($signo) { | |
case SIGTERM: | |
// シャットダウンの処理 | |
socket_close($sock); | |
exit; | |
break; | |
case SIGHUP: | |
// 再起動の処理 | |
break; | |
case SIGUSR1: | |
echo "SIGUSR1 を受け取りました...\n"; | |
socket_close($sock); | |
break; | |
default: | |
// それ以外のシグナルの処理 | |
} | |
} | |
function daemonize() { | |
// PHPのプロセスをデーモン状態にする | |
$pid = pcntl_fork(); | |
if ($pid < 0) { | |
die("フォーク失敗\n"); | |
} else if ($pid > 0) { | |
// 親プロセス | |
exit(); | |
} | |
// 子プロセス | |
// 制御端末の切り離し | |
$sid = posix_setsid(); | |
if ($sid < 0) { | |
die("セッションを生成できませんでした。\n"); | |
} | |
// セッションリーダーでなくする | |
// 2回めの fork() | |
$pid2 = pcntl_fork(); | |
if ($pid2 < 0) { | |
die("2回めのフォーク失敗\n"); | |
} else if ($pid2 > 0) { | |
// 子プロセス | |
exit(); | |
} | |
// 孫プロセス | |
// ルートディレクトリをカレントディレクトリへ | |
chdir("/"); | |
// 標準入出力を閉じる | |
fclose(STDIN); | |
fclose(STDOUT); | |
fclose(STDERR); | |
// デーモンになった! | |
} | |
function server_socket($address, $port) { | |
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) { | |
die("socket_create() に失敗\n"); | |
} | |
if (socket_bind($sock, $address, $port) === false) { | |
die("socket_bind() に失敗\n"); | |
} | |
if (socket_listen($sock, 5) === false) { | |
die("socket_listen() に失敗\n"); | |
} | |
return $sock; | |
} | |
// 待ち受けループ | |
function accept_loop($sock) { | |
$clients = [$sock]; | |
while (true) { | |
$read = $clients; | |
$write = NULL; | |
$except = NULL; | |
if (socket_select($read, $write, $except, NULL) < 1) { | |
continue; | |
} | |
// 接続受け付け | |
if (in_array($sock, $read)) { | |
// 待ち受けソケットに新しい接続が来た | |
$newsock = socket_accept($sock); | |
$clients[] = $newsock; | |
$msg = "\nチャットサーバーに接続しました。\n" . | |
"やめるには 'quit' と打ってください。\n"; | |
socket_write($newsock, $msg); | |
$key = array_search($sock, $read); | |
unset($read[$key]); | |
} | |
// メッセージ受信 | |
foreach ($read as $read_sock) { | |
$data = @socket_read($read_sock, 1024, PHP_NORMAL_READ); | |
if ($data === false) { | |
$key = array_search($read_sock, $clients); | |
unset($clients[$key]); | |
echo "client disconnected.\n"; | |
continue; | |
} | |
$data = trim($data); | |
if (empty($data)) { | |
continue; | |
} | |
if ($data == 'quit') { | |
socket_close($read_sock); | |
$key = array_search($read_sock, $read); | |
echo "quit key = $key\n"; | |
unset($clients[$key]); | |
continue; | |
} | |
$data = $data . "\n"; // 改行を再び付加する | |
// 他のクライアントに配信 | |
broad_cast($data, $clients, $read_sock, $sock); | |
} | |
} | |
} | |
// メッセージ配信処理 | |
function broad_cast($message, $clients, $sender, $server_sock) { | |
foreach ($clients as $sock) { | |
if ($sock === $sender || $sock === $server_sock) { | |
// 送信者自身と待受ソケットは除外 | |
continue; | |
} | |
socket_write($sock, $message, strlen($message)); | |
} | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment