WebSocket简介和例子

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Stubborn_Cow/article/details/78790990
WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
1,流程:在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
2,用法:当获取 Web Socket 连接后,可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
3,支持度:浏览器chrome 10以上, firefox 4以上 ,IE 11以上支持(浏览器在小版本更新时也可能增加支持,需要实测);后台如果是java,起码是jdk1.7版本以上。


例子使用spring boot + chrome
一,后台代码
1,添加pom依赖

<dependencies>
      <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>
	   <dependency>
	  <groupId>org.springframework.boot</groupId>
	  <artifactId>spring-boot-starter-websocket</artifactId>
	</dependency>
   </dependencies>

2,增加配置
@Configuration
public class WebSocketConfig {
	
	  @Bean
	    public ServerEndpointExporter serverEndpointExporter() {
	        return new ServerEndpointExporter();
	    }
}


3,逻辑代码

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

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();

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

    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
        try {
            sendMessage("当前人数");
        } catch (IOException e) {
            System.out.println("IO异常");
        }
    }

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

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);

        //群发消息
        for (MyWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


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


    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message) throws IOException {
        for (MyWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

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

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

    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }
}

二,html代码
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
Welcome<br/>
<input id="text" type="text" /><button οnclick="send()">Send</button>    <button οnclick="closeWebSocket()">Close</button>
<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("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }

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

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

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }

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

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

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

三,调试


展开阅读全文

求能用的 WebSocket 例子

11-29

以下 点链接后 就出现 close connection 不知是什么问题请高手看下rnhtml rn-----------------rnrnrnrnrnrn rnrnrnrn rnrn rnrn rnrnrnrnrnrnrn---------------------------rnpackage df;rnimport java.io.BufferedReader;rnimport java.io.ByteArrayOutputStream;rnimport java.io.IOException;rnimport java.io.InputStream;rnimport java.io.StringReader;rnimport java.net.ServerSocket;rnimport java.net.Socket;rnimport java.security.MessageDigest;rnimport java.security.NoSuchAlgorithmException;rnimport java.util.Arrays;rnimport java.util.HashMap;rnimport java.util.Map;rnrnrnrn/**rn * rn * @功能 WebServerSocket DEMOrn * @作者 蛋蛋rn */rnpublic class WebServerSocketrn rn private static final String TAG = "\r\n";rn public WebServerSocket(ServerSocket serverSocket)rn rn this.serverSocket = serverSocket;rn rn ServerSocket serverSocket = null;rn /**rn * 接收并握手,如果失败,则返回NULL,视为无效的WebSocket,rn * @returnrn * @throws IOException rn */rn public void close() rn rn tryrn rn this.serverSocket.close();rn rn catch (IOException e)rn rn e.printStackTrace();rn rn rn public Socket accept() throws IOExceptionrn rn Socket socket = serverSocket.accept();rn return startHandshake(socket) ? socket : null;rn rn /**rn * 握手rn * @param socketrn * @returnrn * @throws IOExceptionrn */rn private boolean startHandshake(Socket socket) throws IOExceptionrn rn socket.setSoTimeout(10000);rn InputStream in = socket.getInputStream();rn rn ByteArrayOutputStream requestStream = new ByteArrayOutputStream()rn // 原方法内容 return Arrays.copyOf(buf, count); 下面需要根据\r\n\r\n内容,进行跳出,避免重复创建对象rn @Overridern public synchronized byte[] toByteArray()rn rn return buf;rn rn ;rn rn while (Boolean.TRUE)rn rn requestStream.write((byte)in.read());rn byte[] data = requestStream.toByteArray();rn //如果当前数据以 \r\n\r\n结尾,则跳出rn if (data[requestStream.size() - 1] == 0x0A && data[requestStream.size() - 2] == 0x0D && data[requestStream.size() - 3] == 0x0A && data[requestStream.size() - 4] == 0x0D)rn rn break;rn rn rn //报文后跟的8个字节 稍后需要他计算MD5 rn byte[] coda = new byte[8];rn if (in.read(coda) != 8)rn rn return false;rn rn /*String str= "GET /demo HTTP/1.1 \r\n";rn str += "Host: example.com \r\n";rn str += "Connection: Upgrade\r\n";rn str += "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n";rn str += "Sec-WebSocket-Protocol: sample\r\n";rn str += "Upgrade: WebSocket\r\n";rn str += "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n";rn str += "Origin: http://example.com";rn String str1 = "^n:ds[4U";rn byte[] coda = str1.getBytes(); rn byte[] header = str.getBytes();/rn */rn byte[] header = Arrays.copyOf(requestStream.toByteArray(), requestStream.size());rn System.out.println("============================请求报文============================");rn System.out.println(new String(header));rn System.out.println("结尾的8个字节:" +BinaryUtil.formatBytes(coda));rn System.out.println("=======================R=====分隔线=============================");rn //解析请求报文rn Map headerMap = parseRequestHeader(header);rn rn rn //响应请求消息rn System.out.println("============================响应消息=============================");rn ResponseFrame frame = buildResponseMsg(headerMap,coda);rn rn //拼装的 报文头rn System.out.println(frame.getHeader());rn System.out.println("MD5(String)===" + new String(frame.getMd5()));rn System.out.println("MD5(bytes)===" + BinaryUtil.formatBytes(frame.getMd5()));rn rn //响应握手rn ByteArrayOutputStream baos = new ByteArrayOutputStream();rn baos.write(frame.getHeader().getBytes());rn baos.write(frame.getMd5());rn socket.getOutputStream().write(baos.toByteArray());rn socket.setSoTimeout(0);rn return true;rn rn rn /**rn * 构建响应消息报文实体.rn * @param header 请求消息的报文键值对rn * @param coda 请求消息的最后8个字节rn * @return framern * @throws IOExceptionrn */rn public static ResponseFrame buildResponseMsg(Map header, byte[] coda) throws IOExceptionrn rn ResponseFrame frame = new ResponseFrame(); rn /*rn * MD5 部分rn * 将 Sec-WebSocket-Key1 和 Sec-WebSocket-Key2 rn * 以及 请求报文后面的正文的8个字节 转成byte[]rn * 再进行MD5签名rn */rn ByteArrayOutputStream md5Bytes = new ByteArrayOutputStream();rn //协议是 Sec-WebSocket-Key1 和 Sec-WebSocket-Key2 . 我想可能会扩展,就循环了 ^_^!rn for (int i = 1;;i++)rn rn String key = header.get("Sec-WebSocket-Key" + i);rn if(key == null)break;rn md5Bytes.write(MD5(key));rn rn md5Bytes.write(coda);rn byte[] md5 = md5Bytes.toByteArray();rn tryrn rn //MD5 签名rn md5 = MessageDigest.getInstance("MD5").digest(md5);rn rn catch (NoSuchAlgorithmException e)rn rn e.printStackTrace();rn rn frame.setMd5(md5);rn rn /*rn * web socket 响应消息头 部分 ,类似HTTP协议,rn * rn */rn StringBuilder wsHead = new StringBuilder();rn wsHead.append("HTTP/1.1 101 Web Socket Protocol Handshake").append(TAG)rn .append("Upgrade: ").append("WebSocket").append(TAG)rn .append("Connection: ").append("Upgrade").append(TAG)rn .append("Sec-WebSocket-Origin: ").append(header.get("Origin")).append(TAG)rn .append("Sec-WebSocket-Location: ").append("ws://localhost/").append(TAG)rn //.append("Server: ").append("Kaazing Gateway").append(TAG)rn //.append("Date: ").append(new Date()).append(TAG)rn //.append("Access-Control-Allow-Origin: ").append(header.get("Origin")).append(TAG)rn //.append("Access-Control-Allow-Credentials: ").append("true").append(TAG)rn .append(TAG);rn frame.setHeader(wsHead.toString());rn return frame;rn rn rn /**rn * 解析请求消息报文rn * @param header Websocket协议的消息头. 最后rn * @return map rn * @throws IOExceptionrn */rn public static Map parseRequestHeader(byte[] header) throws IOExceptionrn rn Map map = new HashMap();rn BufferedReader reader = new BufferedReader(new StringReader(new String(header)));rn //走读 第一行. 即 : GET HTTP1.1 ... 他不在解析范围rn reader.readLine();rn //开始解析键值对: 如 Upgrade: WebSocket 则 Key=Upgrade Value=WebSocketrn while (reader.ready())rn rn tryrn String[] value = reader.readLine().split(": ");rn if(value.length != 2)rn rn break;rn rn map.put(value[0], value[1]);rn catch(Exception e)break;rn rn return map;rn rn rn /**rn * 根据WebSocket 协议rn * 如:rn * Sec-WebSocket-Key1: 1P:(~7 25Xq46t6ed9T2\ $0rn * 将值 "1P:(~7 25Xq46t6ed9T2\ $0" 只保留数字在 除以空格数.rn * 最后在转成 byte[] (高字节序,高位在前)rn * @param keyrn * @returnrn */rn public static byte[] MD5(String key)rn rn long number = Long.valueOf(key.replaceAll("\\D", ""));rn int spaceSum= key.replaceAll("\\S", "").length();rn return spaceSum == 0 ? null : BinaryUtil.formatInteage((int)(number / spaceSum));rn rn rn rn rn rn public static void main(String[] args) throws Exception rn rn WebServerSocket server = new WebServerSocket(new ServerSocket(1806));rn while(true)rn Socket socket=null;rn try rn //接收客户连接,只要客户进行了连接,就会触发accept();从而建立连接rn socket = server.accept();rn if(socket != null)rn rn System.out.println(socket.isClosed());rn System.out.println(socket);rn System.out.println("\n====开始接收 & 回发消息====");rn new CreateServerThread(socket);rn rn rn catch (Exception e) rn e.printStackTrace();rn rn rnrn rn /*if(socket != null)rn rn System.out.println(socket);rn System.out.println("\n====开始接收 & 回发消息====");rn new CreateServerThread(socket);rn new Thread(new Runnable()rn @Overridern public void run()rn rn tryrn rn InputStream in = socket.getInputStream();rn byte[] data = new byte[2048];rn int len = -1;rn rn while (-1 != (len = in.read(data)))rn rn byte[] bytes = Arrays.copyOf(data, len);rn //消息都是以0x00 打头, 0xFF结尾 收发消息 都是这种格式rn System.out.println(BinaryUtil.formatBytes(bytes));rn //去取 0x00 打头, 以及0xFF结尾rn String clientMsg = new String(bytes,1,bytes.length - 2,"UTF-8");rn System.out.println("收到WEB 客户端的消息:" + clientMsg);rn rn ByteArrayOutputStream msg = new ByteArrayOutputStream();rn msg.write(0x00);//0x00打头rn msg.write("服务端给你发消息了!!..".getBytes("UTF-8"));rn msg.write((byte)0xFF);//0xFF结尾rn rn socket.getOutputStream().write(msg.toByteArray());rn rn rn catch (IOException e)rn rn e.printStackTrace();rn rn rn ).start();rn */rn rn rnrnrnrn 论坛

没有更多推荐了,返回首页