html5 WebSocket 与 PHP socket 聊天室原理

html js

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>聊天室</title>
	<link rel="stylesheet" href="css/style.css">
	<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>
<body>
<div class="head"></div>
<div id="wrapper">
	<div id="message">
	
	</div>
	<div id="action">
		<textarea id="data"></textarea>
		<button id="send">发送</button>
	</div>
	
</div>

<script>
	(function() {

		var socket = new WebSocket('ws://127.0.0.1:8008');
		var send = document.getElementById('send');
		var data = document.getElementById('data');
		var message = document.getElementById('message');
		var wrapper = document.getElementById('wrapper');
		var height = (wrapper.offsetHeight) -270;

		message.style.height = height+'px';
		socket.onopen = function(event) {
			message.innerHTML = '<p><span>连接成功!</span></p>';
		}

		socket.onmessage = function(event) {
			var dl = document.createElement('dl');
			var jsonData = JSON.parse(event.data);
			dl.innerHTML =  "<dt><img src="+jsonData.avatar+"><dt><dd><span></span>"+jsonData.content+"</dd>";
			message.appendChild(dl);
			message.scrollTop = message.scrollHeight;
		}

		socket.onerror = function() {
			message.innerHTML = '<p><span>连接失败!</span></p>';
		}

		send.addEventListener('click', function() {

			var content = data.value;
			if(content.length <= 0) {
				alert('消息不能为空!');
				return false;
			}

			var avatar = Math.random();
			var message = {
				"avatar" : 'images/avatar.jpg',
				"content" : content
			}

			var json = JSON.stringify(message);
			socket.send(json);

			data.value = ''; data.focus();
			
		});
	})();
</script>
</body>
</html>

PHP

<?php

class WebSocket {

	private $socket;

	private $accept;

	private $isHand = array();

	public function __construct($host, $port, $max) {
		$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
		socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, TRUE);
		socket_bind($this->socket, $host, $port);
		socket_listen($this->socket, $max);
	}

	public function start() {
		while(true) {
			$cycle = $this->accept;
			$cycle[] = $this->socket;
			socket_select($cycle, $write, $except, null);

			foreach($cycle as $sock) {
				if($sock === $this->socket) {
					$client = socket_accept($this->socket);
					$this->accept[] = $client;
					$key = array_keys($this->accept);
					$key = end($key);
					$this->isHand[$key] = false;
				} else {
					$length = socket_recv($sock, $buffer, 204800, 0);
					$key = array_search($sock, $this->accept);

					if($length < 7) {
						$this->close($sock);
						continue;
					}

					if(!$this->isHand[$key]) {
						$this->dohandshake($sock, $buffer, $key);
					} else {
						// 先解码,再编码
						$data = $this->decode($buffer);
						$data = $this->encode($data);

						// 判断断开连接(断开连接时数据长度小于10)
						if(strlen($data) > 10) {
							foreach($this->accept as $client) {
								socket_write($client, $data, strlen($data));
							}
						}
					}
				}
				
			}

		}
		
	}

	/**
	 * 首次与客户端握手
	 */
	public function dohandshake($sock, $data, $key) {
		if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $data, $match)) {
			$response = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
			$upgrade  = "HTTP/1.1 101 Switching Protocol\r\n" .
					"Upgrade: websocket\r\n" .
					"Connection: Upgrade\r\n" .
					"Sec-WebSocket-Accept: " . $response . "\r\n\r\n";
			socket_write($sock, $upgrade, strlen($upgrade));
			$this->isHand[$key] = true;
		}
	}

	/**
	 * 关闭一个客户端连接
	 */
	public function close($sock) {
		$key = array_search($sock, $this->accept);
		socket_close($sock);
		unset($this->accept[$key]);
		unset($this->handshake[$key]);
	}

	/**
	 * 解码过程
	 */
	public function decode($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;
	}

	/**
	 * 编码过程
	 */
	public function encode($buffer) {
		$length = strlen($buffer);
		if($length <= 125) {
			return "\x81".chr($length).$buffer;
		} else if($length <= 65535) {
			return "\x81".chr(126).pack("n", $length).$buffer;
		} else {
			return "\x81".char(127).pack("xxxxN", $length).$buffer;
		}
	}


}

$webSocket = new WebSocket('127.0.0.1', 8008, 100);
$webSocket->start();

?>
posted @ 2016-03-18 17:25  陈朔  阅读(4106)  评论(0编辑  收藏  举报