上一篇文章简要的介绍了一下 http 协议, 这次再介绍一下 webSocket 协议, 两者之间有很大的区别, WebSocket 协议是 html5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议.
简单的理解一下什么是双工通讯协议, 之前有讲过 http 协议只能客户端向服务端发起请求, 是单向的应用层协议. 而双工通讯协议, 不仅客户端能向服务端发起请求, 服务端也可以主动推送信息给客户端.
目的: 即时通讯, 替代轮询
WebSocket 协议在 2008 年诞生, 2011 年成为国际标准. 所有浏览器都已经支持了.
相比于 http 协议, websoket 协议给我们带来了极大的方便, 举个例子:
之前, 我们在做消息通知的时候, 需要设置定时器, 频繁的向后台发起异步 Ajax 请求实现长轮询, 获取最新的数据, 这样效率非常低, 非常浪费资源, 因为不停的发起 http 请求, 不停的与服务端建立连接, 或者 http 链接始终打开.
而现在有了 websocket, 就解决了轮询的问题, websocket 只需要建立一次连接, 就可以保持长久连接, 相比轮询不停的建立连接, 大大的提升了效率, 这样只要服务端有数据变化, 就可以立即通知前台了.
websocket 协议与 http 协议之间的关系
首先 HTTP 有 1.1 和 1.0 之说, 也就是所谓的 keep-alive , 把多个 HTTP 请求合并为一个, 但是 Websocket 其实是一个新协议, 跟 HTTP 协议基本没有关系, 只是为了兼容现有浏览器的握手规范而已, 也就是说它是 HTTP 协议上的一种补充, Websocket 是借用了 HTTP 的协议来完成一部分握手.
websocket 有以下特点:
建立在 TCP 协议之上, 服务器端的实现比较容易.
与 HTTP 协议有着良好的兼容性. 默认端口也是 80 和 443, 并且握手阶段采用 HTTP 协议, 因此握手时不容易屏蔽, 能通过各种 HTTP
代理服务器.
属于长连接 (http 协议无状态)
双向通信 (http 是单向通信)
可以跨域, 不受浏览器同源策略的限制 (http 协议不可跨域)
数据格式比较轻量, 性能开销小, 通信高效
可以发送文本, 也可以发送二进制数据.
协议标识符是 ws(如果加密, 则为 wss), 服务器网址就是 URL. 如: ws://example.com:80/some/path
websocket 协议应用
目前 websocket 对大部分浏览器有很好的兼容, 但对于 IE10 以下的浏览器无法兼容. 为了解决兼容性, Socket.IO 就出现了.
Socket.IO 是一个基于 Node.JS 的, 用于实时通信的一个软件包 (包括 client 端和 server 端),Socket.IO 完全由 JavaScript 实现.
平时在应用的时候, 可以直接引入 socket.io 这个库就可以了. 前后台都需要引用该库.
下面针对 Node.JS 的服务端如何使用举个例子:
服务端代码:
- const http = require('http');
- const fs = require('fs');
- const express = require('express');
- const io = require('socket.io');
- const App = express();
- const httpServer = http.createServer(App);
- App.use('/', express.static(__dirname + '/www'));
- httpServer.listen(8081);
- const ioserve = io.listen(httpServer);
- let users = [];
- ioserve.on('connection', (socket) => {
- socket.on('login', (nickname) => {
- if (users.indexOf(nickname)> -1) {
- socket.emit('nickExisted');
- } else {
- socket.userIndex = users.length;
- socket.nickname = nickname;
- users.push(nickname);
- socket.emit('loginSuccess');
- ioserve.sockets.emit('system', nickname, users.length, 'login');
- }
- });
- socket.on('postMsg', function (msg) {
- socket.broadcast.emit('newMsg', socket.nickname, msg);
- });
- socket.on('img', function (imgData) {
- socket.broadcast.emit('newImg', socket.nickname, imgData);
- })
- socket.on('disconnect', function () {
- users.splice(socket.userIndex, 1);
- socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
- });
- })
前台代码
HTML 文件
- <body>
- <div class="wrapper">
- <div class="banner">
- <h1>HiChat :)</h1>
- <span id="status"></span>
- </div>
- <div id="historyMsg">
- </div>
- <div class="controls">
- <div class="items">
- <input id="colorStyle" type="color" placeHolder='#000' title="font color" />
- <input id="emoji" type="button" value="emoji" title="emoji" />
- <label for="sendImage" class="imageLable">
- <input type="button" value="image" />
- <input id="sendImage" type="file" value="image" />
- </label>
- <input id="clearBtn" type="button" value="clear" title="clear screen" />
- </div>
- <textarea id="messageInput" placeHolder="enter to send"></textarea>
- <input id="sendBtn" type="button" value="SEND">
- <div id="emojiWrapper">
- </div>
- </div>
- </div>
- <div id="loginWrapper">
- <p id="info">connecting to server...</p>
- <div id="nickWrapper">
- <input type="text" placeHolder="nickname" id="nicknameInput" />
- <input type="button" value="OK" id="loginBtn" />
- </div>
- </div>
- <script src="/socket.io/socket.io.js"></script>
- <script src="js/hichat.js"></script>
- </body>
JS 文件
- ; (function () {
- function Hichart() {
- this.socket = null;
- }
- Hichart.prototype = {
- init: function () {
- var that = this;
- this.socket = io.connect();
- this.socket.on('connect', function () {
- document.getElementById('info').textContent = 'get yourself a nickname :)';
- document.getElementById('nickWrapper').style.display = 'block';
- document.getElementById('nicknameInput').focus();
- that.login();
- that.postMsg();
- that.uploadImg();
- });
- this.socket.on('nickExisted', function () {
- document.getElementById('info').textContent = '!nickname is taken, choose another pls';
- });
- this.socket.on('loginSuccess', function () {
- document.title = 'hichat |' + document.getElementById('nicknameInput').value;
- document.getElementById('loginWrapper').style.display = 'none';// 隐藏遮罩层显聊天界面
- document.getElementById('messageInput').focus();// 让消息输入框获得焦点
- });
- this.socket.on('system', function (nickname, userCount, type) {
- var msg = nickname + (type === 'login' ? 'joined' : 'left');
- that.displayNewMsg('system', msg, 'red');
- document.getElementById('status').textContent = userCount + (userCount> 1 ? 'users' : 'user') + 'online';
- });
- this.socket.on('newMsg', function (nickname, msg) {
- that.displayNewMsg(nickname, msg);
- });
- this.socket.on('newImg', function (nickname, newImg) {
- that.displayImage(nickname, newImg);
- });
- },
- login: function () {
- var that = this;
- document.getElementById('loginBtn').addEventListener('click', function () {
- var nickname = document.getElementById('nicknameInput').value;
- if (nickname.trim().length) {
- that.socket.emit('login', nickname);
- } else {
- document.getElementById('nicknameInput').focus();
- }
- }, false)
- },
- displayNewMsg: function (user, msg, color) {
- var container = document.getElementById('historyMsg');
- var msgToDisplay = document.createElement('p');
- var data = new Date().toTimeString().substr(0, 8);
- msgToDisplay.style.color = color || '#000';
- msgToDisplay.innerHTML = user + '<span class="timespan">(' + data + '): </span>' + msg;
- container.appendChild(msgToDisplay);
- container.scrollTop = container.scrollHeight;
- },
- displayImage: function (user, imgData, color) {
- var container = document.getElementById('historyMsg'),
- msgToDisplay = document.createElement('p'),
- date = new Date().toTimeString().substr(0, 8);
- msgToDisplay.style.color = color || '#000';
- msgToDisplay.innerHTML = user + '<span class="timespan">(' + date + '): </span> <br/>' + '<a href="' + imgData + '"target="_blank"><img src="' + imgData + '"/></a>';
- container.appendChild(msgToDisplay);
- container.scrollTop = container.scrollHeight;
- },
- postMsg: function () {
- var that = this;
- var sendBtn = document.getElementById('sendBtn');
- var messageInput = document.getElementById('messageInput');
- sendBtn.addEventListener('click', function () {
- var msg = messageInput.value;
- if (msg.trim().length != 0) {
- messageInput.value = '';
- messageInput.focus();
- that.socket.emit('postMsg', msg);
- that.displayNewMsg('me', msg);
- }
- });
- },
- uploadImg: function () {
- var that = this;
- document.getElementById('sendImage').addEventListener('change', function () {
- if (this.files.length) {
- var file = this.files[0];
- var reader = new FileReader();
- if (!reader) {
- that.displayNewMsg('system', '!your browser doesn\'t support fileReader','red');
- this.value = '';
- return;
- }
- reader.onload = function (e) {
- this.value = '';
- that.socket.emit('img', e.target.result);
- that.displayImage('me', e.target.result);
- }
- reader.readAsDataURL(file);
- }
- })
- }
- }
- Windows.onload = function () {
- var hichart = new Hichart();
- hichart.init();
- }
- })()
完整 demo 请查看我的 GitHub, 地址: https://github.com/jianwenjuan/websockt-chart
来源: http://www.bubuko.com/infodetail-3275626.html