websocket学习

简介:
首先在WebSocket出现之前,很多网站为了实现实时推送技术,通常采用的方案是轮询技术,让浏览器每隔几秒就发送一次请求。询问是否有新消息,他做不到服务器主动向客户端推送消息。这样不停连接会大量消耗服务器带宽和资源。面对这种状况,HTML5定义了一个新的基于TCP的应用层协议----WebSocket协议,它能更好的节省服务器资源和带宽并实现真正意义上的实时推送。这种网络通信协议的特点就是持久连接,只需要依赖http协议进行一次连接,建立连接后服务器和客户端都能主动向对方发送或接收数据,也就是一种双向通信协议。
属性:
Socket.readyState
只读属性 readyState 表示连接状态,可以是以下值:
0 - 表示连接尚未建立。
1 - 表示连接已建立,可以进行通信。
2 - 表示连接正在进行关闭。
3 - 表示连接已经关闭或者连接不能打开
Socket.bufferedAmount 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
事件:
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发
方法:
Socket.send() 使用连接发送数据
Socket.close() 关闭连接
使用场景:
应用的场景是很广泛的,即时聊天、消息推送、及时互动游戏、实时股价、实况报道、直播、基于位置的应用,越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。如果你一直记录用户的位置(比如运行应用来记录运动轨迹),你可以收集到更加细致化的数据。当需要实时显示数据的时候用户最期望的是不需要去刷新页面 ,所以用WebSockets是个不错的选择。
然后考虑用什么搭建服务器,对于服务器方面,网上有不同对websocket支持的服务器,例如PHP、nodejs、python、tomcat…下面用nodejs和python搭建服务器练习一下websocket。

接下来要考虑浏览器对websocket的支持 IE10等浏览器以下是不兼容的,所以我们实际的项目中要考虑低版本浏览器的兼容方案:这里我们可以使用Socket.IO,是一个开源的WebSocket库,同时也提供客户端JS库。Socket.IO支持以事件为基础的实时双向通讯,它可以工作在任何平台、浏览器或移动设具有不错的稳定性和性能。对于那些不支持WebSocket的浏览器,会自动降为Ajax连接,最大限度地保证了兼容性。 其实node上有多种websocket模块可以选择,常见的是ws和socketio,(ws一个使用简单,速度极快,稳定的websocket客户端和服务端的Node.js实现。) 考虑到兼容性下面的聊天室用socket.io来做

第一步
安装node 下载地址:​https://nodejs.org/en/download/
安装express npm install express
安装socket npm install socket.io
第二步编写的文件package.json,server.js,index.html. 在根目录下建立server.js
server.js代码

var express = require('express'),
    app = express(),
    server = require('http').createServer(app),
    //  //引入socket.io模块并绑定到服务器
    io = require('socket.io').listen(server),
    users = [];//保存所有在线用户昵称
app.use('/', express.static(__dirname + '/www'));
server.listen(process.env.PORT || 8888);
io.sockets.on('connection', function(socket) {
    //设置昵称
    socket.on('login', function(nickname) {
        if (users.indexOf(nickname) > -1) {
            socket.emit('nickExisted');
        } else {
            //socket.userIndex = users.length;
            socket.nickname = nickname;
            users.push(nickname);
            socket.emit('loginSuccess');
            // 向所有连接到服务器的客户端发送当前登陆用户的昵称
            io.sockets.emit('system', nickname, users.length, 'login');
        };
    });
    //断开连接的事件
    socket.on('disconnect', function() {
        if (socket.nickname != null) {
            将断开连接的用户从users中删除
            users.splice(users.indexOf(socket.nickname), 1);
                //通知除自己以外的所有人
            socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
        }
    });
    //接收新消息
    //接收新消息
    socket.on('postMsg', function(msg, color) {
      //将消息发送到除自己外的所有用户
      
        while (true) {
            socket.broadcast.emit('newMsg', socket.nickname, msg, color);
        }
    });
    //接收
    socket.on('img', function(imgData, color) {
      // 通过一个newImg事件分发到除自己外的每个用户
        socket.broadcast.emit('newImg', socket.nickname, imgData, color);
    });
});

index.html代码

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta name="author" content="Wayou">
        <meta name="description" content="hichat | a simple chat application built with node.js and websocket">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>hichat</title>
        <link rel="stylesheet" href="styles/main.css">
        <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
        <link rel="icon" href="favicon.ico" type="image/x-icon">
    </head>
    <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="scripts/hichat.js"></script>
        <script>
        /**REMOVE ME IF YOU CANT ACCESS GOOGLE SERVICE**/
          (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
          (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
          m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
          })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

          ga('create', 'UA-46794744-7', 'hichat.herokuapp.com');
          ga('send', 'pageview');
        /**REMOVE END**/
    </script>
    </body>
</html>

hitcat.js代码

window.onload = function() {
    //实例化并初始化
    var hichat = new HiChat();
    hichat.init();
};
//定义一个类
var HiChat = function() {
    this.socket = null;
};

//向原型添加方法
HiChat.prototype = {
    init: function() {
        var that = this;
	    //建立服务器的socket连接
        this.socket = io.connect();
	    //监听socket的connect事件这个事件表示链接建立
        this.socket.on('connect', function() {
	   //连接服务器后显示昵称输入框
            document.getElementById('info').textContent = 'get yourself a nickname :)';
            document.getElementById('nickWrapper').style.display = 'block';
            document.getElementById('nicknameInput').focus();
        });
        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('error', function(err) {
            if (document.getElementById('loginWrapper').style.display == 'none') {
                document.getElementById('status').textContent = '!fail to connect :(';
            } else {
                document.getElementById('info').textContent = '!fail to connect :(';
            }
        });
        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(user, msg, color) {
            that._displayNewMsg(user, msg, color);
        });
        this.socket.on('newImg', function(user, img, color) {
            that._displayImage(user, img, color);
        });
	    //昵称的确认按钮
        document.getElementById('loginBtn').addEventListener('click', function() {
            var nickName = document.getElementById('nicknameInput').value;
             //判断输入框是否为空
	    if (nickName.trim().length != 0) {
		    //不为空将昵称发送到服务器
                that.socket.emit('login', nickName);
            } else {
		    //为空输入框获取焦点
                document.getElementById('nicknameInput').focus();
            };
        }, false);
        document.getElementById('nicknameInput').addEventListener('keyup', function(e) {
            if (e.keyCode == 13) {
                var nickName = document.getElementById('nicknameInput').value;
                if (nickName.trim().length != 0) {
                    that.socket.emit('login', nickName);
                };
            };
        }, false);
        document.getElementById('sendBtn').addEventListener('click', function() {
            var messageInput = document.getElementById('messageInput'),
                msg = messageInput.value,
                color = document.getElementById('colorStyle').value;
            messageInput.value = '';
            messageInput.focus();
            if (msg.trim().length != 0) {
                that.socket.emit('postMsg', msg, color);//把消息发送到服务器
                that._displayNewMsg('me', msg, color);//把自己的消息显示到自己的窗口中
                return;
            };
        }, false);
        document.getElementById('messageInput').addEventListener('keyup', function(e) {
            var messageInput = document.getElementById('messageInput'),
                msg = messageInput.value,
                color = document.getElementById('colorStyle').value;
            if (e.keyCode == 13 && msg.trim().length != 0) {
                messageInput.value = '';
                that.socket.emit('postMsg', msg, color);
                that._displayNewMsg('me', msg, color);
            };
        }, false);
        document.getElementById('clearBtn').addEventListener('click', function() {
            document.getElementById('historyMsg').innerHTML = '';
        }, false);
        document.getElementById('sendImage').addEventListener('change', function() {
           //检查是否有文件选中
		if (this.files.length != 0) {
                var file = this.files[0],
				//读取文件
                    reader = new FileReader(),
                    color = document.getElementById('colorStyle').value;
                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, color);
                    that._displayImage('me', e.target.result, color);
                };
                reader.readAsDataURL(file);
            };
        }, false);
        this._initialEmoji();
        document.getElementById('emoji').addEventListener('click', function(e) {
            var emojiwrapper = document.getElementById('emojiWrapper');
            emojiwrapper.style.display = 'block';
            e.stopPropagation();
        }, false);
        document.body.addEventListener('click', function(e) {
            var emojiwrapper = document.getElementById('emojiWrapper');
            if (e.target != emojiwrapper) {
                emojiwrapper.style.display = 'none';
            };
        });
        document.getElementById('emojiWrapper').addEventListener('click', function(e) {
            var target = e.target;
            if (target.nodeName.toLowerCase() == 'img') {
                var messageInput = document.getElementById('messageInput');
                messageInput.focus();
                messageInput.value = messageInput.value + '[emoji:' + target.title + ']';
            };
        }, false);
    },
    _initialEmoji: function() {
        var emojiContainer = document.getElementById('emojiWrapper'),
            docFragment = document.createDocumentFragment();
        for (var i = 69; i > 0; i--) {
            var emojiItem = document.createElement('img');
            emojiItem.src = '../content/emoji/' + i + '.gif';
            emojiItem.title = i;
            docFragment.appendChild(emojiItem);
        };
        emojiContainer.appendChild(docFragment);
    },
    _displayNewMsg: function(user, msg, color) {
        var container = document.getElementById('historyMsg'),
            msgToDisplay = document.createElement('p'),
            date = new Date().toTimeString().substr(0, 8),
            //determine whether the msg contains emoji
            msg = this._showEmoji(msg);
        msgToDisplay.style.color = color || '#000';
        msgToDisplay.innerHTML = user + '<span class="timespan">(' + date + '): </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;
    },
    _showEmoji: function(msg) {
        var match, result = msg,
            reg = /\[emoji:\d+\]/g,
            emojiIndex,
            totalEmojiNum = document.getElementById('emojiWrapper').children.length;
        while (match = reg.exec(msg)) {
            emojiIndex = match[0].slice(7, -1);
            if (emojiIndex > totalEmojiNum) {
                result = result.replace(match[0], '[X]');
            } else {
                result = result.replace(match[0], '<img class="emoji" src="../content/emoji/' + emojiIndex + '.gif" />');//todo:fix this in chrome it will cause a new request for the image
            };
        };
        return result;
    }
};

最后用node server启动服务器
打开浏览器访问localhost
小结:不管是服务器还是客户端,socket.io提供两个核心方法:emit方法用于发送消息,on方法用于监听对方发送的消息。
ws实例:
安装 pywebsocket
在执行以上程序前,我们同样需要创建一个支持 WebSocket? 的服务。使用 git 命令下载:
git clone ​https://github.com/google/pywebsocket.git[[BR]]

mod_pywebsocket 需要 python 环境支持
mod_pywebsocket 是一个 Apache HTTP 的 Web Socket扩展,
安装步骤如下:
解压下载的文件。
进入 pywebsocket 目录。
执行命令:
$ python setup.py build
$ sudo python setup.py install
查看文档说明:
$ pydoc mod_pywebsocket
开启服务
在 pywebsocket/mod_pywebsocket 目录下执行以下命令:
$ sudo python standalone.py -p 9998 -w …/example/
打开浏览器即可访问 我们创建的 websocket.html 文件
websocket.html代码

<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
   <title></title>
        
      <script type="text/javascript">
         function WebSocketTest()
         {
            if ("WebSocket" in window)
            { 
               alert("您的浏览器支持 WebSocket!");
               
               // 打开一个 web socket
               var ws = new WebSocket("ws://localhost:9998/echo");
                                
               ws.onopen = function()
               {
                alert("连接已打开")
                  // Web Socket 已连接上,使用 send() 方法发送数据
                  ws.send("nagainnagain");
               };
 ws.onmessage = function (evt) 
               { 
                  alert('接收的信息是:'+evt.data);
        //              ws.close();
               };
                                
               ws.onclose = function()
               { 
                  // 关闭 websocket
                  alert("连接已关闭..."); 
               };
            }
            
            else
            {
               // 浏览器不支持 WebSocket
               alert("您的浏览器不支持 WebSocket!");
            }
         }
      </script>
                
   </head>
   <body>

总结: Websocket协议处理三件事:建立连接和断开连接、发送数据和接收数据、处理错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值