Skip to content

Instantly share code, notes, and snippets.

@maple-nishiyama
Last active March 3, 2016 09:53
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 maple-nishiyama/0cb6304070fa7d170daa to your computer and use it in GitHub Desktop.
Save maple-nishiyama/0cb6304070fa7d170daa to your computer and use it in GitHub Desktop.
PHP でチャットサーバー
<?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