WebSocket简单使用

一.Socket的概要

Socket又称"套接字",应用程序一般经过"套接字"向网络发出请求或者应答网络请求。Socket的英文原义是“孔”或“插座”,做为UNIX的进程通讯机制。Socket能够实现应用程序间网络通讯。javascript

Socket可使用TCP/IP协议或UDP协议。html

TCP/IP协议前端

TCP/IP协议是目前应用最为普遍的协议,是构成Internet国际互联网协议的最为基础的协议,由TCP和IP协议组成:
TCP协议:面向链接的、可靠的、基于字节流的传输层通讯协议,负责数据的可靠性传输的问题。java

IP协议:用于报文交换网络的一种面向数据的协议,主要负责给每台网络设备一个网络地址,保证数据传输到正确的目的地。web

UDP协议后端

UDP特色:无链接、不可靠、基于报文的传输层协议,优势是发送后不用管,速度比TCP快。浏览器

二.WebSocket的概要

B/S架构的系统多使用HTTP协议,HTTP协议的特色:安全

1 无状态协议
2 用于经过 Internet 发送请求消息和响应消息
3 使用端口接收和发送消息,默认为80端口
底层通讯仍是使用Socket完成。服务器

 

HTTP协议决定了服务器与客户端之间的链接方式,没法直接实现消息推送(F5已坏),一些变相的解决办法:websocket

双向通讯与消息推送

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后立刻返回响应信息并关闭链接。  优势:后端程序编写比较容易。  缺点:请求中有大半是无用,浪费带宽和服务器资源。  实例:适于小型应用。

长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住链接,直到有新消息才返回响应信息并关闭链接,客户端处理完响应信息后再向服务器发送新的请求。  优势:在无消息的状况下不会频繁的请求,耗费资小。  缺点:服务器hold链接会消耗资源,返回数据顺序无保证,难于管理维护。 Comet异步的ashx, 实例:WebQQ、Hi网页版、Facebook IM。

长链接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长链接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。  优势:消息即时到达,不发无用请求;管理起来也相对便。  缺点:服务器维护一个长链接会增长开销。  实例:Gmail聊天

Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript经过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通讯,JavaScript在收到服务器端传送的信息后控制页面的显示。  优势:实现真正的即时通讯,而不是伪即时。  缺点:客户端必须安装Flash插件;非HTTP协议,没法自动穿越防火墙。  实例:网络互动游戏。

Websocket:
WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通信的网络技术。依靠这种技术能够实现客户端和服务器端的长链接,双向实时通讯。
特色:
事件驱动
异步
使用ws或者wss协议的客户端socket

可以实现真正意义上的推送功能

缺点:

少部分浏览器不支持,浏览器支持的程度与方式有区别。

 

WebSocket协议支持(在受控环境中运行不受信任的代码的)客户端与(选择加入该代码的通讯的)远程主机之间进行全双工通讯。用于此的安全模型是Web浏览器经常使用的基于原始的安全模式。 协议包括一个开放的握手以及随后的TCP层上的消息帧。 该技术的目标是为基于浏览器的、须要和服务器进行双向通讯的(服务器不能依赖于打开多个HTTP链接(例如,使用XMLHttpRequest或<iframe>和长轮询))应用程序提供一种通讯机制。
 

三.WebSocket实例

客户端(Web主页)代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>WebSocket前端</title>
  </head>
  <body>
    Welcome<br/><input id="text" type="text"/>
    <button onclick="send()">发送消息</button>
    <hr/>
    <button onclick="closeWebSocket">关闭WebSocket</button>
    <hr/>
    <div id="message"></div>

  </body>
  <script type="text/javascript">
    var websocket=null;
    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        websocket=new WebSocket("ws://localhost:8080/websocket");
    }else{
        alert("当前浏览器 Not support websocket");
    }

    //链接发生错误的回调方法
    websocket.onerror=function(){
        setMessageInnerHTML("WebSocket链接发生错误");
    }

    //接收到消息的回调方法
    websocket.onmessage=function (event) {
        setMessageInnerHTML(event.data);
    }

    //链接关闭的回调方法
    websocket.onclose=function () {
        setMessageInnerHTML("WebSocket链接关闭");
    }

    //监听窗口关闭时间,当窗口关闭时,主动去关闭websocket链接,防止链接还没断开就关闭窗口,server端会抛出异常。
    window.onbeforeprint=function () {
        closeWebSocket();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML+=innerHTML+'<br/>';
    }

    //关闭WebSocket链接
    function closeWebSocket() {
        websocket.close();
    }

    //发送消息
    function send() {
        var message=document.getElementById('text').value;
        websocket.send(message);
    }
  </script>
</html>

后台代码:

package com;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;


@ServerEndpoint("/websocket")
public class WebSocketTest {
    //静态变量,用来记录当前在线链接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每一个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通讯的话,可使用Map来存放,其中Key能够为用户标识
    private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();

    //与某个客户端的链接会话,须要经过它来给客户端发送数据
    private Session session;

    /**
     * 链接创建成功调用的方法
     * @param session  可选的参数。session为与某个客户端的链接会话,须要经过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新链接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose(){
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一链接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);
        //群发消息
        for(WebSocketTest item: webSocketSet){
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    /**
     * 发生错误时调用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 这个方法与上面几个方法不同。没有用注解,是根据本身须要添加的方法。
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException{
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketTest.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketTest.onlineCount--;
    }
}
相关文章
相关标签/搜索