zoukankan      html  css  js  c++  java
  • 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: (.*)
    /", $data, $match)) {
    			$response = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
    			$upgrade  = "HTTP/1.1 101 Switching Protocol
    " .
    					"Upgrade: websocket
    " .
    					"Connection: Upgrade
    " .
    					"Sec-WebSocket-Accept: " . $response . "
    
    ";
    			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();
    
    ?>
    
  • 相关阅读:
    终结篇:MyBatis原理深入解析(二)
    Centos7 安装clamav杀毒
    jenkins 自动化部署
    docker 安装redis
    linux CentOS7 安装字体库-转
    docker 安装jenkins
    linux 下安装docker
    linux 下安装redis
    linux 下mongo 基础配置
    Linux下MongoDB安装和配置详解
  • 原文地址:https://www.cnblogs.com/chenshuo/p/5292975.html
Copyright © 2011-2022 走看看