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+" "+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'>我 "+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>
- websocket学习笔记
- WebSocket学习笔记一
- websocket学习笔记
- WebSocket学习笔记
- WebSocket学习笔记
- Websocket学习笔记
- Android WebSocket 学习笔记
- WebSocket学习笔记
- websocket学习笔记
- WebSocket 学习笔记
- websocket 学习笔记
- WebSocket学习笔记——无痛入门
- HTML5下的WebSocket学习笔记
- MQTT学习笔记-让Mosquitto支持Websocket
- WebSocket学习笔记——无痛入门
- WebSocket学习笔记——无痛入门
- Spring Boot学习笔记(六) WebSocket
- WebSocket笔记
- 字符转换为16进制数字
- c/c++动态内存管理
- JAVAWEB开发之权限管理(三)——shiro与企业项目整合开发(基于Spring)
- crond和crontab
- liunx系统负载量以及性能的分析
- WebSocket学习笔记
- 16进制数字转换为字符
- Android消息处理机制认识过程总结
- 数据库设计规范
- centos7
- u3d创建精灵
- 从宏定义到inline函数
- 一周Python数据类型学习-----数字
- 树莓派3创建ftp服务及远程桌面配置