티스토리 뷰
# client.html
<html>
<head>
<title>WebSocket</title>
<style>
html, body {
font: normal 0.9em arial, helvetica;
}
#log {
width: 440px;
height: 200px;
border: 1px solid #7F9DB9;
overflow: auto;
}
#msg {
width: 330px;
}
</style>
<script>
var socket;
function init() {
var host = "ws://192.168.0.103:65001/test/server.php";
try {
socket = new WebSocket(host);
log('WebSocket - status ' + socket.readyState);
socket.onopen = function (msg) {
log("Welcome - status " + this.readyState);
};
socket.onmessage = function (msg) {
log("Received: " + msg.data);
};
socket.onclose = function (msg) {
log("Disconnected - status " + this.readyState);
};
} catch (ex) {
log(ex);
}
$("msg").focus();
}
function send() {
var txt, msg;
txt = $("msg");
msg = txt.value;
if (!msg) {
alert("Message can not be empty");
return;
}
txt.value = "";
txt.focus();
try {
socket.send(msg);
log('Sent: ' + msg);
} catch (ex) {
log(ex);
}
}
function quit() {
log("Goodbye!");
socket.close();
socket = null;
}
// Utilities
function $(id) {
return document.getElementById(id);
}
function log(msg) {
$("log").innerHTML += "<br>" + msg;
}
function onkey(event) {
if (event.keyCode == 13) {
send();
}
}
</script>
</head>
<body onload="init()">
<h3>WebSocket v2.00</h3>
<div id="log"></div>
<input id="msg" type="textbox" onkeypress="onkey(event)"/>
<button onclick="send()">Send</button>
<button onclick="quit()">Quit</button>
<div>Commands: hello, hi, name, age, date, time, thanks, bye</div>
</body>
</html>
# php -f server.php
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
class SocketService
{
private $address = '192.168.0.103';
private $port = 65001;
private $_sockets;
public function __construct($address = '', $port = '')
{
if (!empty($address)) {
$this->address = $address;
}
if (!empty($port)) {
$this->port = $port;
}
}
public function service()
{
//Get the tcp protocol number.
$tcp = getprotobyname("tcp");
$sock = socket_create(AF_INET, SOCK_STREAM, $tcp);
//socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
if ($sock < 0) {
throw new Exception("failed to create socket: " . socket_strerror($sock) . "\n");
}
socket_bind($sock, $this->address, $this->port);
socket_listen($sock, $this->port);
echo "listen on $this->address $this->port ... \n";
$this->_sockets = $sock;
}
public function run()
{
$this->service();
$clients[] = $this->_sockets;
while (true) {
$changes = $clients;
$write = NULL;
$except = NULL;
//When select is waiting, a of the two clients sends the data first, then socket [Select] will keep the socket of a in $changes and run down, and the socket of the other client will be discarded, so when looping again, it will only listen to A. This can add all the linked client sockets into $changes again in the new loop, so this logic error of this program can be avoided
/** socket_select It's blocking. Only when there is a data request can it be processed. Otherwise, it will be blocked all the time
* Here $changes will read to the currently active connection
* For example, the data before socket select is as follows (describe the resource ID of socket):
* $socket = Resource id #4
* $changes = Array
* (
* [0] => Resource id #5 //Client 1
* [1] => Resource id #4 //server socket resource of bound port
* )
* After calling socket select, there are two situations:
* Case 1: if it is a new client 2 connection, $changes = array ([1] = > resource ID × 4), which is now used to receive the new client 2 connection
* Case 2: if client 1 (resource ID ා) sends a message, then $changes = array ([1] = > resource ID ා), and the user receives the data of client 1
*
* As can be seen from the above description, socket [Select] has two functions, which also realizes IO multiplexing
* 1,Here comes the new client. Introduce the new connection through resource ID 4, as shown in case 1
* 2,If there is a connection to send data, switch to the current connection in real time and receive data, as in case 2*/
socket_select($changes, $write, $except, NULL);
foreach ($changes as $key => $_sock) {
if ($this->_sockets == $_sock) { //Judge whether it is a new socket
if (($newClient = socket_accept($_sock)) === false) {
die('failed to accept socket: ' . socket_strerror($_sock) . "\n");
}
$line = trim(socket_read($newClient, 1024));
if ($line === false) {
socket_shutdown($newClient);
socket_close($newClient);
continue;
}
$this->handshaking($newClient, $line);
//Get client ip
socket_getpeername($newClient, $ip);
$clients[] = $newClient;
echo "Client ip:{$ip} \n";
echo "Client msg:{$line} \n";
} else {
$byte = socket_recv($_sock, $buffer, 2048, 0);
if ($byte < 7) continue;
$msg = $this->message($buffer);
//Business code here
echo "{$key} clinet msg:", $msg, "\n";
//fwrite(STDOUT, 'Please input a argument:');
//$response = trim(fgets(STDIN));
foreach ($clients as $key2 => $_sock2) {
if ($key2 === 0) {
continue;
}
if ($_sock != $_sock2) {
if ($this->send($_sock2, $msg) === false) {
socket_close($_sock2);
unset($clients[$key2]);
}
echo "{$key2} response to Client:" . $msg, "\n";
}
}
}
}
}
}
/**
* handshake processing
* @param $newClient socket
* @return int Information received
*/
public function handshaking($newClient, $line)
{
$headers = array();
$lines = preg_split("/\r\n/", $line);
foreach ($lines as $line) {
$line = rtrim($line);
if (preg_match('/^(\S+): (.*)$/', $line, $matches)) {
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $this->address\r\n" .
"WebSocket-Location: ws://$this->address:$this->port/websocket/websocket\r\n" .
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
return socket_write($newClient, $upgrade, strlen($upgrade));
}
/**
* Parsing received data
* @param $buffer
* @return null|string
*/
public function message($buffer)
{
$len = $masks = $data = $decoded = null;
$len = ord($buffer[1]) & 127;
if ($len === 126) {
$masks = substr($buffer, 4, 4);
$data = substr($buffer, 8);
} else if ($len === 127) {
$masks = substr($buffer, 10, 4);
$data = substr($buffer, 14);
} else {
$masks = substr($buffer, 2, 4);
$data = substr($buffer, 6);
}
for ($index = 0; $index < strlen($data); $index++) {
$decoded .= $data[$index] ^ $masks[$index % 4];
}
return $decoded;
}
/**
* send data
* @param $newClinet New socket
* @param $msg Data to send
* @return int|string
*/
public function send($newClinet, $msg)
{
$msg = $this->frame($msg);
return socket_write($newClinet, $msg, strlen($msg));
}
public function frame($s)
{
$a = str_split_unicode($s, 125);
if (count($a) == 1) {
return "\x81" . chr(strlen($a[0])) . $a[0];
}
$ns = "";
foreach ($a as $o) {
$ns .= "\x81" . chr(strlen($o)) . $o;
}
return $ns;
}
public function str_split_unicode($str, $l = 0)
{
if ($l > 0) {
$ret = array();
$len = mb_strlen($str, "UTF-8");
for ($i = 0; $i < $len; $i += $l) {
$ret[] = mb_substr($str, $i, $l, "UTF-8");
}
return $ret;
}
return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}
/**
* Close socket
*/
public function close()
{
return socket_close($this->_sockets);
}
}
$sock = new SocketService();
$sock->run();
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 추천
- The 100
- 난치 드라이버
- 샤오미 드라이버
- 레드와이파이
- 밸런스 조정
- 영화
- DWE575
- 거미꼬리 뿔 독사
- 크로스컷
- 다시보기
- 가치 없는 블로그
- 테이블 쏘
- Git
- 드라이버
- 세계 군사력
- 스노우 핏
- 카카오 부적
- 사야한다
- 워치 페이스
- ipTIME
- 브롤스타즈
- 번호판 영치
- 비즈박스 설치
- 다운로드
- 빕 핏
- 샤오미
- 원형톱
- 어메이즈 빕핏
- amazfit
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
글 보관함