WebSocket学习笔记

来源:互联网 发布:可怕 人心 知乎 编辑:程序博客网 时间:2024/05/19 18:41

H5的新规范,强调实时性,websocket传输的都是字符串。传统中的web请求-响应模式中实时传输的实现,comet可以一定程度上模拟双向通信,但是效率较低,并且需要服务器较好的支持,还有flash中的socket和xmlsocket可以真正的实现双向通信,通过flex ajax bridge,可以在js中使用这两个功能。而H5定义的websocket协议是将代替上面两个技术,它是建立在http协议的基础之上,可以更好的节省服务器资源和带宽并且达到实时通信,JavaEE7也实现了该协议。主要实现的是4个操作,在B-S的架构下面,客户端可以进行取放、服务器拿取的操作以及客户端进行拿取,服务器进行取放。
说明】这里的利用Tomcat自带的一个Demo,自己实现。这里新建WEB项目的时候直接引入Tomcat的目录即可,不再需要其他的jar包,这里我们用到的是两个jar,一个是tomcat7-websocket.jar和websocket-api.jar,前者实现了后者的接口。

DEMO 1

(Eclipse–>WebSocketTest)
首先是一个配置类:

package com.hhu.config;import java.util.Set;import javax.websocket.Endpoint;import javax.websocket.server.ServerApplicationConfig;import javax.websocket.server.ServerEndpointConfig;//这里实现的是ServerApplicationConfig,有两个方法,一种是注解启动,一种接口启动//这是一个启动类public class DemoConfig implements ServerApplicationConfig {    //注解方式 启动,这里它是自动执行的,自动扫描带有@ServerEndPoint(即终端)注解的类来自动注册    //这里我们通常都是用注解的方式来完成    @Override    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scan) {        System.out.println("config.........." + scan.size());        //返回scan,如果返回,服务器不会帮助注册任何Socket,提供了一定的过滤作用        return scan;    }    //接口方式 启动,由于使用了注解的方式,那这个接口的方式我们不予关注    @Override    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> arg0) {        // TODO Auto-generated method stub        return null;    }}

然后写Socket,这里包含了服务端对客户端所有的响应

package com.hhu.socket;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import javax.websocket.OnClose;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;/** * 就是Socket * @author Weiguo Liu * */@ServerEndpoint("/echo")public class EchoSocket {    //用来查看单例还是多例的    public EchoSocket() {        System.out.println("EchoSocket.EchoSocket()");    }    /*这个注解表示只要有人链接这个Socket,这个方法就会执行    *带进来一个Session,一个管道(会话)代表一次Session    */    @OnOpen    public void open(Session session) {        //一个Session代表一个通信会话(不同的客户端链接他们的建立的session的id不一样)        System.out.println("已进入open方法,session的id:" + session.getId());    }    /*     * 浏览器关闭的时候,自动调用@OnClose的方法,表示session会话结束     */    @OnClose    public void close(Session session){        System.out.println("当前session关闭:session.id" + session.getId());    }    /*     * 服务端接受客户端发送的信息之后所要执行的方法     */    @OnMessage    public void message(Session session,String msg) throws IOException {        System.out.println("客户端说" + msg);        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");        Date date = new Date();        String now = sdf.format(date);        //这里服务端也可以给客户端发送消息,当然如果是其他类别的消息需要用不同的sendxxx()方法        session.getBasicRemote().sendText(now + ":我是服务器,我已经接受你的消息,客户端说“" + msg + "”");    }}

前端的页面也很简单:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><html><body><h2>Hello World!</h2><input id="msg"/><button onclick="subOpen()">链接</button><button onclick="subSend()">发送</button><div id="servermsg"></div><script type="text/javascript">    var ws; //一个ws对象就是一个通信管道    //target表示的是请求地址,你要链接谁    var target = "ws://localhost:8080/WebSocketTest/echo";    function subOpen(){        if('WebSocket'in window) {            ws = new WebSocket(target);        } else if('MozWebSocket'in window) {            ws = new MozWebSocket(target);        } else {            alert("当前浏览器不支持WebSocket!");            return;        }        //客户端从服务端接收信息也是基于事件的,异步,所以在open的时候就要注册一个message        ws.onmessage = function(MsgEvent) {//这里返回的是一个事件对象!!            console.info(MsgEvent.data);//真实的服务端发送的消息在事件对象的data变量中            //往div中注入内容用document.getElementById("divid").innerHTML            //追加内容的时候就用+=,不追加就直接用=就好            document.getElementById("servermsg").innerHTML += MsgEvent.data;        }    }    //发送消息    function subSend() {        //获取输入框的内容        var msg = document.getElementById("msg").value;        //发送消息(客户端给服务端发送消息)        ws.send(msg);        //清空输入框中的内容        document.getElementById("msg").value = "";    }</script></body></html>

Demo2:聊天室–群聊+私聊

(Eclipse–>WebChat)
第二个Demo是实现了一个聊天室的常规功能:群聊和私聊,其实就是服务端向客户端发送消息所选择的Session不同而已其他的还是一样的。

还是一样,配置不能少:

package com.hhu.config;import java.util.Set;import javax.websocket.Endpoint;import javax.websocket.server.ServerApplicationConfig;import javax.websocket.server.ServerEndpointConfig;//这里实现的是ServerApplicationConfig,有两个方法,一种是注解启动,一种接口启动//这是一个启动类public class DemoConfig implements ServerApplicationConfig {    //注解方式 启动,这里它是自动执行的,自动扫描带有@ServerEndPoint(即终端)注解的类来自动注册    //这里我们通常都是用注解的方式来完成,有几个类(加了注解的)scan的size()就是多少    @Override    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scan) {        System.out.println("config.........." + scan.size());        //返回scan,如果返回,服务器不会帮助注册任何Socket,提供了一定的过滤作用        return scan;    }    //接口方式 启动,由于使用了注解的方式,那这个接口的方式我们不予关注    @Override    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> arg0) {        // TODO Auto-generated method stub        return null;    }}

然后是webSocket

package com.hhu.socket;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import javax.websocket.OnClose;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import com.google.gson.Gson;import com.hhu.util.ContentVo;import com.hhu.util.Message;/** * 建立聊天室的webSocket程序 * @author Weiguo Liu * *///标记前台请求的地址@ServerEndpoint("/chatSocket")public class ChatSocket {    private String username;    //这里声明为static类型的变量是为了将sessions整个变量变为公共的变量才能共同使用    private static List<Session> sessions = new ArrayList<Session>();    private static List<String> names = new ArrayList<String>();    private static Map<String,Session> map = new HashMap<String,Session>();    //在聊天室链接后(即成功创建聊天室的Socket)所要干的事儿    @OnOpen    public void open(Session session) {        //这里的session和Servlet中的session不是同一个session,        //要获取用户的名字,就要先从Servlet中获取,然后再在请求的时候将参数传过来即可        //getQueryString()可以将问号后面全部拿出来,在SpringMVC中又是不一样的        String user = session.getQueryString();        //将username=xx中的xx取出来        username = user.split("=")[1];        //将当前的session放到List中        this.sessions.add(session);        //将用户添加进List        this.names.add(username);        this.map.put(this.username, session);        Message msg = new Message();        msg.setWelcom("欢迎" + this.username + "进入聊天室!");        msg.setUsernames(names);        //进行广播,将这条消息广播到每一个session中        broadcast(this.sessions, msg.toJson());    }    //进行广播,将消息发送给每个session中,需要遍历每一个session    public void broadcast(List<Session> ss,String msg) {        Iterator<Session> iterator=ss.iterator();        while(iterator.hasNext()){            //获取每个通信通道            Session session = (Session)iterator.next();            try {                //向每个用户广播一条消息                session.getBasicRemote().sendText(msg);            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }     //session关闭时候的触发的方法    @OnClose    public void close(Session session) {        //在浏览器关闭时需要,将该session从List中移除,否则再进行广播的时候,该还会遍历这        //个已经移除的session,将会报错        this.sessions.remove(session);        this.names.remove(this.username);        Message message = new Message();        message.setWelcom(this.username + "已退出聊天室!");        message.setUsernames(this.names);        broadcast(this.sessions, message.toJson());    }    private static Gson gson = new Gson();    //接受到来自客户端发送的消息    @OnMessage    public void message(Session session, String json) {        //将json字符串转成ContentVo对象        ContentVo vo = gson.fromJson(json, ContentVo.class);        if(vo.getType()==1) {//广播            Message message = new Message();            message.setContent(this.username,vo.getMsg());            //这里不再是广播,而是选择性进行发送            broadcast(sessions, message.toJson());        } else {//单聊            String to = vo.getTo();            Session to_session = this.map.get(to);            Message message = new Message();            message.setContent(this.username, "<font color=red>私聊:</font>"+vo.getMsg());            try {                //发送消息                to_session.getBasicRemote().sendText(message.toJson());            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }}

登录跳转的Servlet:

package com.hhu.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * Servlet implementation class LoginServlet */public class LoginServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    /**     * @see HttpServlet#HttpServlet()     */    public LoginServlet() {        super();        // TODO Auto-generated constructor stub    }    /**      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)     */    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        //从提交的表单中获取“username”的值        String username = request.getParameter("username");        System.out.println("进入Servelet");        //将这个名字放到session中        request.getSession().setAttribute("username", username);        System.out.println("表单提交成功username=" + username);        //跳转到另一页面         response.sendRedirect("chart.jsp");    }    /**     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)     */    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        // TODO Auto-generated method stub        doGet(request, response);    }}

两个用于发送消息的工具类

package com.hhu.util;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;import com.google.gson.Gson;/** * 消息包装类 * @author Weiguo Liu * */public class Message {    private String welcom;    private List<String> usernames;    private String content;    public String getContent() {        return content;    }    public void setContent(String content) {        this.content = content;    }    public void setContent(String name,String msg) {        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");        this.content = sdf.format(new Date()) + "<br/>" + name + ":" + msg + "<br/>";    }    public String getWelcom() {        return welcom;    }    public void setWelcom(String welcom) {        this.welcom = welcom;    }    public List<String> getUsernames() {        return usernames;    }    public void setUsernames(List<String> usernames) {        this.usernames = usernames;    }    public Message() {        super();    }    private static Gson gson = new Gson();    public String toJson() {        return gson.toJson(this);    }    @Override    public String toString() {        return "Message [welcom=" + welcom + ", usernames=" + usernames + "]";    }}
package com.hhu.util;public class ContentVo {    private String to;    private String msg;    private Integer type;    public String getTo() {        return to;    }    public void setTo(String to) {        this.to = to;    }    public String getMsg() {        return msg;    }    public void setMsg(String msg) {        this.msg = msg;    }    public Integer getType() {        return type;    }    public void setType(Integer type) {        this.type = type;    }}

最后是登录界面只是简单的进行模拟,这里仅给出聊天室的界面

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><%@ page isELIgnored="false"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>聊天室</title><script type="text/javascript" src="${pageContext.request.contextPath }/resources/jquery-3.2.1.min.js"></script><script type="text/javascript">    //var username = "${sessionScope.username}";    /*当然也可以用上面的写法,这里需要注意的是一定要加引号,通过el表达式获取的是    *一个变量,只有加了引号才是字符串    */    var username = '${username}';    //进入页面就直接打开socket通道    var ws;    var target = "ws://localhost:8080/WebChat/chatSocket?username="+username;    window.onload = function() {         //判定浏览器是否支持websocket协议        if('WebSocket' in window) {            ws = new WebSocket(target);        } else if('MoWebSocket' in window) {            ws = new MozWebSocket(target);        } else {            alert("当前浏览器不支持WebSocket");            return;        }        //接受来自服务器的消息,返回一个事件对象,具体的返回内容在data属性中        ws.onmessage = function(event) {            //由于传过来的变成了json内容的字符串,所以这里所处理,eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码            eval("var msg=" + event.data + ";");            if(undefined!=msg.welcom){                //在div中追加消息                $("#content").append(msg.welcom+"</br>");            }            if(undefined!=msg.usernames){                //刷新的时候清空用户列表,否则刷新会造成用户的重复添加                $("#userList").html("");                //这是遍历的用户列表                $(msg.usernames).each(function(){                    $("#userList").append("<input type=checkbox value='" + this +"'/>" + this + "</br>");                });            }            //接受到服务端的消息后            if(undefined!=content) {                //在聊天追加用户发送的内容                $("#content").append(msg.content);            }        }    }    //向后发送消息    function subSend() {        //获取输入框中的值,这里的获取和清空都是jquery中的写法        var val = $("#msg").val();        console.info("msg===" + val);        //清空输入框中的值        $("#msg").val("");        //获取复选框选中了几个,这里注意有的时候写成size(),有时写成length,根据jquery的版本不同做适当调整        console.info($('#userList :checked').length);        var obj = null;        if($('#userList :checked').length==0) {            obj = {                msg:val,                type:1//1表示广播 2表示单聊            }        } else {            //获取发送对象,即复选框中的选中值            var to = $('#userList :checked').val();            //获取复选框中的选中值            console.info(to);            obj = {                to:to,                msg:val,                type:2//1表示广播 2表示单聊            }        }        var str = JSON.stringify(obj);//将obj转换成JSON字符串        ws.send(str);        $("#msg").val("");    }</script></head><body>    <!--聊天的主体窗口  -->    <div id="container" style="border: 1px solid black;width: 400px;height: 400px;float: left;">        <div id="content" style="height: 350px;">        </div>        <div style="border-top: 1px solid black;width: 400px;height: 55px;">            <input id="msg" style="border: 1px solid white;width: 348px;height:55px;"/>            <button onclick="subSend()">发送</button>        </div>    </div>    <!-- 聊天用户列表 -->    <div id="userList" style="border: 1px solid black;width: 100px;height: 390px;float: left;">    </div></body></html>

最后再来一个在线聊天的加强版来说明SpringMVC中websocket的使用,这个案例很管用!!!

(Eclipse–>SpringWebSocket)
websocket的配置文件

/** * WebScoket配置处理器 */@Component@EnableWebSocketpublic class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {    @Resource    MyWebSocketHandler handler;    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {        registry.addHandler(handler, "/ws").addInterceptors(new HandShake());        registry.addHandler(handler, "/ws/sockjs").addInterceptors(new HandShake()).withSockJS();    }}

HandShake处理类

/** * 每次建立连接都会进行握手 * @author Weiguo Liu * */public class HandShake implements HandshakeInterceptor {    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {        System.out.println("Websocket:用户[ID:" + ((ServletServerHttpRequest) request).getServletRequest().getSession(false).getAttribute("uid") + "]已经建立连接");        if (request instanceof ServletServerHttpRequest) {            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;            HttpSession session = servletRequest.getServletRequest().getSession(false);            // 标记用户            Long uid = (Long) session.getAttribute("uid");            if(uid!=null){                attributes.put("uid", uid);            }else{                return false;            }        }        return true;    }    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {    }}

拦截器

@Componentpublic class MyWebSocketHandler implements WebSocketHandler {    public static final Map<Long, WebSocketSession> userSocketSessionMap;    static {        userSocketSessionMap = new HashMap<Long, WebSocketSession>();    }    /**     * 建立连接后     */    public void afterConnectionEstablished(WebSocketSession session)            throws Exception {        Long uid = (Long) session.getAttributes().get("uid");        if (userSocketSessionMap.get(uid) == null) {            userSocketSessionMap.put(uid, session);        }    }    /**     * 消息处理,在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理     */    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {            if(message.getPayloadLength()==0)return;            Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);            msg.setDate(new Date());            sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));    }    /**     * 消息传输错误处理     */    public void handleTransportError(WebSocketSession session,            Throwable exception) throws Exception {        if (session.isOpen()) {            session.close();        }        Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap                .entrySet().iterator();        // 移除Socket会话        while (it.hasNext()) {            Entry<Long, WebSocketSession> entry = it.next();            if (entry.getValue().getId().equals(session.getId())) {                userSocketSessionMap.remove(entry.getKey());                System.out.println("Socket会话已经移除:用户ID" + entry.getKey());                break;            }        }    }    /**     * 关闭连接后     */    public void afterConnectionClosed(WebSocketSession session,            CloseStatus closeStatus) throws Exception {        System.out.println("Websocket:" + session.getId() + "已经关闭");        Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap                .entrySet().iterator();        // 移除Socket会话        while (it.hasNext()) {            Entry<Long, WebSocketSession> entry = it.next();            if (entry.getValue().getId().equals(session.getId())) {                userSocketSessionMap.remove(entry.getKey());                System.out.println("Socket会话已经移除:用户ID" + entry.getKey());                break;            }        }    }    public boolean supportsPartialMessages() {        return false;    }    /**     * 给所有在线用户发送消息     *      * @param message     * @throws IOException     */    public void broadcast(final TextMessage message) throws IOException {        Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap                .entrySet().iterator();        // 多线程群发        while (it.hasNext()) {            final Entry<Long, WebSocketSession> entry = it.next();            if (entry.getValue().isOpen()) {                // entry.getValue().sendMessage(message);                new Thread(new Runnable() {                    public void run() {                        try {                            if (entry.getValue().isOpen()) {                                entry.getValue().sendMessage(message);                            }                        } catch (IOException e) {                            e.printStackTrace();                        }                    }                }).start();            }        }    }    /**     * 给某个用户发送消息     *      * @param userName     * @param message     * @throws IOException     */    public void sendMessageToUser(Long uid, TextMessage message)            throws IOException {        WebSocketSession session = userSocketSessionMap.get(uid);        if (session != null && session.isOpen()) {            session.sendMessage(message);        }    }}

Spring中的配置:

    <mvc:annotation-driven />    <mvc:resources location="/resources/" mapping="/resources/**" />    <context:component-scan base-package="com.hhu" />    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">        <property name="prefix" value="/WEB-INF/views/" />        <property name="suffix" value=".jsp" />        <property name="order" value="1" />    </bean>

web.xml中常规配置即可,记得加上Spring的DispatcherServlet

前端界面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%    String path = request.getContextPath();    String basePath = request.getServerName() + ":"            + request.getServerPort() + path + "/";    String basePath2 = request.getScheme() + "://"            + request.getServerName() + ":" + request.getServerPort()            + path + "/";%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title></title><script type="text/javascript" src="<%=basePath2%>resources/jquery.js"></script><style>textarea {    height: 300px;    width: 100%;    resize: none;    outline: none;}input[type=button] {    float: right;    margin: 5px;    width: 50px;    height: 35px;    border: none;    color: white;    font-weight: bold;    outline: none;}.clear {    background: red;}.send {    background: green;}.clear:active {    background: yellow;}.send:active {    background: yellow;}.msg {    width: 100%;    height: 25px;    outline: none;}#content {    border: 1px solid gray;    width: 100%;    height: 400px;    overflow-y: scroll;}.from {    background-color: green;    width: 80%;    border-radius: 10px;    height: 30px;    line-height: 30px;    margin: 5px;    float: left;    color: white;    padding: 5px;    font-size: 22px;}.to {    background-color: gray;    width: 80%;    border-radius: 10px;    height: 30px;    line-height: 30px;    margin: 5px;    float: right;    color: white;    padding: 5px;    font-size: 22px;}.name {    color: gray;    font-size: 12px;}.tmsg_text {    color: white;    background-color: rgb(47, 47, 47);    font-size: 18px;    border-radius: 5px;    padding: 2px;}.fmsg_text {    color: white;    background-color: rgb(66, 138, 140);    font-size: 18px;    border-radius: 5px;    padding: 2px;}.sfmsg_text {    color: white;    background-color: rgb(148, 16, 16);    font-size: 18px;    border-radius: 5px;    padding: 2px;}.tmsg {    clear: both;    float: right;    width: 80%;    text-align: right;}.fmsg {    clear: both;    float: left;    width: 80%;}</style><script>        var path = '<%=basePath%>';        var uid=${uid eq null?-1:uid};        if(uid==-1){            location.href="<%=basePath2%>";        }        var from=uid;        var fromName='${name}';        var to=uid==1?2:1;        var websocket;        if ('WebSocket' in window) {            websocket = new WebSocket("ws://" + path + "/ws?uid="+uid);        } else if ('MozWebSocket' in window) {            websocket = new MozWebSocket("ws://" + path + "/ws"+uid);        } else {            websocket = new SockJS("http://" + path + "/ws/sockjs"+uid);        }        websocket.onopen = function(event) {            console.log("WebSocket:已连接");            console.log(event);        };        websocket.onmessage = function(event) {            var data=JSON.parse(event.data);            console.log("WebSocket:收到一条消息",data);            var textCss=data.from==-1?"sfmsg_text":"fmsg_text";            $("#content").append("<div class='fmsg'><label class='name'>"+data.fromName+"&nbsp;"+data.date+"</label><div class='"+textCss+"'>"+data.text+"</div></div>");            scrollToBottom();        };        websocket.onerror = function(event) {            console.log("WebSocket:发生错误 ");            console.log(event);        };        websocket.onclose = function(event) {            console.log("WebSocket:已关闭");            console.log(event);        }            function sendMsg(){                var v=$("#msg").val();                if(v==""){                    return;                }else{                    var data={};                    data["from"]=from;                    data["fromName"]=fromName;                    data["to"]=to;                    data["text"]=v;                    websocket.send(JSON.stringify(data));                    $("#content").append("<div class='tmsg'><label class='name'>我&nbsp;"+new Date().Format("yyyy-MM-dd hh:mm:ss")+"</label><div class='tmsg_text'>"+data.text+"</div></div>");                    scrollToBottom();                    $("#msg").val("");                }            }            function scrollToBottom(){                var div = document.getElementById('content');                div.scrollTop = div.scrollHeight;            }            Date.prototype.Format = function (fmt) { //author: meizz                 var o = {                    "M+": this.getMonth() + 1, //月份                     "d+": this.getDate(), //日                     "h+": this.getHours(), //小时                     "m+": this.getMinutes(), //分                     "s+": this.getSeconds(), //秒                     "q+": Math.floor((this.getMonth() + 3) / 3), //季度                     "S": this.getMilliseconds() //毫秒                 };                if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));                for (var k in o)                if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));                return fmt;            }            function send(event){                var code;                 if(window.event){                     code = window.event.keyCode; // IE                 }else{                     code = e.which; // Firefox                 }                if(code==13){                     sendMsg();                            }            }            function clearAll(){                $("#content").empty();            }        </script></head><body>    欢迎:${sessionScope.name }    <div id="content"></div>    <input type="text" placeholder="请输入要发送的信息" id="msg" class="msg" onkeydown="send(event)">    <input type="button" value="发送" class="send" onclick="sendMsg()" >    <input type="button" value="清空" class="clear" onclick="clearAll()"></body></html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%    String path = request.getContextPath();    String basePath= request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title></title><script type="text/javascript" src="<%=basePath%>resources/jquery.js"></script><script type="text/javascript">    var path='<%=basePath%>';    function broadcast(){        $.ajax({            url:path+'msg/broadcast',            type:"post",            data:{text:$("#msg").val()},            dataType:"json",            success:function(data){                alert("发送成功");            }        });    }</script></head><body>    发送广播    <textarea style="width:100%;height:300px;" id="msg" ></textarea>    <input type="button" value="发送" onclick="broadcast()"></body></html>