HTML5 WebSocket的原理及聊天室应用
2020-10-28 19:04

基本原理

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议(这段是我从 Runoob 上摘的)。它的使用比较简单,在客户端只需要new WebSocket(url)对象,即可使用。

这么说可能有些抽象,简单来说,就是通过一次 HTTP 握手,就可以在服务端和客户端之间建立一个通道,进行实时的信息传输,无需轮询。

          [ HTTP ]
[客户端]                [服务器]
   |                      |
   |--------------------->|
   |        请求          |
   |                      |
   |<---------------------|
   |        响应          |
   |                      |
   |                      |
   |--------------------->|
   |        请求          |
   |                      |
   |<---------------------|
   |        响应          |
   |                      |
   |                      |
        [ WebSocket ]
[客户端]                [服务器]
   |                      |
   |--------------------->|
   |         握手         |
   |                      |
   |<---------------------|
   |    告诉客户端已收到   |
   |                      |
   |<-------------------->|
   |     双向的往来消息    |
   |<-------------------->|
   |                      |
   |                      |
   |----------------------|
   |       关闭连接        |

那既然有了HTTP,要用WebSocket是要有一定的原因的,问题就在于HTTP是单向的(客户端请求、服务器响应),服务器是无法主动给客户端发送消息的,如果要实现如聊天室,只能通过资源、带宽消耗较大的轮询等方式进行实现,在这样的情况下,WebSocket就出现了。下面是一个利用WebSocket制作实时聊天室的例子。

例子

首先先设计一下实现方式,我的设想是这样的:客户端发送消息,就告诉服务器这段消息的内容、时间和发送者,服务器收到后向所有人广播这条消息,这样所有人就得到了这位发送的消息。

首先写一个服务端的程序,这里使用nodejsnodejs-websocket库,先写出引入依赖和创建服务器对象。

var ws = require("nodejs-websocket")           //添加Nodejs-WebSocket依赖
var port = 8001
var server=ws.createServer(function(conn){     //创建一个WebSocket服务器!
}).listen(port)                                //设置服务器开启的端口

然后根据刚才的设计,应该在收到消息发送者的内容的时候,把这个消息广播给所有人,那么应该在收到消息后直接再广播出去,使用了conn.sendText()函数,然后通过服务器server对象的connections属性,给每个连接者都发送刚才发送者发送的消息:

var ws = require("nodejs-websocket")           //添加Nodejs-WebSocket依赖
var port = 8001
var server=ws.createServer(function(conn){     //创建一个WebSocket服务器!
    conn.on("text", function (str) {                //监听收到消息事件(名为"text")
      server.connections.forEach(function (conn) {    //给每个连接的人都执行这个操作
        conn.sendText(str)                              //给这个人发送刚才的消息     
      })
      console.log("收到消息:",str)                  //控制台输出刚刚消息的内容
    })
}).listen(port)                                //设置服务器开启的端口

最后添加一个输出服务器开设的ip及端口的功能,最终代码如下:

var ws = require("nodejs-websocket")           //添加Nodejs-WebSocket依赖
var port = 8001
console.log("你的服务器的地址是:ws://"+getIP()+":"+port+",快让你的朋友们进入聊天室吧")
var server=ws.createServer(function(conn){     //创建一个WebSocket服务器!
    conn.on("text", function (str) {                //监听收到消息事件(名为"text")
      server.connections.forEach(function (conn) {    //给每个连接的人都执行这个操作
        conn.sendText(str)                              //给这个人发送刚才的消息     
      })
      console.log("收到消息:",str)                  //控制台输出刚刚消息的内容
    })
}).listen(port)                                //设置服务器开启的端口

//以下代码为获取电脑内网IP
function getIP(){
  var os=require("os")                              //引入OS依赖
  return os.networkInterfaces()['WLAN'][1].address  //返回Network信息中的内网IP
}

现在这样一个简易的服务器部分就完成了,服务端代码请见本章节最下方。

然后进行一个客户端前端页面的编写:

<div id="Output" style="width:100%;height:50%;border:2px solid black;border-radius:4px;margin-top:10px;"></div>
<div style="margin-top:10px;">
    <input type="text" id="Input" style="position:absolute;width:90%;height:20%;">
    <button style="position:absolute;right:5px;width:10%;height:20%;"onclick="sendmessage()">发送</button>
</div>
<div style="position:absolute;bottom:10px;">
    <b>连接到聊天室</b><br/>
    聊天室地址 <input type="text" id="ServerLink"/>
    聊天室昵称 <input type="text" id="MyName"/>
    <button onclick="connect()">连接</button>
</div>

然后进行客户端的接收、发送消息核心制作:

var Socket;
function connect(){
    var serverlink=document.getElementById("ServerLink").value;
    var myname=document.getElementById("MyName").value;
    var output=document.getElementById("Output");
    Socket = new WebSocket(serverlink);
    Socket.onopen = function()
    {
        output.innerHTML+="<b style='color:green'>成功连接到服务器!</b>";
    };
    Socket.onmessage = function (evt) 
    { 
        console.log(evt);
        var data=JSON.parse(evt.data);
        output.innerHTML+="<b>"+data.name+"</b> >> <b>"+data.message+"</b><br/>";
    };
}
function sendmessage(){
    var input=document.getElementById("Input").value;
    var myname=document.getElementById("MyName").value;
    var date=new Date();
    var data={};
    data.time=date.getHours()+":"+date.getMinutes();
    data.name=myname;
    data.message=input;
    Socket.send(JSON.stringify(data));
    return;
}
window.onbeforeunload = function(evt) {
Socket.close();
}//关闭窗口前先关闭连接,否则服务器会报错

最后可以试试效果,比较神奇的,使用了大概70行左右就完成了一个完备的聊天室。

实例代码

./index.html(客户端)

<html>
    <head>
    </head>
    <body>
        <div id="Output" style="width:100%;height:50%;border:2px solid black;border-radius:4px;margin-top:10px;"></div>
        <div style="margin-top:10px;">
            <input type="text" id="Input" style="position:absolute;width:90%;height:20%;">
            <button style="position:absolute;right:5px;width:10%;height:20%;"onclick="sendmessage()">发送</button>
        </div>
        <div style="position:absolute;bottom:10px;">
            <b>连接到聊天室</b><br/>
            聊天室地址 <input type="text" id="ServerLink"/>
            聊天室昵称 <input type="text" id="MyName"/>
            <button onclick="connect()">连接</button>
        </div>
        <script>
            var Socket;
            function connect(){
                var serverlink=document.getElementById("ServerLink").value;
                var myname=document.getElementById("MyName").value;
                var output=document.getElementById("Output");
                Socket = new WebSocket(serverlink);
                Socket.onopen = function()
                {
                    output.innerHTML+="<b style='color:green'>成功连接到服务器!</b><br/>";
                };
                Socket.onmessage = function (evt) 
                { 
                    console.log(evt);
                    var data=JSON.parse(evt.data);
                    output.innerHTML+="<b>"+data.name+"</b> >> <b>"+data.message+"</b><br/>";
                };
            }
            function sendmessage(){
                var input=document.getElementById("Input").value;
                var myname=document.getElementById("MyName").value;
                var date=new Date();
                var data={};
                data.time=date.getHours()+":"+date.getMinutes();
                data.name=myname;
                data.message=input;
                Socket.send(JSON.stringify(data));
                return;
            }
            window.onbeforeunload = function(evt) {
                Socket.close();
            }//关闭窗口前先关闭连接,否则服务器会炸
        </script>
    </body>
</html>

./WS.js(服务端)

var ws = require("nodejs-websocket")           //添加Nodejs-WebSocket依赖
var port = 8001
console.log("你的服务器的地址是:ws://"+getIP()+":"+port+",快让你的朋友们进入聊天室吧")
var server=ws.createServer(function(conn){     //创建一个WebSocket服务器!
    conn.on("text", function (str) {                //监听收到消息事件(名为"text")
      server.connections.forEach(function (conn) {    //给每个连接的人都执行这个操作(server对象的connections进行foreach操作)
        conn.sendText(str)                              //给这个人发送刚才的消息     
      })
      console.log("收到消息:",str)                  //控制台输出刚刚消息的内容
    })
}).listen(port)                                //设置服务器开启的端口

//以下代码为获取电脑内网IP
function getIP(){
  var os=require("os")                              //引入OS依赖
  return os.networkInterfaces()['WLAN'][1].address  //返回Network信息中的内网IP
}

[^WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。]: 摘自Runoob HTML5 Websocket