我的编程空间,编程开发者的网络收藏夹
学习永远不晚

WebSocket的通信原理和使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

WebSocket的通信原理和使用

一、什么是WebSocket?

1.1 简介

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信,即允许服务器主动发送信息给客户端。因此,在WebSocket中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输,客户端和服务器之间的数据交换变得更加简单。

1.2 WebSocket的优势

现在,很多网站为了实现推送技术,所用的技术都是Ajax轮询。轮询是在特定的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。

这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求。然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。HTML5定义的WebSocket协议优势如下:

小Header,互相沟通的Header非常小,只有2Bytes左右。
2、服务器不再被动接收到浏览器的请求之后才返回数据,而是在有新数据时就主动推送给浏览器。
3、WebSocket协议能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

1.3 WebSocket的原理 

▪ Websocket协议由RFC 6455定义,协议分为两个部分: 握手阶段和全双工通信阶段。

  客户端发送的header内容 

GET /nickname11 HTTP/1.1 Host: 127.0.0.1:9090Connection: UpgradeUpgrade: websocketSec-WebSocket-Extensions: permessage-deflate; client_max_window_bitsSec-WebSocket-Key: wJdg8v4EJiDsIZg5+s0hY8RUQ2A=Sec-WebSocket-Version: 13Origin: http://127.0.0.1

  服务端响应的header内容,这里的Sec-WebSocket-Accept要根据发送的Sec-WebSocket-Key来处理算出来,计算方法:base64_encode(sha1(websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true)) 。

HTTP/1.1 101 Switching ProtocolUpgrade: WebSocketSec-WebSocket-Version: 13Connection: UpgradeSec-WebSocket-Accept: wJdg8v4EJiDsIZg5+s0hY8RUQ2A=

▪ Websocket协议的握手阶段是使用的HTTP协议。

▪ Websocket协议的“全双工”消息通信是基于 TCP/IP 的协议集之上的,客户端和服务端可随时发送数据。协议连接是“ws”或者加密的“wss”。

▪  通信的数据是基于“帧(frame)”的,可以传输文本数据,也可以直接传输二进制数据,效率高。

 一条消息(message)可由一个或多个帧(Frame)组成,很多时候会将帧和消息混用,因为大部分时候一条消息只使用一个帧

二、使用PHP实现WebSocket通信

server.php(服务端) 

master = $this->createWebSocket();         //创建socket连接池        $this->sockets=array($this->master);    }public function start(){while (true) {$changes=$this->sockets;            $write=NULL;            $except=NULL;            //设置非阻塞,让多个连接能同时正常往下执行@socket_select($changes, $write, $except, NULL);foreach($changes as $socket){//判断是否新的socket连接if($socket == $this->master){$client=socket_accept($socket);$key=uniqid();$this->sockets[]=$client;$this->users[$key]=array(                        'client'=>$client,                        'is_shake'=>0                    );}else{$len=0;                    $buffer='';do{                        $l=socket_recv($socket,$buf,1024,0);                        $len+=$l;                        $buffer.=$buf;                    }while($l==1024);$key = $this->search($socket);// 如果接收的信息长度小于7,则该client的socket为断开连接if($len<7){                        unset($this->users[$key]);            socket_close($socket);                        continue;                    }                    //判断连接是否已握手                    if(!$this->users[$key]['is_shake']){                    $this->shake($key, $buffer);                    }else{                    //接收客户端发送消息                    $buffer = $this->getMsg($buffer);                        if($buffer === false){continue;                        }                        //发送消息                        $this->sendMsg($key,$buffer);                    }}}}}protected function intoRedis($data){$redis = new Redis();$redis->pconnect($this->redisIp, $this->redisPort, $this->redisLength);$redis->lpush("ws_".$this->getMd5Key($data['username']), json_encode($data));return true;}protected function search($socket){        foreach ($this->users as $key=>$val){            if($socket==$val['client'])            return $key;        }        return false;    }    protected function shake($key, $buf)    {    preg_match("/Sec-WebSocket-Key: (.*)\r\n/i",$buf,$match);    //用于服务端计算Sec_WebSocket_Accept的固定的字符串    $keyStr = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';        $res= "HTTP/1.1 101 Switching Protocol".PHP_EOL            ."Upgrade: WebSocket".PHP_EOL            ."Sec-WebSocket-Version: 13".PHP_EOL            ."Connection: Upgrade".PHP_EOL            ."Sec-WebSocket-Accept: " . base64_encode(sha1($match[1].$keyStr ,true)) .PHP_EOL.PHP_EOL;  // 注意需要两个换行        // 向客户端应答 Sec-WebSocket-Accept        socket_write($this->users[$key]['client'], $res, strlen($res));        //对已经握手的client做标志        $this->users[$key]['is_shake'] = 1;        return true;    }protected function sendMsg($key, $buffer){$index = strpos($buffer, ":");        $data = [        'username' => substr($buffer, 0, $index),        'msg' => substr($buffer, ($index+1)),        'time' => date("Y-m-d H:i:s", time()),        ];        foreach($this->users as $val){        $msg = $this->buildMsg(json_encode($data));            socket_write($val['client'], $msg, strlen($msg));        }        //通过redis记录消息        $this->intoRedis($data);        echo "
";        print_r($data);}// 编码服务端向客户端发送的内容protected function buildMsg($msg) {    $frame = [];    $frame[0] = '81';    $len = strlen($msg);    if ($len < 126) {        $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);    } else if ($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;    }    $data = '';    $l = strlen($msg);    for ($i = 0; $i < $l; $i++) {        $data .= dechex(ord($msg{$i}));    }    $frame[2] = $data;    $data = implode('', $frame);    return pack("H*", $data);}// 解析客户端向服务端发送的内容protected function getMsg($buffer) {    $res = '';    $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++) {        $res .= $data[$index] ^ $masks[$index % 4];    }    return $res;}//建立WebSocket链接protected function createWebSocket(){    $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);    socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);//1代表接受所有的数据包    socket_bind($server, $this->ip, $this->port);    socket_listen($server);        echo 'Socket连接创建成功,时间: '.date('Y-m-d H:i:s').PHP_EOL;    return $server;}protected function getMd5Key($username){return md5($username."WebSocket");}}$server = new server();$act = isset($_POST['act']) ? $_POST['act'] : 'start';if($act == 'start'){$server->start();}else if($act == 'getAllMsg'){$server->getRedis();}

getredis.php(获取存在redis的历史消息) 

500, 'msg'=>'act参数不能为空', 'data'=>[]]);exit;}if($act != 'getAllMsg'){echo json_encode(['code'=>500, 'msg'=>'act传参错误', 'data'=>[]]);exit;}$redisIp = '127.0.0.1';$redisPort = 6379;$redisLength = 1024*600;$redis = new Redis();$redis->pconnect($redisIp, $redisPort, $redisLength);$keys = $redis->keys("ws_*");$data = [];if($keys){foreach($keys as $key){$res = $redis->lGetRange($key, 0, -1);if($res){foreach($res as &$val){$val = json_decode($val, JSON_UNESCAPED_UNICODE);$val['time_stamp'] = strtotime($val['time']);}$data = array_merge($res, $data);}}}if($data){$sort = array_column($data, 'time_stamp');array_multisort($sort, SORT_ASC, $data);}echo json_encode(['code'=>200, 'msg'=>'获取成功', 'data'=>$data]);exit;

chat.html(客户端)

   WebSocket聊天室

状态栏

当前用户: 在线情况:离线    


聊天记录栏

webSocket事件输出栏

来源地址:https://blog.csdn.net/m0_68949064/article/details/127729569

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

WebSocket的通信原理和使用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何使用PHP和WebSocket实现实时通信

随着互联网技术的不断发展,实时通信已经成为了日常生活中不可缺少的一部分。利用WebSockets技术可以实现高效、低延迟的实时通信,而PHP作为互联网领域使用最广泛的开发语言之一,也提供了相应的WebSocket支持。本文将为大家介绍如何使
如何使用PHP和WebSocket实现实时通信
2023-12-17

使用 Spring Boot 实现 WebSocket实时通信

在开发 Web 应用程序时,我们有时需要将服务端事件推送到连接的客户端。但 HTTP 并不能做到。客户端打开与服务端的连接并请求数据,但服务端不能打开与客户端的连接并推送数据。为了解决这个限制,我们可以建立了一个轮询模式,网页会间隔地轮询服
2023-05-30

golang函数和管道通信的原理

go 语言中函数和管道结合使用实现进程间通信。函数可将管道作为参数传递,通过管道发送或接收数据。管道是无缓冲通道,可用于在 goroutine 之间发送和接收数据,并支持无向和有向管道。发送数据时使用 Go 语言中函数和管道通信原理与实战
golang函数和管道通信的原理
2024-05-04

PHP和WebSocket: 实现即时通信的优化方法

PHP和WebSocket: 实现即时通信的优化方法引言:随着互联网的快速发展,即时通信成为人们生活中必不可少的一部分。而在建立一个高效和可靠的即时通信系统中,WebSocket技术的应用变得越来越普遍,它可以实现双向实时通信,大大提高了用
PHP和WebSocket: 实现即时通信的优化方法
2023-12-17

如何使用Go语言实现多线程Websocket通信

在近年来,实时通信已经成为了一种基本的需要。WebSocket则更是实时通信当中的佼佼者,它能够更快速、有效地实现客户端和服务器端之间的实时通信。而Go语言也作为近年来较火热的一种语言,被广泛应用于实时通信中。利用Go语言的优点以及多线程处
如何使用Go语言实现多线程Websocket通信
2023-12-14

如何在golang中使用WebSocket进行跨平台通信

WebSocket是一种用于在Web浏览器和服务器之间进行实时双向通信的技术。它提供了一种可靠的方式来发送和接收数据,并且比传统的HTTP请求-响应机制更加高效、快速。在golang中,我们可以使用第三方库来支持WebSocket通信,本文
如何在golang中使用WebSocket进行跨平台通信
2023-12-18

WebSocket与实时通信的应用案例分析

随着互联网的发展和技术的进步,实时通信在各种应用中变得越来越重要。而传统的基于HTTP的请求-响应模式往往无法满足实时通信的需求,因此WebSocket作为一种新的协议应运而生。WebSocket协议基于TCP,允许在客户端和服务器之间建立
2023-10-21

微信小程序如何使用WebSocket实现即时通讯

使用WebSocket实现即时通讯功能,可以让用户实时收发消息,并保持连接状态。在微信小程序中,可以通过wx.connectSocket()方法创建WebSocket连接,并设置相关事件监听器来处理收发消息的逻辑。以下是基本步骤:创建Web
微信小程序如何使用WebSocket实现即时通讯
2024-04-03

PHP和WebSocket: 实现即时通信的完美解决方案

随着互联网的快速发展,越来越多的应用需要实现即时通信功能,比如在线聊天、实时数据监控等。而传统的 HTTP 协议并不适合这种场景,因为它是基于请求/响应的,必须客户端不断向服务器发送请求才能获取最新数据,这样带来了很大的开销和延迟。为了解决
PHP和WebSocket: 实现即时通信的完美解决方案
2023-12-17

如何在Golang中使用WebSocket实现一个通信功能

本篇文章给大家分享的是有关如何在Golang中使用WebSocket实现一个通信功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是golanggolang 是Google
2023-06-06

PHP和WebSocket: 实现即时通信的完美解决方案

随着互联网的快速发展,越来越多的应用需要实现即时通信功能,比如在线聊天、实时数据监控等。而传统的 HTTP 协议并不适合这种场景,因为它是基于请求/响应的,必须客户端不断向服务器发送请求才能获取最新数据,这样带来了很大的开销和延迟。为了解决
PHP和WebSocket: 实现即时通信的完美解决方案
2023-12-17

如何使用PHP和WebSocket构建实时通知功能

如何使用PHP和WebSocket构建实时通知功能随着互联网的发展,实时通知功能开始变得越来越重要。无论是社交媒体的消息提醒、即时通讯的聊天功能,还是在线游戏的实时数据更新,都需要实时通知功能来提供用户最新的信息。在本文中,我们将介绍如何使
如何使用PHP和WebSocket构建实时通知功能
2023-12-18

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录