티스토리 뷰
# 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
- 카카오 부적
- ipTIME
- 드라이버
- 브롤스타즈
- 다시보기
- 번호판 영치
- 스노우 핏
- 원형톱
- 레드와이파이
- 크로스컷
- DWE575
- 영화
- 어메이즈 빕핏
- 사야한다
- Git
- 난치 드라이버
- 추천
- 워치 페이스
- 테이블 쏘
- 다운로드
- 가치 없는 블로그
- The 100
- 빕 핏
- 거미꼬리 뿔 독사
- 샤오미
- 세계 군사력
- 비즈박스 설치
- 밸런스 조정
- 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 | 31 |
글 보관함