Java for Web学习笔记(四七) WebSocket(4)Java Client和二进制消息

               

小例子说明

不是所有的Client都是前端页面,服务器也可能发起一个WebSocket连接,向其他服务器请求某项服务。小例子模拟两个WebSocket客户端,向server建立连接,当server收到消息时,向所有的连接的client分发该消息,当某个client连接或者关闭连接时,向其他client发布状态变化消息。

为了方便测试,client和server都在同一个web app中,要求client启动连接和发送消息,使用servlet进行触发。

二进制的消息

二进制消息需要进行序列化。在Java中对象序列化比JSON要高效,但是与非Java的程序互动,流行JSON。

public class ClusterMessage implements Serializable{    private static final long serialVersionUID = 1L;    private String nodeId;    private String message;    public ClusterMessage(){            }    public ClusterMessage(String nodeId , String message){        this.nodeId = nodeId;        this.message = message;    }    public String getNodeId() {        return nodeId;    }    public void setNodeId(String nodeId) {        this.nodeId = nodeId;    }    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }        }

Cluster Server代码

我们重点学习如何解析二进制消息,如何发送二进制消息。

@ServerEndpoint("/clusterNodeSocket/{nodeId}")public class ClusterNodeServerEndpoint {    //存储所有的client,以便进行消息广播    private static final List nodes = new ArrayList<>(2);    @OnOpen    public void onOpen(Session session, @PathParam("nodeId") String nodeId){        System.out.println("Node [" + nodeId  + "] connected to cluster server");        ClusterMessage message = new ClusterMessage(nodeId, "Joined the cluster.");        try {            byte[] bytes = ClusterNodeServerEndpoint.toByteArray(message);            for(Session node : nodes){                node.getBasicRemote().sendBinary(ByteBuffer.wrap(bytes));            }        } catch (IOException e) {            System.out.println("Exception when notifying of new node : " + e.toString());            e.printStackTrace();        }        ClusterNodeServerEndpoint.nodes.add(session);    }    @OnMessage    public void onMessage(Session session, byte[] message){        try{            for(Session node : ClusterNodeServerEndpoint.nodes){                if(node != session)                    node.getBasicRemote().sendBinary(ByteBuffer.wrap(message)); //发送二进制消息byte[]            }        }catch (IOException e) {            logger.error("Exception when handling message on server : " + e.toString());            e.printStackTrace();        }    }    @OnClose    public void onClose(Session session, @PathParam("nodeId") String nodeId){        System.out.println("Node [" + nodeId + "] disconnected.");        ClusterNodeServerEndpoint.nodes.remove(session);        ClusterMessage message = new ClusterMessage(nodeId, "Left the cluster.");        try{            for(Session node : ClusterNodeServerEndpoint.nodes){                node.getBasicRemote().sendBinary(ByteBuffer.wrap(                                                    ClusterNodeServerEndpoint.toByteArray(message)));            }        }catch (IOException e) {            System.out.println("Exception when notifying of left node : " + e.toString());            e.printStackTrace();        }            }    //将对象转换为byte[]: message(ClusterMessage) -> stream(ObjectOutputStream) --> output(ByteArrayOutputStream)    private static byte[] toByteArray(ClusterMessage message) throws IOException {        try(ByteArrayOutputStream output = new ByteArrayOutputStream();            ObjectOutputStream stream = new ObjectOutputStream(output)){               stream.writeObject(message);                return output.toByteArray();        }    }}

Cluster Client代码

我们模拟两个webSocket clients,为了方便触发,采用Servlet。在web.xml中定义如下,将两个servlet指向同一个类

   <servlet>       <servlet-name>clusterNode1servlet-name>       <servlet-class>cn.wei.flowingflying.chapter10.cluster.ClusterNodeServletservlet-class>       <init-param>           <param-name>nodeIdparam-name>           <param-value>1param-value>       init-param>   servlet>   <servlet-mapping>       <servlet-name>clusterNode2servlet-name>       <url-pattern>/clusterNode2url-pattern>   servlet-mapping>   <servlet>       <servlet-name>clusterNode2servlet-name>       <servlet-class>cn.wei.flowingflying.chapter10.cluster.ClusterNodeServletservlet-class>       <init-param>           <param-name>nodeIdparam-name>           <param-value>2param-value>       init-param>   servlet>   <servlet-mapping>       <servlet-name>clusterNode2servlet-name>       <url-pattern>/clusterNode2url-pattern>   servlet-mapping>

WebSocket代码如下:

//annotation @ClientEndpoint 不会像@ServiceEndpoint 那样自动实例化,只是作为一个有效endpoint的标记@ClientEndpointpublic class ClusterNodeServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    private String nodeId;    private Session session;    //在init()时读取配置信息,建立webSocket连接    @Override    public void init() throws ServletException {        this.nodeId = this.getInitParameter("nodeId");        String path = this.getServletContext().getContextPath() +                      "/clusterNodeSocket/" + this.nodeId;        try {            //【1】连接webSocket server            URI uri = new URI("ws","localhost:8080",path,null,null);                    this.session = ContainerProvider.getWebSocketContainer().connectToServer(this, uri);                } catch (URISyntaxException  | DeploymentException | IOException e) {            e.printStackTrace();            throw new ServletException("Cannot connect to " + path + ".", e);        }    }    @Override    public void destroy() {        try {            this.session.close(); //关闭webSocket连接        } catch (IOException e) {            e.printStackTrace();        }        super.destroy();    }    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        //【2】发送对象的二进制消息        ClusterMessage message = new ClusterMessage(this.nodeId,                  "request:{ip:\"" + request.getRemoteAddr() + "\",queryString:\"" + request.getQueryString() + "\"}");        try(OutputStream output = this.session.getBasicRemote().getSendStream();            ObjectOutputStream stream = new ObjectOutputStream(output)){            stream.writeObject(message);        }        response.getWriter().append("OK");    }    @OnMessage    public void onMessage(InputStream input){        //【3】读二进制信息        try(ObjectInputStream stream = new ObjectInputStream(input)){            ClusterMessage message = (ClusterMessage)stream.readObject();            System.out.println("Node [" + this.nodeId + "]: Message received from cluster; node = " +                               message.getNodeId() + ", message = " + message.getMessage());        }catch (IOException | ClassNotFoundException e) {            e.printStackTrace();        }     }    @OnClose    public void onClose(CloseReason reason){        CloseReason.CloseCode code = reason.getCloseCode();        if(code != CloseReason.CloseCodes.NORMAL_CLOSURE){            System.out.println("WebSocket connection closed unexpectedly;" +                               " code = " + code + ", reason = " + reason.getReasonPhrase());        }    }}

相关链接: 我的Professional Java for Web Applications相关文章
           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

你可能感兴趣的:(Java for Web学习笔记(四七) WebSocket(4)Java Client和二进制消息)