zoukankan      html  css  js  c++  java
  • websocket初体验(能传文字和图片)

    这两天花时间看了一下websocket,自己也跟着动手做了一个毕竟简单的实现 记录一下:

    websocket分为 客户端 和 服务端 两部分  

    客户端代码 

                    var ws = new WebSocket('ws://127.0.0.1:7777'); // 访问的是本地的服务器
    
    		function sendMsg() {
    			let _file = document.querySelector('input[type=file]').files[0];
    			let text = document.getElementById('txtinput').value;
    			
    			if (ws.readyState == 1) {
    				// 处理图片为base64格式
    				if (_file) {
    					let reader = new FileReader();
    					reader.readAsDataURL(_file); // 转化为二进制流
    					reader.onload = () => {
    						ws.send(reader.result);
    					}
    				} else {
    					ws.send(text);
    				}
    			}
    		}
    
    		// 连接服务器
    		ws.onopen = function (e) {
    			console.log('connect');
    		}
    
    		// 接收服务器的消息
    		ws.onmessage = function(e) {
    			if (e.data.indexOf('base64,') > 0) {
    				let img = new Image();
    				img.src = e.data;
    				document.getElementById('area').appendChild(img);
    			} else {
    				let text = document.createElement('div');
    				text.innerHTML = e.data;
    
    				document.getElementById('area').appendChild(text);
    			}
    		}
    
    		ws.onclose = e => {
    			console.log('lost connect');
    			ws = 0;
    		}
    		
    		ws.onerror = e => {
    			console.log('error');
    			console.log(e);
    		}        
    

      客户端第一次连接服务端的时候会有一个握手的步骤 客服端发送WebSocket-Key给服务端,服务端拿到之后 又加密返给客户端 双方都通过后 websocket连接建立成功

    服务端代码(PHP实现)

    class SocketController
    {
        public $master;
        public $sockets;
        private $len = 1024;
    
        public function __construct($ip, $port)
        {
            // 创建一个IPV4的套接字
            $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1); // 设置接收所有数据
            // 绑定IP 端口
            socket_bind($this->master, $ip, $port);
            //开启监听
            socket_listen($this->master);
    //        socket_set_nonblock($this->master);
            $this->sockets['master'] = $this->master;
            echo 'socket create' . PHP_EOL;
        }
    
    
    
        // 接收消息
        public function run()
        {
            while (true)
            {
                $changes = $this->sockets;
                $write = $exception = [];
            // 没处理异常的情况 关于第四个参数 null 表示等待客户端发消息或有新连接再往下走 会阻塞在这里 非null的情况没做尝试 socket_select($changes, $write, $exception, null); foreach ($changes as $k => $master) { // 说明有新的客户端进来 if ($master == $this->master) { $client = socket_accept($master); //获取客户端发送内容 $buffer = trim(socket_read($client,1024)); $this->hand($client, $buffer); // 握手 $key = uniqid(); echo 'new connect ' . $key . PHP_EOL; $this->sockets[$key] = $client; } else // 说明客户端发了信息过来 { $content = ''; $contentLength = 0; echo 'begin get content ' . $k . PHP_EOL; // 用do while 是因为socket_recv 会阻塞程序 消息接收完了也卡在这不往下走 do { $len = socket_recv($master, $subContent, $this->len, 0); $content .= $subContent; $contentLength += $len; }while($len == $this->len); if ($contentLength > 0) { // 把消息 给到其他连接 $this->sendMsg($content); } else { $this->unlinkSocket($k); // 关闭socket连接 } } } } } // 发送消息 public function sendMsg($content) { $fixedContent = self::encode($content); foreach ($this->sockets as $k => $socket) { if ($k == 'master') continue; socket_write($socket, $fixedContent, strlen($fixedContent)); } echo 'send msg' . PHP_EOL; } // 关闭套接字 public function unlinkSocket($connect) { echo 'lost connect' . $connect . PHP_EOL; @socket_shutdown($this->sockets[$connect]); socket_close($this->sockets[$connect]); unset($this->sockets[$connect]); } // 握手 public function hand($client, $buffer) { $buf = substr($buffer, strpos($buffer,'Sec-WebSocket-Key:') + 18); $key = trim(substr($buf, 0, strpos($buf, "\r\n"))); $newKey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true)); //按照协议组合信息进行返回 $newMessage = "HTTP/1.1 101 Switching Protocols\r\n"; $newMessage .= "Upgrade: websocket\r\n"; $newMessage .= "Sec-WebSocket-Version: 13\r\n"; $newMessage .= "Connection: Upgrade\r\n"; $newMessage .= "Sec-WebSocket-Accept: " . $newKey . "\r\n\r\n"; socket_write($client, $newMessage, strlen($newMessage)); } /** * https://www.cnblogs.com/zhangmingda/p/12678630.html 很好的解释了$masks $data为什么这样取值 * @param $buffer * @return string */ public function encode($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]; } // end // 这段的意思是 把数据分割成125个字节的长度大小 大于125个字节的 就分段返回 // $a = str_split($decoded, 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; // end // 这个就不分段了 $frame = array(); $frame[0] = '81'; $len = strlen($decoded); if ($len < 126) { $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len); } elseif ($len < 65025) { $s = dechex($len); $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s; } else { $s=dechex($len); $frame[1]= '7f' . str_repeat('0',16 - strlen($s)) . $s; } $msg = ''; for ($i = 0; $i < $len; $i++) { $msg .= dechex(ord($decoded{$i})); } $frame[2] = $msg; $data = implode('', $frame); return pack("H*", $data); } } $sc = new SocketController('127.0.0.1', 7777); $sc->run();

      刚写完服务端代码的时候并没有发送过来的数据做处理,以为是发送过来一个 ‘hello world’ 服务端拿到的就应该是一个明文的字符串 一眼就能看到懂那种,后来打印了之后才发现事情并没有这么简单,看了  https://www.cnblogs.com/zhangmingda/p/12678630.html  这个老哥的文章才知道数据在这个过程中是怎么传递的 牵扯到位运算及字符操作函数 对这方面也了解不深就不赘述了

    上面已经基本上算是实现了聊天室的功能

    每天都是不想努力的一天....
  • 相关阅读:
    微信公众号开发第三课 实现简单微信文本通讯
    微信公众号开发第二课 百度BAE搭建和数据库使用
    微信公众号开发第一课 预备知识和申请权限账号
    joomla3.1安装不通过Magic Quotes GPC解决方法
    获取汉字拼音的简便方法
    C#快速学习笔记(译)续一
    C#快速学习笔记(译)
    Xcode中如何集成Unity
    【ios开发】iOS App测试方案
    【IOS开发】SimPholders的使用
  • 原文地址:https://www.cnblogs.com/Theia/p/13199973.html
Copyright © 2011-2022 走看看