基于webSocket的聊天室
前言
不知大家在平时的需求中有没有遇到需要实时处理信息的情况,如站内信,订阅,聊天之类的。在这之前我们通常想到的方法一般都是采用轮训的方式每隔一定的时间向服务器发送请求从而获得最新的数据,但这样会浪费掉很多的资源并且也不是实时的,于是随着HTML5
的推出带来了websocket
可以根本的解决以上问题实现真正的实时传输。
websocket是什么?
至于websocket
是什么、有什么用这样的问题一Google一大把,这里我就简要的说些websocket
再本次实例中的作用吧。
由于在本次实例中需要实现的是一个聊天室,一个实时的聊天室。如下图:
采用websocket
之后可以让前端和和后端像C/S模式一样实时通信,不再需要每次单独发送请求。由于是基于H5的所以对于老的浏览器如IE7、IE8之类的就没办法了,不过H5是大势所趋这点不用担心。
后端
既然推出了websocket
,作为现在主流的Java肯定也有相应的支持,所以在JavaEE7
之后也对websocket
做出了规范,所以本次的代码理论上是要运行在Java1.7
+和Tomcat7.0+
之上的。
看过我前面几篇文章的朋友应该都知道本次实例也是运行在之前的SSM之上的,所以这里就不再赘述了。
首先第一步需要加入websocket
的依赖:
1 packagecom.css.tax.mobilebs.util;2 3 importjava.io.IOException;4 importjava.io.UnsupportedEncodingException;5 importjava.util.Date;6 importjava.util.HashMap;7 importjava.util.Iterator;8 importjava.util.Map;9 importjava.util.concurrent.CopyOnWriteArraySet;10 11 importjavax.websocket.OnClose;12 importjavax.websocket.OnError;13 importjavax.websocket.OnMessage;14 importjavax.websocket.OnOpen;15 importjavax.websocket.Session;16 importjavax.websocket.server.PathParam;17 importjavax.websocket.server.ServerEndpoint;18 19 importorg.g4studio.common.dao.Reader;20 importorg.g4studio.common.service.impl.BaseServiceImpl;21 importorg.g4studio.common.util.SpringBeanLoader;22 importorg.g4studio.common.web.BaseAction;23 importorg.g4studio.core.metatype.Dto;24 importorg.g4studio.core.metatype.impl.BaseDto;25 importorg.junit.Test;26 27 importcom.css.tax.mobilebs.Vo.CurrentUserVo;28 importcom.css.tax.mobilebs.serviceI.WebSocketService;29 importcom.css.tax.mobilebs.serviceI.ZjzzService;30 31 /** 32 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,33 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端34 */ 35 @ServerEndpoint("/websocket/{user}/{id}/{ptbz}")36 public class WebSocket extendsBaseAction{37 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 38 private static int onlineCount = 0;39 //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识 40 private static CopyOnWriteArraySet<Map<String, WebSocket>> webSocketSet = new CopyOnWriteArraySet<Map<String, WebSocket>>();41 private static CopyOnWriteArraySet<Map<String, WebSocket>> webSocketSetPt = new CopyOnWriteArraySet<Map<String, WebSocket>>();42 private WebSocketService zjzzWebSocketService = (WebSocketService)super.getService("zjzzWebSocketService");43 //与某个客户端的连接会话,需要通过它来给客户端发送数据 44 privateSession session;45 private Map<String, WebSocket> webSocketMap = new HashMap<String, WebSocket>();46 private CurrentUserVo currentUserVo = newCurrentUserVo();47 48 /** 49 * 连接建立成功调用的方法50 *51 *@paramsession52 * 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据53 */ 54 @OnOpen55 public void onOpen(@PathParam("user") String user,56 @PathParam("id") String id, @PathParam("ptbz") String ptbz,57 Session session) {58 String charset =getEncoding(user);59 try{60 byte[] b =user.getBytes(charset);61 user = new String(b, "utf-8");62 } catch(UnsupportedEncodingException e) {63 e.printStackTrace();64 }65 this.session =session;66 currentUserVo.setFbrmc(user);67 currentUserVo.setFbr(id);68 currentUserVo.setPtbz(ptbz);69 currentUserVo.setTwr_dm(id);70 currentUserVo.setPtbz(ptbz);71 webSocketMap.put(id, this);72 if("pt".equals(ptbz)) {73 currentUserVo.setZjmc(user);74 currentUserVo.setZjry_dm(id);75 currentUserVo.setKhdfwr(id);76 webSocketSetPt.add(webSocketMap);77 }else{78 currentUserVo.setNsrmc(user);79 webSocketSet.add(webSocketMap); //加入set中 80 }81 addOnlineCount(); //在线数加 82 System.out.println("有新连接加入!当前在线人数为" +getOnlineCount());83 }84 85 /** 86 * 连接关闭调用的方法87 */ 88 @OnClose89 public voidonClose() {90 if("pt".equals(this.currentUserVo.getPtbz())) {91 webSocketSetPt.remove(this.webSocketMap);92 }else{93 webSocketSet.remove(this.webSocketMap); //从set中删除 94 }95 subOnlineCount(); //在线数减 96 System.out.println("有一连接关闭!当前在线人数为" +getOnlineCount());97 }98 99 /** 100 * 收到客户端消息后调用的方法101 *102 *@parammessage103 * 客户端发送过来的消息104 *@paramsession105 * 可选的参数106 */ 107 @OnMessage108 public voidonMessage(String message,Session session) {109 boolean boo = false;110 111 System.out.println("来自" + currentUserVo.getFbrmc() + "的消息:" + message+",发向"+currentUserVo.getJsr_dm());112 //判断是否为未读消息 113 int a = message.indexOf("&&&");114 System.out.println(a);115 if(a!=-1) {//为未读消息 116 String[] msgStr = message.split("&&&");117 String userMes = msgStr[0];118 String[] user = userMes.split("@");119 String uuid = "";120 String wdjsr = "";121 String fbrmc = "";122 String fbr = "";123 String zjzzuuid = "";124 if(user.length>0) {125 wdjsr = user[0];126 uuid = user[1];127 fbrmc = user[2];128 fbr = user[3];129 zjzzuuid = user[4];130 }131 message = msgStr[1];132 CopyOnWriteArraySet<Map<String, WebSocket>> websocketSet = null;133 if("pt".equals(this.currentUserVo.getPtbz())) {134 websocketSet =webSocketSetPt;135 }else{136 websocketSet =webSocketSet;137 }138 if(!"everybody".equals(wdjsr)) {139 //消息有指定平台用户 140 for (Map<String, WebSocket>itemMap : websocketSet) {141 WebSocket socket =itemMap.get(wdjsr);142 if(socket!=null) {143 try{144 if("pt".equals(this.currentUserVo.getPtbz())) {145 socket.sendMessage(fbr+"@@"+fbrmc + "@^&" +message);146 socket.currentUserVo.setKhdfwr(this.currentUserVo.getFbr());147 }else{148 socket.sendMessage(fbrmc + "@^&" +message);149 socket.currentUserVo.setKhdfwr(fbr);150 }151 socket.currentUserVo.setJsr_dm(fbr);//设置该对话的接收人代码 152 socket.currentUserVo.setJsr(fbrmc);153 socket.currentUserVo.setZjzzuuid(zjzzuuid);154 boo = true;155 Dto reDto = newBaseDto();156 reDto.put("uuid", uuid);157 reDto.put("ckbz", "Y");158 reDto.put("xgr_dm", "111");159 reDto.put("xgsj", SystemUtils.dateFormat(newDate()));160 zjzzWebSocketService.updateLtxxDatas(reDto);161 } catch(IOException e) {162 e.printStackTrace();163 }164 }165 }166 }else{167 //未读消息没有指定平台用户 168 if("pt".equals(this.currentUserVo.getPtbz())) {169 Object[] objArr =webSocketSetPt.toArray();170 int index = (int) (Math.random() *objArr.length);171 Map<String, WebSocket> map = (Map<String, WebSocket>) objArr[index];172 for(WebSocket socket : map.values()) {173 if(socket!=null) {174 try{175 boo = true;176 socket.sendMessage(fbr+"@@"+fbrmc + "@^&" +message);177 socket.currentUserVo.setZjzzuuid(zjzzuuid);178 socket.currentUserVo.setJsr_dm(fbr);179 socket.currentUserVo.setJsr(fbrmc);180 socket.currentUserVo.setKhdfwr(socket.currentUserVo.getFbr());181 //修改未读对话记录 182 Dto reDto = newBaseDto();183 reDto.put("uuid", uuid);184 reDto.put("jsr", this.currentUserVo.getFbr());185 reDto.put("ckbz", "Y");186 reDto.put("khdfwr", this.currentUserVo.getKhdfwr());187 reDto.put("xgr_dm", "111");188 reDto.put("xgsj", SystemUtils.dateFormat(newDate()));189 zjzzWebSocketService.updateWdWzdDatas(reDto);190 } catch(IOException e) {191 e.printStackTrace();192 continue;193 }194 }195 }196 }197 }198 }else{199 String[] xxArr = message.split("@@");200 String bz = "";201 String zjzzuuid = "";202 String fsmessage = "";203 String jsr = "";204 if(!"pt".equals(this.currentUserVo.getPtbz())) {205 String userXx = xxArr[0];206 String[] user = userXx.split("&&");207 bz = user[0];208 zjzzuuid = user[1];209 this.currentUserVo.setZjzzuuid(zjzzuuid);210 fsmessage = xxArr[1];211 }else{212 String[] xx = message.split("&@");213 jsr = xx[0];214 fsmessage = xx[1];215 }216 if("pt".equals(this.currentUserVo.getPtbz())) {217 if(jsr!=null&&jsr.length()>0) {218 //平台发送消息,发向指定客户端 219 boolean pdboo = false;220 for (Map<String, WebSocket>itemMap : webSocketSet) {221 WebSocket socket =itemMap.get(jsr);222 if(socket!=null) {223 try{224 boo = true;225 pdboo = true;226 socket.sendMessage(currentUserVo.getFbrmc() + "@^&" +fsmessage);227 //this.currentUserVo.setJsr_dm(socket.currentUserVo.getFbr());228 //this.currentUserVo.setJsr(socket.currentUserVo.getFbrmc());229 //存储已查看对话信息 230 if(socket.currentUserVo.getZjzzuuid()==null) {231 socket.currentUserVo.setZjzzuuid(this.currentUserVo.getZjzzuuid());232 }233 saveYckMessage(socket.currentUserVo.getZjzzuuid(),"Y",fsmessage,"Y",jsr);234 } catch(IOException e) {235 e.printStackTrace();236 continue;237 }238 }239 }240 if(!pdboo) {241 this.currentUserVo.setJsr_dm(jsr);242 String uuid =zjzzWebSocketService.queryZjzzuuidByJsr(jsr);243 this.currentUserVo.setZjzzuuid(uuid);244 boo = true;245 saveYckMessage(uuid,"Y",fsmessage,"N",this.currentUserVo.getJsr_dm());246 }247 }248 }else{249 if(this.currentUserVo.getJsr_dm()!=null&&this.currentUserVo.getJsr_dm().length()>0) {250 //该客户端消息已有接收对象,顺利发送给平台 251 for (Map<String, WebSocket>itemMap : webSocketSetPt) {252 WebSocket socket =itemMap.get(currentUserVo.getJsr_dm());253 if(socket!=null) {254 try{255 boo = true;256 socket.sendMessage(currentUserVo.getFbr()+"@@"+currentUserVo.getFbrmc() + "@^&" +fsmessage);257 //存储已查看对话信息 258 saveYckMessage(zjzzuuid,bz,fsmessage,"Y",this.currentUserVo.getJsr_dm());259 } catch(IOException e) {260 e.printStackTrace();261 continue;262 }263 }264 }265 }else{266 //该客户端尚未有接收对象,需要随机指定接收对象,并连接发送消息 267 if(webSocketSetPt.size()>0){268 //平台有用户连接 269 Object[] objArr =webSocketSetPt.toArray();270 int index = (int) (Math.random() *objArr.length);271 Map<String, WebSocket> map = (Map<String, WebSocket>) objArr[index];272 for(WebSocket socket : map.values()) {273 if(socket!=null) {274 try{275 boo = true;276 socket.sendMessage(currentUserVo.getFbr()+"@@"+currentUserVo.getFbrmc() + "@^&" +fsmessage);277 socket.currentUserVo.setJsr_dm(this.currentUserVo.getFbr());278 socket.currentUserVo.setJsr(this.currentUserVo.getFbrmc());279 this.currentUserVo.setKhdfwr(socket.currentUserVo.getFbr());280 this.currentUserVo.setJsr_dm(socket.currentUserVo.getFbr());281 this.currentUserVo.setZjry_dm(socket.currentUserVo.getFbr());282 this.currentUserVo.setZjmc(socket.currentUserVo.getFbrmc());283 this.currentUserVo.setJsr(socket.currentUserVo.getFbrmc());284 //存储已查看对话信息 285 saveYckMessage(zjzzuuid,bz,fsmessage,"Y",this.currentUserVo.getJsr_dm());286 } catch(IOException e) {287 e.printStackTrace();288 continue;289 }290 }291 }292 }else{293 try{294 this.sendMessage("系统:消息已发送,但当前未有服务人员链接,无法为您解答疑问!");295 //平台没有用户连接,无法发送消息,保存数据为所有人可接收的未读消息 296 this.currentUserVo.setJsr_dm("everybody");297 this.currentUserVo.setZjry_dm("everybody");298 this.currentUserVo.setKhdfwr("everybody");299 saveYckMessage(zjzzuuid,bz,fsmessage,"N",this.currentUserVo.getJsr_dm());300 boo = true;301 } catch(IOException e) {302 e.printStackTrace();303 }304 }305 }306 }307 308 }309 if(!boo){310 if(this.currentUserVo.getJsr_dm()!=null&&this.currentUserVo.getZjzzuuid()!=null) {311 if("pt".equals(this.currentUserVo.getPtbz())) {312 saveYckMessage(this.currentUserVo.getZjzzuuid(),"Y",message,"N",this.currentUserVo.getJsr_dm());313 }else{314 //存储未查看对话信息 315 String[] xxArr = message.split("@@");316 String userXx = xxArr[0];317 String[] user = userXx.split("&&");318 String bz = user[0];319 String zjzzuuid = user[1];320 String fsmessage = xxArr[1];321 saveYckMessage(zjzzuuid,bz,fsmessage,"N",this.currentUserVo.getJsr_dm());322 }323 }else{324 try{325 this.sendMessage("系统:消息未发出,可能是连接失败!请重新连接");326 } catch(IOException e) {327 e.printStackTrace();328 }329 }330 }331 }332 333 /** 334 *335 *@paramzjzzuuid 对话组uuid336 *@parambz 是否存在对话组标志337 *@parammessage 对话信息338 *@paramckbz 对话是否查看标志339 */ 340 private voidsaveYckMessage(String zjzzuuid,String bz,String message,String ckbz,String jsr) {341 Dto dto = newBaseDto();342 String fbrmc =currentUserVo.getFbrmc();343 String fbr =currentUserVo.getFbr();344 String khdfwr =currentUserVo.getKhdfwr();345 if("N".equals(bz)) {//是否有uuid,判断其在数据库中是否存在已有信息346 //数据库没有该对话记录,故需创建对话记录 347 dto.put("uuid", zjzzuuid);348 //以下需前台传值 349 dto.put("twr_dm", currentUserVo.getTwr_dm());350 dto.put("nsrsbh", currentUserVo.getNsrsbh());351 dto.put("nsrmc", currentUserVo.getNsrmc());352 dto.put("zjmc", currentUserVo.getZjmc());353 dto.put("zjry_dm", currentUserVo.getZjry_dm());354 dto.put("fbr", fbr);355 dto.put("fbrmc", fbrmc);356 dto.put("jsr", jsr);357 dto.put("lrr_dm", fbr);358 dto.put("lrrq", SystemUtils.dateFormat(newDate()));359 zjzzWebSocketService.insertZjzzDhjl(dto);360 361 Dto dto1 = newBaseDto();362 dto1.put("lrr_dm", fbr);363 dto1.put("lrrq", SystemUtils.dateFormat(newDate()));364 dto1.put("uuid", SystemUtils.genUUID());365 dto1.put("zjzzuuid", zjzzuuid);366 dto1.put("dhnr", message);367 dto1.put("dhsj", SystemUtils.dateFormat(newDate()));368 dto1.put("fbr", fbr);369 dto1.put("ckbz", ckbz);370 dto1.put("khdfwr", khdfwr);371 dto1.put("fbrmc", fbrmc);372 dto1.put("jsr", jsr);373 zjzzWebSocketService.insertZjzzMxDhjlByUuid(dto1);374 }else{375 Dto redto = newBaseDto();376 redto.put("lrr_dm", fbr);377 redto.put("lrrq", SystemUtils.dateFormat(newDate()));378 redto.put("uuid", SystemUtils.genUUID());379 redto.put("zjzzuuid", zjzzuuid);380 redto.put("dhnr", message);381 redto.put("dhsj", SystemUtils.dateFormat(newDate()));382 redto.put("fbr", fbr);383 redto.put("ckbz", ckbz);384 redto.put("khdfwr", khdfwr);385 redto.put("fbrmc", fbrmc);386 redto.put("jsr", jsr);387 zjzzWebSocketService.insertZjzzMxDhjlByUuid(redto);388 }389 }390 391 /** 392 * 发生错误时调用393 *394 *@paramsession395 *@paramerror396 */ 397 @OnError398 public voidonError(Session session, Throwable error) {399 System.out.println("发生错误");400 error.printStackTrace();401 }402 403 /** 404 * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。405 *406 *@parammessage407 *@throwsIOException408 */ 409 public void sendMessage(String message) throwsIOException {410 this.session.getBasicRemote().sendText(message);411 //this.session.getAsyncRemote().sendText(message); 412 }413 414 public static synchronized intgetOnlineCount() {415 returnonlineCount;416 }417 418 public static synchronized voidaddOnlineCount() {419 WebSocket.onlineCount++;420 }421 422 public static synchronized voidsubOnlineCount() {423 WebSocket.onlineCount--;424 }425 426 public staticString getEncoding(String str) {427 String encode = "GB2312";428 try{429 if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是GB2312 430 String s =encode;431 return s; //是的话,返回GB2312,以下代码同理 432 }433 } catch(Exception e) {434 e.printStackTrace();435 }436 encode = "ISO-8859-1";437 try{438 if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是ISO-8859-1 439 String s1 =encode;440 returns1;441 }442 } catch(Exception e) {443 e.printStackTrace();444 }445 encode = "UTF-8";446 try{447 if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是UTF-8编码 448 String s2 =encode;449 returns2;450 }451 } catch(Exception e) {452 e.printStackTrace();453 }454 encode = "GBK";455 try{456 if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是GBK 457 String s3 =encode;458 returns3;459 }460 } catch(Exception e) {461 e.printStackTrace();462 }463 return ""; //到这一步,你就应该检查是不是其他编码啦 464 }465 }
这就是整个websocket
的后端代码。看起来也比较简单主要就是使用那几个注解。每当有一个客户端连入、关闭、发送消息都会调用各自注解的方法。这里我讲一下sendMessage()
这个方法。
websocket绕坑
在sendMessage()
方法中我只想实现一个简单的功能,就是将每次的聊天记录都存到数据库中。看似一个简单的功能硬是花了我半天的时间。
我先是按照以前的惯性思维只需要在这个类中注入service
即可。但是无论怎么弄每次都注入不进来都是null
。
最后没办法只有google了,最后终于在神级社区StackOverFlow
中找到了答案,就是前边所说的需要添加的第二个 maven
依赖,然后加入@ServerEndpoint(value = "/websocket",configurator = SpringConfigurator.class)
这个注解即可利用Spring
注入了。接着就可以做消息的保存了。
前端
前端我采用了Ext.js做的。还是先贴一下代码:
zxws_main.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <metacharset="UTF-8"> 5 <title>Parent Page</title> 6 <linkrel="stylesheet"type="text/css"href="css/Globle.css" /> 7 <linkrel="stylesheet"type="text/css"href="css/index.css" /> 8 <scriptsrc="js/jquery-1.12.0.min.js"type="text/javascript" 9 charset="utf-8"></script> 10 <styletype="text/css"> 11 #main_left div{ 12 background:#D1D1D1; 13 border-bottom:1px #DDDDDD solid; 14 } 15 16 #main_left input{ 17 font-size:14px; 18 text-align:center; 19 height:40px; 20 width:150px; 21 line-height:40px; 22 color:white; 23 } 24 25 #main_left span{ 26 background:red none repeat scroll 0 0; 27 border-radius:9px; 28 display:inline-block; 29 height:18px; 30 line-height:18px; 31 width:18px; 32 text-align:center; 33 color:white; 34 display:none; 35 36 } 37 38 #add{ 39 position:absolute; 40 bottom:200px; 41 } 42 .butt{ 43 height:30px; 44 width:40px; 45 margin-top:80px; 46 margin-left:-391px; 47 display:inline-block; 48 } 49 </style> 50 </head> 51 <body> 52 <buttonclass="butt"type="button"class="fs"onclick="()">清理</button> 53 <divid="main"> 54 <divid="main_left"> 55 <!--<h1>This is the Parent Page.</h1>--> 56 57 </div> 58 <divid="main_right"> 59 <iframestyle="width: 600px; height: 540px;"id="childframe" 60 name="childframe"src="zxws.jsp"></iframe> 61 </div> 62 </div> 63 <scripttype="text/javascript"src="js/map.js"></script> 64 65 </body> 66 <scripttype="text/javascript"src="zxws_main.js"></script> 67 </html>
zxws_main.js
1 /** 2 * Created by wanglei on 2017-07-03.3 */ 4 var user = GetQueryString("user");5 var id = GetQueryString("id");6 var ptbz = GetQueryString("ptbz");//客户端为:khd;平台为:pt; 7 var uuid = "";8 var websocket = null;9 var jsonStr = localStorage.getItem('WSBS_PT_ZXBS_'+id);10 var jsonObj2;11 var userOnLineMap = newMap();12 function GetQueryString(name) {13 var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");14 var r = decodeURI(window.location.search).substr(1).match(reg);15 if (r != null)16 return unescape(r[2]);17 return null;18 }19 var zxwin =window;20 var parentWin;21 //定义最后光标对象 22 23 var lastEditRange, selection;24 //判断当前浏览器是否支持WebSocket 25 if ('WebSocket'in window) {26 websocket = new WebSocket("ws://222.90.69.254:7001/mobile/websocket/" 27 + user+"/"+id+"/"+ptbz);28 } else{29 alert('当前浏览器 Not support websocket,请更换浏览器');30 }31 //连接发生错误的回调方法 32 websocket.onerror =function() {33 setMessageInnerHTML("WebSocket连接发生错误");34 };35 //连接成功建立的回调方法 36 websocket.onopen =function(aa) {37 //setMessageInnerHTML("WebSocket连接成功");38 //查看是否有未读消息,并发送消息 39 if(jsonStr==null||jsonStr.length==0||jsonStr=="{}") {40 jsonObj2 ={};41 var o = document.getElementById("main_right");42 o.style.visibility="hidden";43 }else{44 jsonObj2 =JSON.parse(jsonStr);45 //document.getElementById("main_right").innerHTML = "<iframe style='width:600px; height:540px;' id='childframe' name='childframe' src='zxws.jsp'></iframe>"; 46 }47 $.ajax({48 method:"POST",49 data:{50 "twr_dm":id,51 "ptbz":ptbz52 },53 url:"../zjzz.do?reqCode=initDhxx",54 success: function(msg){55 var datareq = eval('(' + msg + ')');56 if(msg!=null&&msg!='[]'){57 //debugger; 58 if("pt"!=ptbz) {59 uuid =datareq.uuid;60 document.getElementById("uuid").setAttribute("uuid", datareq.uuid);61 bz =datareq.bz;62 document.getElementById("uuid").setAttribute("bz", datareq.bz);63 }64 $.ajax({65 method:"POST",66 data : {67 //"zjzzuuid" : document.getElementById("uuid").getAttribute("uuid"), 68 "jsr": id69 },70 url:"../zjzz.do?reqCode=queryLtxxDatas",71 success: function(msg){72 //debugger;73 //初始化历史聊天记录 74 var json =eval(jsonObj2);75 //localStorage.removeItem('WSBS_PT_ZXBS_'+id); 76 var a = 1;77 if(jsonStr!=null&&jsonStr.length>0) {78 for(var prop in json)79 {80 var userArray = prop.split("@");81 var jsrid = userArray[0];82 var jsrmc = userArray[1];83 //alert(jsrid+"~~"+jsrmc); 84 if(jsrmc!=undefined&&jsrmc!=null&&jsrmc.length>0) {85 userOnLineMap.put(jsrid, jsrmc);86 $("#main_left").append("<div><input id='"+jsrid+"'/><span id='"+jsrid+"_span'></span></div>");87 $("#"+jsrid).attr("value",jsrmc);88 $("#"+jsrid).attr("type","button");89 $("#"+jsrid).attr("onclick","btnClick('"+jsrid+"')");90 if(a==1) {91 var jsrMsg =jsonObj2[prop];92 var obj = window.frames["childframe"].document;93 //debugger; 94 window.frames["childframe"].document.getElementById("uuid").setAttribute("bz", "Y");95 window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrid",jsrid);96 window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrmc",jsrmc);97 for (var i = 0; i < jsrMsg.length; i++) {98 var fsr =jsrMsg[i].fsr;99 if(fsr==jsrid) {100 var c1 = obj.getElementById("dhk");101 var div1 = obj.createElement("div");102 div1.className = "org_div";103 104 /*var now = new Date();105 var nowTime = now.toLocaleString();106 var date = nowTime.substring(0,10);//截取日期107 var time = nowTime.substring(10,20);*/ 108 109 var yhDiv = obj.createElement("span");110 yhDiv.className = "org_yh1";111 yhDiv.innerHTML = jsrMsg[i].fsrmc+":";112 var newDiv = obj.createElement("p");113 newDiv.className = "org_box";114 newDiv.innerHTML =jsrMsg[i].dhnr;115 var sjDiv = document.createElement("p");116 sjDiv.className = "org_sj2";117 sjDiv.innerHTML =jsrMsg[i].lrrq;118 //var lrrq = time;119 //sjDiv.innerHTML = lrrq; 120 div1.appendChild(yhDiv);121 div1.appendChild(newDiv);122 div1.appendChild(sjDiv);123 c1.appendChild(div1);124 }else{125 var c1 = obj.getElementById("dhk");126 var div1 = obj.createElement("div");127 div1.className = "org_div";128 129 /*var now = new Date();130 var nowTime = now.toLocaleString();131 var date = nowTime.substring(0,10);//截取日期132 var time = nowTime.substring(10,20);*/ 133 134 var yhDiv = obj.createElement("span");135 yhDiv.className = "org_yh";136 yhDiv.innerHTML = ":我";137 var newDiv = obj.createElement("p");138 newDiv.className = "org_hf";139 newDiv.innerHTML =jsrMsg[i].dhnr;140 var sjDiv = document.createElement("p");141 sjDiv.className = "org_sj1";142 //var lrrq = time;143 //sjDiv.innerHTML = lrrq; 144 sjDiv.innerHTML =jsrMsg[i].lrrq;145 div1.appendChild(yhDiv);146 div1.appendChild(newDiv);147 div1.appendChild(sjDiv);148 c1.appendChild(div1);149 c1.scrollTop =c1.scrollHeight;150 }151 }152 }153 a = a+1;154 }155 }156 }157 if(msg!=null){158 var datareq = eval('(' + msg + ')');159 for ( var int = 0; int < datareq.length; int++) {160 var msg = datareq[int].dhnr;161 var dhuuid = datareq[int].uuid;162 var wdjsr = datareq[int].jsr;163 var fbrmc = datareq[int].fbrmc;164 var fbr = datareq[int].fbr;165 var zjzzuuid = '';166 if("pt"!=ptbz) {167 zjzzuuid = document.getElementById("uuid").getAttribute("uuid");168 }else{169 zjzzuuid = datareq[int].zjzzuuid;170 }171 websocket.send(wdjsr+"@"+dhuuid+"@"+fbrmc+"@"+fbr+"@"+zjzzuuid+"&&&"+msg);172 }173 }174 }175 });176 }177 }178 });179 }180 //接收到消息的回调方法 181 websocket.onmessage =function(event) {182 debugger;183 var o = document.getElementById("main_right");184 if(o.style.visibility=="hidden") {185 o.style.visibility="visible";186 //document.getElementById("main_right").innerHTML = "<iframe style='width:600px; height:540px;' id='childframe' name='childframe' src='zxws.jsp'></iframe>"; 187 }188 var mes =event.data;189 var msg = "";190 var jsr = "";191 if(ptbz=='pt') {192 var arr = mes.split('@@');193 jsr = arr[0];//设置当前tab的id,即接收人 194 msg = arr[1];195 }else{196 msg=mes;197 }198 var fsrArr = msg.split('@^&');199 var fsr = fsrArr[0];200 msg = fsrArr[1];201 var xxUser =userOnLineMap.get(jsr);202 debugger;203 if(xxUser!=undefined && xxUser.length>0) {204 205 }else{206 userOnLineMap.put(jsr, fsr);207 $("#main_left").append("<div><input id='"+jsr+"'/><span id='"+jsr+"_span'></span></div>");208 $("#"+jsr).attr("value",fsr);209 $("#"+jsr).attr("type","button");210 $("#"+jsr).attr("onclick","btnClick('"+jsr+"')");211 }212 if (fsr !=user) {213 var obj = window.frames["childframe"].document.getElementById("uuid");214 if(obj.getAttribute("fsrid")!=undefined&&obj.getAttribute("fsrid")!=jsr) {215 var num = document.getElementById(jsr+"_span").innerHTML;216 if(num==null) {217 num=0;218 }219 var num1 = num-0+1;220 document.getElementById(jsr+"_span").innerHTML=num1;221 if(num1>0){222 document.getElementById(jsr+"_span").style.display='inline-block';223 }224 //如果是新用户进来得先走这步,所以再这里就要给它加上日期,如果是老用户的话直接跳到else里去执行了,走到发送里面在给加日期 225 var now = newDate();226 var lrrq =now.toLocaleString();227 var date = lrrq.substring(0,10);//截取日期 228 var time = lrrq.substring(10,20);229 230 var dataJson = {'fsr':jsr,'jsr':id,'fsrmc':fsr,'dhnr':msg,'lrrq':lrrq};231 document.getElementById("childframe").contentWindow.updateLocalStorage(jsr,fsr,dataJson);232 }else{233 obj.setAttribute("fsrid",jsr);234 obj.setAttribute("fsrmc",fsr);235 }236 if(obj.getAttribute("bz")==undefined){237 //debugger; 238 btnClick(jsr);239 }240 obj.setAttribute("bz","Y");241 document.getElementById("childframe").contentWindow.hfxx(jsr,fsr,msg);242 }243 }244 245 function btnClick(jsr){246 //alert(JSON.stringify(jsonObj2));247 //debugger; 248 document.getElementById(jsr+"_span").style.display='none';249 document.getElementById(jsr+"_span").innerHTML='';250 window.frames["childframe"].document.getElementById("dhk").innerHTML = '';251 localStorage.setItem('WSBS_PT_ZXBS_'+id, JSON.stringify(jsonObj2));252 window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrid",jsr);253 window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrmc",userOnLineMap.get(jsr));254 document.getElementById("childframe").contentWindow.initLocalStorage(jsr,id);255 256 //alert(window.frames["childframe"].document.getElementById("uuid").getAttribute("fsrid")); 257 }258 259 //连接关闭的回调方法 260 websocket.onclose =function() {261 localStorage.setItem('WSBS_PT_ZXBS_'+id, JSON.stringify(jsonObj2));262 //localStorage.removeItem('WSBS_PT_ZXBS_'+id);263 //setMessageInnerHTML("WebSocket连接关闭"); 264 }265 266 //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 267 window.onbeforeunload =function() {268 closeWebSocket();269 }270 271 //关闭WebSocket连接 272 function closeWebSocket() {273 websocket.close();274 }275 276 function send(data) {//获取当前tab的id,即接收人 277 websocket.send(data);278 }其实其中重要的就是那几个JS方法,都写有注释。需要注意的是这里
1 //判断当前浏览器是否支持WebSocket 2 if ('WebSocket'in window) {3 websocket = new WebSocket("ws://222.90.69.254:7001/mobile/websocket/" 4 + user+"/"+id+"/"+ptbz);5 } else{6 alert('当前浏览器 Not support websocket,请更换浏览器');7 }
zxws.jsp
<%@ page language="java"contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <htmllang="en"> <head> <metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"> <metaname="viewport"content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0" /> <title>在线问税</title> </head> <scripttype="text/javascript"src="../zjzz/js/jquery-1.12.0.min.js"charset="utf-8"></script> <scripttype="text/javascript"src="zxws.js"charset="utf-8"></script> <style>.org_box{float:left;text-align:left;font-family:"Microsoft YaHei", 微软雅黑;font-size:16px;margin-top:5px;height:auto;width:auto;max-width:55%;line-height:30px;border:1px solid #E3E3E3;background:#F6F6F6;border-radius:4px;padding:5px;display:inline-block; }.org_div{margin-top:8px;float:right;width:100%;word-break:break-all; }.org_hf{float:right;border:1px solid #87CEEB;background:#ACD9F8;border-radius:4px;padding:5px;height:auto;width:auto;line-height:30px;max-width:55%;word-break:break-all;display:inline-block;overflow:hidden;text-align:left; }.org_yh{float:right;height:20px;word-break:break-all; }.org_yh1{margin-top:10px;float:left;height:auto;width:auto;word-break:break-all; }.org_sj1{float:right;height:5px;font-size:8px;word-break:break-all;text-align:left;border-radius:4px;padding:5px;margin-top:0px; }.org_sj2{float:left;height:5px;font-size:8px;word-break:break-all;text-align:left;border-radius:4px;padding:0px;margin-top:9px; }*{margin:0px;padding:0px;list-style:none;box-sizing:border-box; }#zt{height:516px; }#dhk{margin-top:2px;height:300px;width:590px;overflow-Y:auto;border:1px solid #e7eaf1;margin-left:5px;margin-right:5px; }#lts{height:160px;overflow:auto;border:1px solid #e7eaf1;margin-left:5px;margin-right:5px; }#gjl{height:20%;overflow:auto; }#ltk{height:79.5%;overflow:auto;border:1px solid #e7eaf1;display:block;visibility:visible; }.close{position:absolute;top:5px;right:5px;z-index:1000;width:19px;height:19px;cursor:pointer;-webkit-user-select:none; }.minisize{position:absolute;right:38px;top:4px;z-index:1000;width:19px;height:19px;cursor:pointer;-webkit-user-select:none; }.fs{margin-left:500px;;background:#0f88eb;width:75px;color:#fff;border:1px solid #0f88eb;cursor:pointer;display:inline-block;font-size:14px;line-height:32px;padding:0 16px;text-align:center;float:left; }.qx{margin-left:20px;;background:#0f88eb;width:75px;color:#fff;border:1px solid #0f88eb;cursor:pointer;display:inline-block;font-size:14px;line-height:32px;padding:0 16px;text-align:center;float:left; }#bqb{position:absolute;width:250px;border:1px solid #e7eaf1;left:30px;top:300px;visibility:hidden; } </style><body><!--<button type="button" class="fs" οnclick="localStorage.removeItem('WSBS_PT_ZXBS_'+id);" id=" ">清理</button>--><divid="zt"><divid="bqb"><imgsrc="biaoqing/1.png"style="width: 27px; height: 27px;"onclick="bqdj(1);" /> <imgsrc="biaoqing/2.png"style="width: 27px; height: 27px;"onclick="bqdj(2);" /> <imgsrc="biaoqing/3.png"style="width: 27px; height: 27px;"onclick="bqdj(3);" /> <imgsrc="biaoqing/4.png"style="width: 27px; height: 27px;"onclick="bqdj(4);" /> <imgsrc="biaoqing/5.png"style="width: 27px; height: 27px;"onclick="bqdj(5);" /> <imgsrc="biaoqing/6.png"style="width: 27px; height: 27px;"onclick="bqdj(6);" /></div><divid="dhk"style="background: #EEEEEE"></div><divid="lts"><divid="gjl"><ul><li><imgonclick="tjbq()"src="picture/1.png"style="width: 27px; height: 27px; margin-left: 5px; margin-left: 5px;" /></li></ul></div><divid="ltk"contenteditable="true" ></div></div><divstyle="margin-top: 3px;"><!--<span id="subjectchk">还可输入 <strong id="checklen"style="color: #FF0000">400</strong> 个字符</span>--><buttontype="button"class="fs"onclick="fsxx();"id="uuid">发送</button><!--<button type="button" class="qx" οnclick="closewindow();">关闭</button>--></div></div></body> </html>
zxws.js
1 function fsxx() {2 var c = document.getElementById("ltk").innerHTML;3 var jsr = document.getElementById("uuid").getAttribute("fsrid");4 var fsrmc = document.getElementById("uuid").getAttribute("fsrmc");5 6 var now = newDate();7 var nowTime =now.toLocaleString();8 var date = nowTime.substring(0,10);//截取日期 9 var time = nowTime.substring(10,20);10 11 //var date = new Date();12 //var year = date.getFullYear();13 //var month = date.getMonth()+1;14 //var day = date.getDate();15 //var hour = date.getHours();16 //var minute = date.getMinutes();17 //var second = date.getSeconds();18 //alert(year+'年'+month+'月'+day+'日'+hour+':'+minute+':'+second); 19 20 window.parent.send(jsr+"&@"+c);21 var c1 = document.getElementById("dhk");22 var div1 = document.createElement("div");23 div1.className = "org_div";24 var yhDiv = document.createElement("span");25 yhDiv.className = "org_yh";26 yhDiv.innerHTML = ":我";27 var newDiv = document.createElement("span");28 newDiv.className = "org_hf";29 var msg =c ;30 newDiv.innerHTML =msg;31 var sjDiv = document.createElement("span");32 sjDiv.className = "org_sj1";33 var lrrq =nowTime;34 sjDiv.innerHTML =lrrq;35 var dataJson = {'fsr':window.parent.id,'jsr':jsr,'fsrmc':fsrmc,'dhnr':c,'lrrq':lrrq};36 updateLocalStorage(jsr,fsrmc,dataJson);37 div1.appendChild(yhDiv);38 div1.appendChild(newDiv);39 div1.appendChild(sjDiv);40 c1.appendChild(div1);41 /*var c2 = document.createElement("br");42 c1.appendChild(c2);*/ 43 document.getElementById("ltk").innerHTML = "";44 c1.scrollTop =c1.scrollHeight;45 }46 function hfxx(jsr,fsr,data) {47 var a = document.getElementById("uuid").getAttribute("fsrid");48 var fsrmc = document.getElementById("uuid").getAttribute("fsrmc");49 //var d = document.getElementById("time").innerHTML; 50 51 var now = newDate();52 var nowTime =now.toLocaleString();53 var date = nowTime.substring(0,10);//截取日期 54 var time = nowTime.substring(10,20);55 //var date = new Date();56 //var year = date.getFullYear();57 //var month = date.getMonth()+1;58 //var day = date.getDate();59 //var hour = date.getHours();60 //var minute = date.getMinutes();61 //var second = date.getSeconds();62 //alert(year+'年'+month+'月'+day+'日'+hour+':'+minute+':'+second); 63 64 if(a==jsr) {65 var c1 = document.getElementById("dhk");66 var div1 = document.createElement("div");67 div1.className = "org_div";68 var yhDiv = document.createElement("span");69 yhDiv.className = "org_yh1";70 yhDiv.innerHTML = fsr+":";71 var newDiv = document.createElement("span");72 newDiv.className = "org_box";73 newDiv.innerHTML =data;74 var sjDiv = document.createElement("span");75 sjDiv.className = "org_sj2";76 var lrrq =nowTime;77 sjDiv.innerHTML =lrrq;78 div1.appendChild(yhDiv);79 div1.appendChild(newDiv);80 div1.appendChild(sjDiv);81 c1.appendChild(div1);82 /*var c2 = document.createElement("br");83 c1.appendChild(c2);*/ 84 c1.scrollTop =c1.scrollHeight;85 var dataJson = {'fsr':a,'jsr':window.parent.id,'fsrmc':fsr,'dhnr':data,'lrrq':lrrq};86 updateLocalStorage(a,fsrmc,dataJson);87 }else if(a=="系统"){88 var c1 = document.getElementById("dhk");89 var div1 = document.createElement("div");90 div1.className = "org_div";91 var newDiv = document.createElement("span");92 newDiv.className = "org_box";93 newDiv.innerHTML =data;94 div1.appendChild(newDiv);95 c1.appendChild(div1);96 /*var c2 = document.createElement("br");97 c1.appendChild(c2);*/ 98 c1.scrollTop =c1.scrollHeight;99 }100 }101 //更新对话json串 102 var jsonObj1 =window.parent.jsonObj2;103 function updateLocalStorage(jsr,fsr,data) {104 //debugger; 105 var str = jsr+"@"+fsr;106 if(jsonObj1[str]==undefined) {107 var temp =[];108 jsonObj1[str] =temp;109 jsonObj1[str].push(data);110 }else{111 jsonObj1[str].push(data);112 }113 }114 function initLocalStorage(jsr,id) {115 jsonObj1 =window.parent.jsonObj2;116 var fsrid = document.getElementById("uuid").getAttribute("fsrid");117 var fsrmc = document.getElementById("uuid").getAttribute("fsrmc");118 var str = fsrid+"@"+fsrmc;119 var jsonStr = localStorage.getItem('WSBS_PT_ZXBS_'+id);120 if(jsonStr==null||jsonStr.length==0) {121 122 }else{123 var jsonMsg =JSON.parse(jsonStr);124 if(jsonMsg[str]!=undefined) {125 var jsrMsg =jsonMsg[str];126 for (var i = 0; i < jsrMsg.length; i++) {127 var fsr =jsrMsg[i].fsr;128 if(fsr==fsrid) {129 var c1 = document.getElementById("dhk");130 var div1 = document.createElement("div");131 div1.className = "org_div";132 var yhDiv = document.createElement("span");133 yhDiv.className = "org_yh1";134 yhDiv.innerHTML = jsrMsg[i].fsrmc+":";135 var newDiv = document.createElement("p");136 newDiv.className = "org_box";137 newDiv.innerHTML =jsrMsg[i].dhnr;138 var sjDiv = document.createElement("span");139 sjDiv.className = "org_sj2";140 sjDiv.innerHTML =jsrMsg[i].lrrq;141 div1.appendChild(yhDiv);142 div1.appendChild(newDiv);143 div1.appendChild(sjDiv);144 c1.appendChild(div1);145 c1.scrollTop =c1.scrollHeight;146 }else{147 var c1 = document.getElementById("dhk");148 var div1 = document.createElement("div");149 div1.className = "org_div";150 var yhDiv = document.createElement("span");151 yhDiv.className = "org_yh";152 yhDiv.innerHTML = ":我";153 var newDiv = document.createElement("p");154 newDiv.className = "org_hf";155 newDiv.innerHTML =jsrMsg[i].dhnr;156 var sjDiv = document.createElement("span");157 sjDiv.className = "org_sj1";158 sjDiv.innerHTML =jsrMsg[i].lrrq;159 div1.appendChild(yhDiv);160 div1.appendChild(newDiv);161 div1.appendChild(sjDiv);162 c1.appendChild(div1);163 c1.scrollTop =c1.scrollHeight;164 }165 }166 }167 }168 }169 170 function tjbq() {171 if(bqb.style.visibility=='visible'){172 bqb.style.visibility="hidden";173 }else{174 bqb.style.visibility="visible";175 }176 }177 178 function hiddenBq() {179 var bqb = document.getElementById("bqb");180 bqb.style.visibility = "hidden";181 }182 183 function bqdj(num) {184 var imgsrc = "biaoqing/" + num + ".png";185 showImg(imgsrc);186 hiddenBq();187 }188 189 function showImg(imgsrc) {190 var img = document.createElement("img");191 img.src =imgsrc;192 //img.onselectstart= function(){return false;} 193 var parent = document.getElementById("ltk");194 var div = document.createElement("div");195 div.appendChild(img);196 div.style.display = 'inline-block';197 div.onselectstart= function(){return false;}198 parent.appendChild(div);199 }200 function getByteLen(val) {201 var len = 0;202 for (var i = 0; i < val.length; i++) {203 var a =val.charAt(i);204 if (a.match(/[^\x00-\xff]/ig) != null) {205 len += 2;206 }207 else{208 len += 1;209 }210 }211 returnlen;212 }213 //只要键盘一抬起就验证编辑框中的文字长度,最大字符长度可以根据需要设定 214 function checkLength(obj) {215 var a = document.getElementById("ltk");216 debugger;217 var maxChars = 400;//最多字符数 218 var curr = maxChars -getByteLen(obj.innerHTML);219 if (curr > 0) {220 document.getElementById("checklen").innerHTML =curr.toString();221 } else{222 document.getElementById("checklen").innerHTML = '0';223 document.getElementById("ltk").readOnly = true;224 var oBtn = document.getElementById('fs');225 oBtn.onclick =function(){226 oBtn.disabled = 'disabled';227 };228 }229 }230 231 /** 232 * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。233 *234 *@parammessage235 *@throwsIOException236 */ 237 function xxql() {238 this.session.getBasicRemote().sendText(message);239 /*if("pt".equals(this.currentUserVo.getPtbz())) {240 webSocketSetPt.remove(this.webSocketMap);241 }else{242 webSocketSet.remove(this.webSocketMap); // 从set中删除243 }244 subOnlineCount(); // 在线数减245 System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());*/ 246 }247
注意:最后要在web-info下lib文件夹下引入websocket-api.jar,这样打war包才不会出错
转载于:https://www.cnblogs.com/wangxuemei/p/8417821.html
基于webSocket的聊天室相关推荐
- 基于WebSocket实现聊天室(Node)
基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议,服务端可以主动向前端传递数据,相比比AJAX轮询服务器,WebSocket采用监听的方式,减轻了服务器压力 ...
- java开发websocket聊天室_java实现基于websocket的聊天室
[实例简介] java实现基于websocket的聊天室 [实例截图] [核心代码] chatMavenWebapp └── chat Maven Webapp ├── pom.xml ├── src ...
- SpringBoot + Vue 实现基于 WebSocket 的聊天室(单聊)
前言 在前一篇文章SpringBoot 集成 STOMP 实现一对一聊天的两种方法中简单介绍了如何利用 STOMP 实现单聊,本文则将以一个比较完整的示例展示实际应用,不过本文并未使用 STOMP,而 ...
- SSM(五)基于webSocket的聊天室
SSM(五)基于webSocket的聊天室 前言 不知大家在平时的需求中有没有遇到需要实时处理信息的情况,如站内信,订阅,聊天之类的.在这之前我们通常想到的方法一般都是采用轮训的方式每隔一定的时间向服 ...
- java websocket netty_用SpringBoot集成Netty开发一个基于WebSocket的聊天室
前言 基于SpringBoot,借助Netty控制长链接,使用WebSocket协议做一个实时的聊天室. 项目效果 项目统一登录路径:http://localhost:8080/chat/netty ...
- 基于 WebSocket 的聊天室项目(下)
1.创建一个聊天室的数据库 注意修改上一篇准备工作中写的配置文件中的数据库名称:: 创建数据库,如果不存在`websocket_chatroom`默认字符集`utf8`; 使用`websocket_c ...
- 基于django channel 实现websocket的聊天室
websocket 网易聊天室? web微信? 直播? 假如你工作以后,你的老板让你来开发一个内部的微信程序,你需要怎么办?我们先来分析一下里面的技术难点 消息的实时性? 实现群聊 现在有 ...
- 视频教程-基于Java的WebSocket的聊天室-Java
基于Java的WebSocket的聊天室 多年 Java 企业级应用开发工作经验,曾参与中国人寿.华夏人寿.泰康人寿.信达财险等保险行业内部业务管理系统以及线上在线产品的开发:参与中国人民银行.清华大 ...
- Python用tornado的websocket开发聊天室
Python用tornado的websocket开发聊天室 用tornado开发基于异步websocket聊天室-demo 目录结构 Python包 main.py app/views.py temp ...
最新文章
- pycuda write complex numbers — errors:class “cuComplex” has no member “i”
- 王石:我每天都强迫自己做的一件事
- document.getElementById 学习总结
- oracle添加表的索引,oracle批量添加指定表前缀的索引SQL语句
- .net framework摘抄与理解
- MongoDB 如何使用内存?为什么内存满了?
- 【原创】关于MATLAB中imagesc函数图像保存的问题
- linux 3.10中完成量的使用
- php 5.4 安装xcache,安装xcache为你的PHP加速
- 2019118_四个化学数据分析(1)
- JDBCUtils——DBCP
- Spring Cloud Alibaba Sentinel之热点参数限流篇
- CentOS7 下MariaDB安装与简单配置
- 有哪些免费批量删除PDF文档的页码的方法
- 解决IOS浏览器或者微信浏览器播放audio音效第二次播放不全
- 如何用手机远程控制电脑
- MPCS-314 1A 光电耦合器 用于IGBT/MOSFET隔离栅极驱动 完美代替ELS3150 亿光
- android面试题分析总结
- Fusion APP-添加检查软件更新功能
- mysql中如何统计数据_mysql中的数据统计方法
热门文章
- 【教程】openstack all-in-one 方式安装stein版本教程
- 大学python搜题软件_中国大学MOOC的APP(慕课)用Python玩转数据答案搜题公众号
- 百度地图 设置缩放等级
- Burpsuite安全测试测试指导
- Effective C++(编写new和delete时需固守常规)
- 好看留言板源码php,挑战最棒的留言本的源码(一)
- 风力机叶片气动设计 matlab 程序,基于Matlab的1.5MW风力机叶片设计和优化方法
- win 10计算机文件,教你win10电脑怎么打开ai文件
- 微信公众号管理欧宁泰php,EasySwoole搭建高效微信管理后台
- CAD怎么添加剖面符号?CAD剖面符号绘制教程