websocket笔记以及一个微型聊天室例子

首先说明:这里的tomcat用的是tomcat8.0.36,并不适合tomcat7以及以下版本,(没办法websocket的api一直在变,到8之后貌似稳定下来了)

websocket也是html5的新增加内容之一,号称是下一代客户端/服务器异步通信办法,私以为虽然有点吹牛的成分,但是以后说不定能成为异步通信的半壁江山,至于取代ajax,我觉的应该不会

websocket的一个很有意思的特点就是双向通信,这一点其实也不稀奇,跟socket一样的。
我记得大二上学期的java课程设计我做的是一个仿照QQ的用户程序socket通信,写起来虽然顺畅但是因为那个c/s架构写起来痛苦不已,只实现一个简单的群聊就要大概一千五的代码量,尤其回调函数跟界面绑定的时候,写起来很X痛,从此就再没写过windows界面程序
下边是websocket的原理性知识总结是写给我自己看的,如果你没兴趣,可以跳过直接到代码:

  • tcp建立连接
    tcp连接的建立需要经历”三次握手“的过程。过程如下client发送SYN包(值为j)以及SEQ包到server端,此时client进入SYN_SEND状态。此为第一次握手。server端收到SYN包后,发送一个ACK(值为seq+1)确认包和SYN(值为k)给client,此时server进入SYN_RECV状态。此为第二次握手。client收到SYN+ACK包后,向server发送一个ACK(值为k+1),该包发送完成后,client和server均进入ESTABLISH状态。此为第三次握手。

client和server两端状态变化如下:

client:

CLOSED->SYN_SEND->ESTABLISH

server:

CLOSED->LISTEN->SYN_RECV->ESTABLISH

  • tcp连接释放
    Tcp释放连接的过程需要经历“四次挥手”的过程,为什么建立连接只需要3次握手,而释放连接需要进行4次挥手呢?很简单,因为TCP连接是全双工(Full Duplex)的,因此造成了两个方向都需要进行关闭。怎么理解呢?client和server,需要关闭连接,此时client通知server我要关闭连接了,此时关闭的只会是client这一端的连接,而server端并未关闭,它依旧能够向client发送数据。当然,关闭连接也可以是server作为主动方的。接下来以client主动断开与server端的连接为场景来描述整个过程,我们把它分为两个阶段,分别为client端关闭连接和server端关闭连接。

《websocket笔记以及一个微型聊天室例子》

第一阶段

首先client会发送一个FIN包给server(同时还有ack和seq包),这是要告诉server,我已经没有数据要发给你了,此时client处于FIN_WAIT_1状态。接收到FIN包的server处于CLOSE_WAIT的状态。
server发回一个ACK(值为client传过来的seq+1)和seq(值为client传过来的ack的值)给client。client收到server发过来的包后确认关闭连接,此时client处于FIN_WAIT_2。

第二阶段

server在接收到client的FIN后,得知client要断开tcp连接了,于是在发送完ack和seq给client后,自己发送一个FIN包给client(也带有ack和seq包),告诉client我也要断开连接了,此时server处于LAST_ACK状态。
client接收到server的FIN信息后,会回复server一个ack包,并且会进入TIME_WAIT状态,持续2个MSL(Max Segment Lifetime),这个时间windows下为240s。而server接收到client后便关闭连接。client在指定时间过后仍然没有接收到server的数据,确认server已经没有数据过来,也关闭了连接。
此时双方都进入CLOSED状态。
  • 建立细节

tcp是传输层的协议,tcp三次握手后,应用层协议http也便建立了连接。而对于当今web的发展情况,http仍有许多瓶颈。

一条连接只能发送一个请求。
请求只能从客户端开始。客户端不可以接收除响应以外的指令。
请求/响应首部未经压缩发送,首部信息越多延迟越大。
发送冗长的首部。每次互相发送相同的首部造成较多的浪费。
可任意选择数据压缩格式。非强制压缩发送。
虽然已经出现了很多解决方案,如ajax、comet,但是他们最终使用的都是http协议,因此也无法从根本上解决这些瓶颈。

因此也就诞生了一个新的通信协议,WebSocket协议,一种全双工通信协议。

该通信协议建立在http协议的基础之上,因此连接的发起方仍然是客户端,在http连接建立之后,再将协议升级为webSocket连接,在webSocket连接建立之后,客户度和服务器端都可以主动向对方发送报文信息了。

建立webSocket连接,需要先建立http连接,并在此基础上再进行一次”握手“。

client会发起一个”握手“的请求,请求首部含有upgrade:websocket(还有其他首部,具体看如下示例)。服务器端返回一个101状态码,确认转换协议。完成握手后便可以使用websocket协议进行通信。

Client(request)

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: AQIDBAUGBwgJCgsMDQ4PEC==
Origin: http://example.com
Sec-WebSocket-protocol: chat, superchat
Sec-WebSocket-Version: 13

server (response)

HTTP/1.1 101 switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat

websocket这个协议涉及前端显示,以及服务器处理,我这里使用基础的java+js来实现一个简单的群聊

如果不熟悉websocket的api,你可以看这里:https://developer.mozilla.org…
以及:https://www.ibm.com/developer…

《websocket笔记以及一个微型聊天室例子》

重要的只有两个文件:chat.java以及chat.html

Chat.java:

package example;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;


@ServerEndpoint(value="/ws/chat/{nickName}")
public class Chat {
    private static final Set<Chat> connections = new CopyOnWriteArraySet<Chat>();
    private String nickName;
    private Session session;
    
    
    public Chat(){
        
    }
    
    /*
     * 打开连接
     */    
    @OnOpen
    public void onOpen(Session session,@PathParam(value="nickName") String nickName){
        this.session=session;
        this.nickName=nickName;
        connections.add(this);
        System.out.println("新用户连接进入,名字是:"+this.nickName);
        String message=String.format("System>%s %s",this.nickName,"hasjoined.");
        Chat.broadCast(message);
        
    }
    /*
     * 关闭连接
     */
    @OnClose
    public void onClose(){
        connections.remove(this);
        String message=String.format("System> %s, %s", this.nickName,
                " has disconnection.");
        Chat.broadCast(message);
    }
    
    /*
     * 接收信息
     */
    @OnMessage
    public void onMessage(String message,@PathParam(value="nickName")String nickName){
        System.out.println("新消息from:"+nickName+" : "+message);
        Chat.broadCast(nickName+">"+message);
    }
    /*
     * 错误消息
     */
    @OnError
    public void onError(Throwable throwable){
        System.out.println(throwable.getMessage());
    }
    /*
     * 广播消息
     */
    private static void broadCast(String message){
        for(Chat chat:connections){
            try{
                synchronized (chat) {
                    chat.session.getBasicRemote().sendText(message);
                }
            }catch(IOException e){
                connections.remove(chat);
                try{
                    chat.session.close();
                    
                }catch(IOException e1){
                    chat.broadCast(String.format("System> %s %s", chat.nickName,
                            " has bean disconnection."));
                }
            }
        }
    }
}

chat.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Testing websockets</title>
</head>
<body>
  <div>
      <input type="text" id="yourName"/>
    <button id="start">click to start</button>
  </div>
  <div style="width:100%;">
      <div id="messages" style="border:5px solid red;width:50%;height:300px;margin-top:20px;"></div>
  </div>
  <div style="width:100%;margin-top:20px;">
      <div id="sends" style="float:left;width:50%;height:50px;margin-right:20px;">
          <input type="text"  id="MessageUN" style="width:100%;height:48px;"/>
      </div>
      <button style="float:left;width:10%;height:50px;" id="sendMessage">发送</button>
  </div>
  
  <script type="text/javascript">
  var button=document.getElementById("start");
  button.onclick=function(){
      
      
      
      
      var name=document.getElementById("yourName");
      console.log(name.value);
      var websocketAdd="ws://localhost:8080/t8j8/ws/chat/"+name.value;
      var webSocket=new WebSocket(websocketAdd); 

      
      function onMessage(event) {
            document.getElementById('messages').innerHTML 
              += '<br />' + event.data;
      }

      function onOpen(event) {
            document.getElementById('messages').innerHTML 
              = 'Connection established';
          //一旦链接开始,尝试发出一条通讯消息
             start();
          alert("消息通道开启,可以发送消息了");
          //确认链接开始,就可以开始消息的发送了
          //首先绑定一个点击事件
          var sendMessage=document.getElementById("sendMessage");
          sendMessage.onclick=function(){
              var message= document.getElementById('MessageUN').value;
              //我们之前会有一个唯一的标识符,就是在click to start之前的标识符
              webSocket.send(message);
              //上边一部完成之后,会自动触发onmessage事件
          }
          
      }
      
      
       function start() {
         webSocket.send(name.value+" : "+'hello');
       }

      function onError(event) {
            alert(event.data);
      }
      
      webSocket.onerror = function(event) {
            onError(event)
      };

      webSocket.onopen = function(event) {
            onOpen(event)
      };

      webSocket.onmessage = function(event) {
            onMessage(event)
      };      
        
  }
  
  </script>
</body>
</html>

一些问题:
WebSockets 简介:将套接字引入网络

WebSocket 是什么原理?为什么可以实现持久连接?(看尤雨溪大神的回答)

再谈应用环境下的TIME_WAIT和CLOSE_WAIT

如果你熟悉nodejs你还可以:
使用Node.js+Socket.IO搭建WebSocket实时应用

    原文作者:2fa0
    原文地址: https://segmentfault.com/a/1190000006898269
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞