前言

不知大家在平时的需求中有没有遇到需要实时处理信息的情况,如站内信,订阅,聊天之类的。在这之前我们通常想到的方法一般都是采用轮训的方式每隔一定的时间向服务器发送请求从而获得最新的数据,但这样会浪费掉很多的资源并且也不是实时的,于是随着HTML5的推出带来了websocket可以根本的解决以上问题实现真正的实时传输。

websocket是什么?

至于websocket是什么、有什么用这样的问题一Google一大把,这里我就简要的说些websocket再本次实例中的作用吧。
由于在本次实例中需要实现的是一个聊天室,一个实时的聊天室。如下图:

采用websocket之后可以让前端和和后端像C/S模式一样实时通信,不再需要每次单独发送请求。由于是基于H5的所以对于老的浏览器如IE7、IE8之类的就没办法了,不过H5是大势所趋这点不用担心。

后端

既然推出了websocket,作为现在主流的Java肯定也有相应的支持,所以在JavaEE7之后也对websocket做出了规范,所以本次的代码理论上是要运行在Java1.7+和Tomcat7.0+之上的。
看过我前面几篇文章的朋友应该都知道本次实例也是运行在之前的SSM之上的,所以这里就不再赘述了。
首先第一步需要加入websocket的依赖:

 package com.css.tax.mobilebs.util;

 import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint; import org.g4studio.common.dao.Reader;
import org.g4studio.common.service.impl.BaseServiceImpl;
import org.g4studio.common.util.SpringBeanLoader;
import org.g4studio.common.web.BaseAction;
import org.g4studio.core.metatype.Dto;
import org.g4studio.core.metatype.impl.BaseDto;
import org.junit.Test; import com.css.tax.mobilebs.Vo.CurrentUserVo;
import com.css.tax.mobilebs.serviceI.WebSocketService;
import com.css.tax.mobilebs.serviceI.ZjzzService; /**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/websocket/{user}/{id}/{ptbz}")
public class WebSocket extends BaseAction{
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static CopyOnWriteArraySet<Map<String, WebSocket>> webSocketSet = new CopyOnWriteArraySet<Map<String, WebSocket>>();
private static CopyOnWriteArraySet<Map<String, WebSocket>> webSocketSetPt = new CopyOnWriteArraySet<Map<String, WebSocket>>();
private WebSocketService zjzzWebSocketService = (WebSocketService)super.getService("zjzzWebSocketService");
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private Map<String, WebSocket> webSocketMap = new HashMap<String, WebSocket>();
private CurrentUserVo currentUserVo = new CurrentUserVo(); /**
* 连接建立成功调用的方法
*
* @param session
* 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(@PathParam("user") String user,
@PathParam("id") String id, @PathParam("ptbz") String ptbz,
Session session) {
String charset = getEncoding(user);
try {
byte[] b = user.getBytes(charset);
user = new String(b, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
this.session = session;
currentUserVo.setFbrmc(user);
currentUserVo.setFbr(id);
currentUserVo.setPtbz(ptbz);
currentUserVo.setTwr_dm(id);
currentUserVo.setPtbz(ptbz);
webSocketMap.put(id, this);
if("pt".equals(ptbz)) {
currentUserVo.setZjmc(user);
currentUserVo.setZjry_dm(id);
currentUserVo.setKhdfwr(id);
webSocketSetPt.add(webSocketMap);
}else{
currentUserVo.setNsrmc(user);
webSocketSet.add(webSocketMap); // 加入set中
}
addOnlineCount(); // 在线数加
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
} /**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
if("pt".equals(this.currentUserVo.getPtbz())) {
webSocketSetPt.remove(this.webSocketMap);
}else{
webSocketSet.remove(this.webSocketMap); // 从set中删除
}
subOnlineCount(); // 在线数减
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
} /**
* 收到客户端消息后调用的方法
*
* @param message
* 客户端发送过来的消息
* @param session
* 可选的参数
*/
@OnMessage
public void onMessage(String message,Session session) {
boolean boo = false; System.out.println("来自" + currentUserVo.getFbrmc() + "的消息:" + message+",发向"+currentUserVo.getJsr_dm());
//判断是否为未读消息
int a = message.indexOf("&&&");
System.out.println(a);
if(a!=-1) {//为未读消息
String[] msgStr = message.split("&&&");
String userMes = msgStr[0];
String[] user = userMes.split("@");
String uuid = "";
String wdjsr = "";
String fbrmc = "";
String fbr = "";
String zjzzuuid = "";
if(user.length>0) {
wdjsr = user[0];
uuid = user[1];
fbrmc = user[2];
fbr = user[3];
zjzzuuid = user[4];
}
message = msgStr[1];
CopyOnWriteArraySet<Map<String, WebSocket>> websocketSet = null;
if("pt".equals(this.currentUserVo.getPtbz())) {
websocketSet = webSocketSetPt;
}else{
websocketSet = webSocketSet;
}
if(!"everybody".equals(wdjsr)) {
//消息有指定平台用户
for (Map<String, WebSocket> itemMap : websocketSet) {
WebSocket socket = itemMap.get(wdjsr);
if(socket!=null) {
try {
if("pt".equals(this.currentUserVo.getPtbz())) {
socket.sendMessage(fbr+"@@"+fbrmc + "@^&" + message);
socket.currentUserVo.setKhdfwr(this.currentUserVo.getFbr());
}else{
socket.sendMessage(fbrmc + "@^&" + message);
socket.currentUserVo.setKhdfwr(fbr);
}
socket.currentUserVo.setJsr_dm(fbr);//设置该对话的接收人代码
socket.currentUserVo.setJsr(fbrmc);
socket.currentUserVo.setZjzzuuid(zjzzuuid);
boo = true;
Dto reDto = new BaseDto();
reDto.put("uuid", uuid);
reDto.put("ckbz", "Y");
reDto.put("xgr_dm", "111");
reDto.put("xgsj", SystemUtils.dateFormat(new Date()));
zjzzWebSocketService.updateLtxxDatas(reDto);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}else{
//未读消息没有指定平台用户
if("pt".equals(this.currentUserVo.getPtbz())) {
Object[] objArr = webSocketSetPt.toArray();
int index = (int) (Math.random() * objArr.length);
Map<String, WebSocket> map = (Map<String, WebSocket>) objArr[index];
for (WebSocket socket : map.values()) {
if(socket!=null) {
try {
boo = true;
socket.sendMessage(fbr+"@@"+fbrmc + "@^&" + message);
socket.currentUserVo.setZjzzuuid(zjzzuuid);
socket.currentUserVo.setJsr_dm(fbr);
socket.currentUserVo.setJsr(fbrmc);
socket.currentUserVo.setKhdfwr(socket.currentUserVo.getFbr());
//修改未读对话记录
Dto reDto = new BaseDto();
reDto.put("uuid", uuid);
reDto.put("jsr", this.currentUserVo.getFbr());
reDto.put("ckbz", "Y");
reDto.put("khdfwr", this.currentUserVo.getKhdfwr());
reDto.put("xgr_dm", "111");
reDto.put("xgsj", SystemUtils.dateFormat(new Date()));
zjzzWebSocketService.updateWdWzdDatas(reDto);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
}
}
}else{
String[] xxArr = message.split("@@");
String bz = "";
String zjzzuuid = "";
String fsmessage = "";
String jsr = "";
if(!"pt".equals(this.currentUserVo.getPtbz())) {
String userXx = xxArr[0];
String[] user = userXx.split("&&");
bz = user[0];
zjzzuuid = user[1];
this.currentUserVo.setZjzzuuid(zjzzuuid);
fsmessage = xxArr[1];
}else{
String[] xx = message.split("&@");
jsr = xx[0];
fsmessage = xx[1];
}
if("pt".equals(this.currentUserVo.getPtbz())) {
if(jsr!=null&&jsr.length()>0) {
//平台发送消息,发向指定客户端
boolean pdboo = false;
for (Map<String, WebSocket> itemMap : webSocketSet) {
WebSocket socket = itemMap.get(jsr);
if(socket!=null) {
try {
boo = true;
pdboo = true;
socket.sendMessage(currentUserVo.getFbrmc() + "@^&" + fsmessage);
//this.currentUserVo.setJsr_dm(socket.currentUserVo.getFbr());
//this.currentUserVo.setJsr(socket.currentUserVo.getFbrmc());
//存储已查看对话信息
if(socket.currentUserVo.getZjzzuuid()==null) {
socket.currentUserVo.setZjzzuuid(this.currentUserVo.getZjzzuuid());
}
saveYckMessage(socket.currentUserVo.getZjzzuuid(),"Y",fsmessage,"Y",jsr);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
if(!pdboo) {
this.currentUserVo.setJsr_dm(jsr);
String uuid = zjzzWebSocketService.queryZjzzuuidByJsr(jsr);
this.currentUserVo.setZjzzuuid(uuid);
boo = true;
saveYckMessage(uuid,"Y",fsmessage,"N",this.currentUserVo.getJsr_dm());
}
}
}else{
if(this.currentUserVo.getJsr_dm()!=null&&this.currentUserVo.getJsr_dm().length()>0) {
//该客户端消息已有接收对象,顺利发送给平台
for (Map<String, WebSocket> itemMap : webSocketSetPt) {
WebSocket socket = itemMap.get(currentUserVo.getJsr_dm());
if(socket!=null) {
try {
boo = true;
socket.sendMessage(currentUserVo.getFbr()+"@@"+currentUserVo.getFbrmc() + "@^&" + fsmessage);
//存储已查看对话信息
saveYckMessage(zjzzuuid,bz,fsmessage,"Y",this.currentUserVo.getJsr_dm());
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
}else{
//该客户端尚未有接收对象,需要随机指定接收对象,并连接发送消息
if(webSocketSetPt.size()>0){
//平台有用户连接
Object[] objArr = webSocketSetPt.toArray();
int index = (int) (Math.random() * objArr.length);
Map<String, WebSocket> map = (Map<String, WebSocket>) objArr[index];
for (WebSocket socket : map.values()) {
if(socket!=null) {
try {
boo = true;
socket.sendMessage(currentUserVo.getFbr()+"@@"+currentUserVo.getFbrmc() + "@^&" + fsmessage);
socket.currentUserVo.setJsr_dm(this.currentUserVo.getFbr());
socket.currentUserVo.setJsr(this.currentUserVo.getFbrmc());
this.currentUserVo.setKhdfwr(socket.currentUserVo.getFbr());
this.currentUserVo.setJsr_dm(socket.currentUserVo.getFbr());
this.currentUserVo.setZjry_dm(socket.currentUserVo.getFbr());
this.currentUserVo.setZjmc(socket.currentUserVo.getFbrmc());
this.currentUserVo.setJsr(socket.currentUserVo.getFbrmc());
//存储已查看对话信息
saveYckMessage(zjzzuuid,bz,fsmessage,"Y",this.currentUserVo.getJsr_dm());
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
}else{
try {
this.sendMessage("系统:消息已发送,但当前未有服务人员链接,无法为您解答疑问!");
//平台没有用户连接,无法发送消息,保存数据为所有人可接收的未读消息
this.currentUserVo.setJsr_dm("everybody");
this.currentUserVo.setZjry_dm("everybody");
this.currentUserVo.setKhdfwr("everybody");
saveYckMessage(zjzzuuid,bz,fsmessage,"N",this.currentUserVo.getJsr_dm());
boo = true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
} }
if(!boo){
if(this.currentUserVo.getJsr_dm()!=null&&this.currentUserVo.getZjzzuuid()!=null) {
if("pt".equals(this.currentUserVo.getPtbz())) {
saveYckMessage(this.currentUserVo.getZjzzuuid(),"Y",message,"N",this.currentUserVo.getJsr_dm());
}else{
//存储未查看对话信息
String[] xxArr = message.split("@@");
String userXx = xxArr[0];
String[] user = userXx.split("&&");
String bz = user[0];
String zjzzuuid = user[1];
String fsmessage = xxArr[1];
saveYckMessage(zjzzuuid,bz,fsmessage,"N",this.currentUserVo.getJsr_dm());
}
}else{
try {
this.sendMessage("系统:消息未发出,可能是连接失败!请重新连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
*
* @param zjzzuuid 对话组uuid
* @param bz 是否存在对话组标志
* @param message 对话信息
* @param ckbz 对话是否查看标志
*/
private void saveYckMessage(String zjzzuuid,String bz,String message,String ckbz,String jsr) {
Dto dto = new BaseDto();
String fbrmc = currentUserVo.getFbrmc();
String fbr = currentUserVo.getFbr();
String khdfwr = currentUserVo.getKhdfwr();
if("N".equals(bz)) {//是否有uuid,判断其在数据库中是否存在已有信息
//数据库没有该对话记录,故需创建对话记录
dto.put("uuid", zjzzuuid);
//以下需前台传值
dto.put("twr_dm", currentUserVo.getTwr_dm());
dto.put("nsrsbh", currentUserVo.getNsrsbh());
dto.put("nsrmc", currentUserVo.getNsrmc());
dto.put("zjmc", currentUserVo.getZjmc());
dto.put("zjry_dm", currentUserVo.getZjry_dm());
dto.put("fbr", fbr);
dto.put("fbrmc", fbrmc);
dto.put("jsr", jsr);
dto.put("lrr_dm", fbr);
dto.put("lrrq", SystemUtils.dateFormat(new Date()));
zjzzWebSocketService.insertZjzzDhjl(dto); Dto dto1 = new BaseDto();
dto1.put("lrr_dm", fbr);
dto1.put("lrrq", SystemUtils.dateFormat(new Date()));
dto1.put("uuid", SystemUtils.genUUID());
dto1.put("zjzzuuid", zjzzuuid);
dto1.put("dhnr", message);
dto1.put("dhsj", SystemUtils.dateFormat(new Date()));
dto1.put("fbr", fbr);
dto1.put("ckbz", ckbz);
dto1.put("khdfwr", khdfwr);
dto1.put("fbrmc", fbrmc);
dto1.put("jsr", jsr);
zjzzWebSocketService.insertZjzzMxDhjlByUuid(dto1);
}else{
Dto redto = new BaseDto();
redto.put("lrr_dm", fbr);
redto.put("lrrq", SystemUtils.dateFormat(new Date()));
redto.put("uuid", SystemUtils.genUUID());
redto.put("zjzzuuid", zjzzuuid);
redto.put("dhnr", message);
redto.put("dhsj", SystemUtils.dateFormat(new Date()));
redto.put("fbr", fbr);
redto.put("ckbz", ckbz);
redto.put("khdfwr", khdfwr);
redto.put("fbrmc", fbrmc);
redto.put("jsr", jsr);
zjzzWebSocketService.insertZjzzMxDhjlByUuid(redto);
}
} /**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
} /**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
// this.session.getAsyncRemote().sendText(message);
} public static synchronized int getOnlineCount() {
return onlineCount;
} public static synchronized void addOnlineCount() {
WebSocket.onlineCount++;
} public static synchronized void subOnlineCount() {
WebSocket.onlineCount--;
} public static String getEncoding(String str) {
String encode = "GB2312";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { // 判断是不是GB2312
String s = encode;
return s; // 是的话,返回GB2312,以下代码同理
}
} catch (Exception e) {
e.printStackTrace();
}
encode = "ISO-8859-1";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { // 判断是不是ISO-8859-1
String s1 = encode;
return s1;
}
} catch (Exception e) {
e.printStackTrace();
}
encode = "UTF-8";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { // 判断是不是UTF-8编码
String s2 = encode;
return s2;
}
} catch (Exception e) {
e.printStackTrace();
}
encode = "GBK";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { // 判断是不是GBK
String s3 = encode;
return s3;
}
} catch (Exception e) {
e.printStackTrace();
}
return ""; // 到这一步,你就应该检查是不是其他编码啦
}
}

这就是整个websocket的后端代码。看起来也比较简单主要就是使用那几个注解。每当有一个客户端连入、关闭、发送消息都会调用各自注解的方法。这里我讲一下sendMessage()这个方法。

websocket绕坑

sendMessage()方法中我只想实现一个简单的功能,就是将每次的聊天记录都存到数据库中。看似一个简单的功能硬是花了我半天的时间。
我先是按照以前的惯性思维只需要在这个类中注入service即可。但是无论怎么弄每次都注入不进来都是null
最后没办法只有google了,最后终于在神级社区StackOverFlow中找到了答案,就是前边所说的需要添加的第二个 maven依赖,然后加入@ServerEndpoint(value = "/websocket",configurator = SpringConfigurator.class)这个注解即可利用Spring注入了。接着就可以做消息的保存了。

前端

前端我采用了Ext.js做的。还是先贴一下代码:

zxws_main.html
 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Parent Page</title>
<link rel="stylesheet" type="text/css" href="css/Globle.css" />
<link rel="stylesheet" type="text/css" href="css/index.css" />
<script src="js/jquery-1.12.0.min.js" type="text/javascript"
charset="utf-8"></script>
<style type="text/css">
#main_left div {
background: #D1D1D1;
border-bottom: 1px #DDDDDD solid;
} #main_left input {
font-size: 14px;
text-align: center;
height: 40px;
width: 150px;
line-height: 40px;
color: white;
} #main_left span {
background: red none repeat scroll 0 0;
border-radius: 9px;
display: inline-block;
height: 18px;
line-height: 18px;
width: 18px;
text-align: center;
color: white;
display: none; } #add {
position: absolute;
bottom: 200px;
}
.butt{
height:30px;
width: 40px;
margin-top: 80px;
margin-left: -391px;
display: inline-block;
}
</style>
</head>
<body>
<button class="butt" type="button" class="fs" onclick="()">清理</button>
<div id="main">
<div id="main_left">
<!--<h1>This is the Parent Page.</h1>--> </div>
<div id="main_right">
<iframe style="width: 600px; height: 540px;" id="childframe"
name="childframe" src="zxws.jsp"></iframe>
</div>
</div>
<script type="text/javascript" src="js/map.js"></script> </body>
<script type="text/javascript" src="zxws_main.js"></script>
</html>
zxws_main.js
 /**
* Created by wanglei on 2017-07-03.
*/
var user = GetQueryString("user");
var id = GetQueryString("id");
var ptbz = GetQueryString("ptbz");//客户端为:khd;平台为:pt;
var uuid = "";
var websocket = null;
var jsonStr = localStorage.getItem('WSBS_PT_ZXBS_'+id);
var jsonObj2;
var userOnLineMap = new Map();
function GetQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = decodeURI(window.location.search).substr(1).match(reg);
if (r != null)
return unescape(r[2]);
return null;
}
var zxwin = window;
var parentWin;
// 定义最后光标对象 var lastEditRange, selection;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://222.90.69.254:7001/mobile/websocket/"
+ user+"/"+id+"/"+ptbz);
} else {
alert('当前浏览器 Not support websocket,请更换浏览器');
}
//连接发生错误的回调方法
websocket.onerror = function() {
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function(aa) {
//setMessageInnerHTML("WebSocket连接成功");
//查看是否有未读消息,并发送消息
if(jsonStr==null||jsonStr.length==0||jsonStr=="{}") {
jsonObj2 = {};
var o = document.getElementById("main_right");
o.style.visibility="hidden";
}else{
jsonObj2 = JSON.parse(jsonStr);
//document.getElementById("main_right").innerHTML = "<iframe style='width:600px; height:540px;' id='childframe' name='childframe' src='zxws.jsp'></iframe>";
}
$.ajax({
method:"POST",
data:{
"twr_dm":id,
"ptbz":ptbz
},
url:"../zjzz.do?reqCode=initDhxx",
success: function(msg){
var datareq = eval('(' + msg + ')');
if(msg!=null&&msg!='[]'){
//debugger;
if("pt"!=ptbz) {
uuid = datareq.uuid;
document.getElementById("uuid").setAttribute("uuid", datareq.uuid);
bz = datareq.bz;
document.getElementById("uuid").setAttribute("bz", datareq.bz);
}
$.ajax({
method:"POST",
data : {
//"zjzzuuid" : document.getElementById("uuid").getAttribute("uuid"),
"jsr" : id
},
url:"../zjzz.do?reqCode=queryLtxxDatas",
success: function(msg){
//debugger;
//初始化历史聊天记录
var json = eval(jsonObj2);
//localStorage.removeItem('WSBS_PT_ZXBS_'+id);
var a = 1;
if(jsonStr!=null&&jsonStr.length>0) {
for (var prop in json)
{
var userArray = prop.split("@");
var jsrid = userArray[0];
var jsrmc = userArray[1];
//alert(jsrid+"~~"+jsrmc);
if(jsrmc!=undefined&&jsrmc!=null&&jsrmc.length>0) {
userOnLineMap.put(jsrid, jsrmc);
$("#main_left").append("<div><input id='"+jsrid+"'/><span id='"+jsrid+"_span'></span></div>");
$("#"+jsrid).attr("value",jsrmc);
$("#"+jsrid).attr("type","button");
$("#"+jsrid).attr("onclick","btnClick('"+jsrid+"')");
if(a==1) {
var jsrMsg = jsonObj2[prop];
var obj = window.frames["childframe"].document;
//debugger;
window.frames["childframe"].document.getElementById("uuid").setAttribute("bz", "Y");
window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrid",jsrid);
window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrmc",jsrmc);
for (var i = 0; i < jsrMsg.length; i++) {
var fsr = jsrMsg[i].fsr;
if(fsr==jsrid) {
var c1 = obj.getElementById("dhk");
var div1 = obj.createElement("div");
div1.className = "org_div"; /*var now = new Date();
var nowTime = now.toLocaleString();
var date = nowTime.substring(0,10);//截取日期
var time = nowTime.substring(10,20);*/ var yhDiv = obj.createElement("span");
yhDiv.className = "org_yh1";
yhDiv.innerHTML = jsrMsg[i].fsrmc+":";
var newDiv = obj.createElement("p");
newDiv.className = "org_box";
newDiv.innerHTML = jsrMsg[i].dhnr;
var sjDiv = document.createElement("p");
sjDiv.className = "org_sj2";
sjDiv.innerHTML = jsrMsg[i].lrrq;
//var lrrq = time;
//sjDiv.innerHTML = lrrq;
div1.appendChild(yhDiv);
div1.appendChild(newDiv);
div1.appendChild(sjDiv);
c1.appendChild(div1);
}else{
var c1 = obj.getElementById("dhk");
var div1 = obj.createElement("div");
div1.className = "org_div"; /*var now = new Date();
var nowTime = now.toLocaleString();
var date = nowTime.substring(0,10);//截取日期
var time = nowTime.substring(10,20);*/ var yhDiv = obj.createElement("span");
yhDiv.className = "org_yh";
yhDiv.innerHTML = ":我";
var newDiv = obj.createElement("p");
newDiv.className = "org_hf";
newDiv.innerHTML = jsrMsg[i].dhnr;
var sjDiv = document.createElement("p");
sjDiv.className = "org_sj1";
//var lrrq = time;
//sjDiv.innerHTML = lrrq;
sjDiv.innerHTML = jsrMsg[i].lrrq;
div1.appendChild(yhDiv);
div1.appendChild(newDiv);
div1.appendChild(sjDiv);
c1.appendChild(div1);
c1.scrollTop = c1.scrollHeight;
}
}
}
a = a+1;
}
}
}
if(msg!=null){
var datareq = eval('(' + msg + ')');
for ( var int = 0; int < datareq.length; int++) {
var msg = datareq[int].dhnr;
var dhuuid = datareq[int].uuid;
var wdjsr = datareq[int].jsr;
var fbrmc = datareq[int].fbrmc;
var fbr = datareq[int].fbr;
var zjzzuuid = '';
if("pt"!=ptbz) {
zjzzuuid = document.getElementById("uuid").getAttribute("uuid");
}else{
zjzzuuid = datareq[int].zjzzuuid;
}
websocket.send(wdjsr+"@"+dhuuid+"@"+fbrmc+"@"+fbr+"@"+zjzzuuid+"&&&"+msg);
}
}
}
});
}
}
});
}
//接收到消息的回调方法
websocket.onmessage = function(event) {
debugger;
var o = document.getElementById("main_right");
if(o.style.visibility=="hidden") {
o.style.visibility="visible";
//document.getElementById("main_right").innerHTML = "<iframe style='width:600px; height:540px;' id='childframe' name='childframe' src='zxws.jsp'></iframe>";
}
var mes = event.data;
var msg = "";
var jsr = "";
if(ptbz=='pt') {
var arr = mes.split('@@');
jsr = arr[0];//设置当前tab的id,即接收人
msg = arr[1];
}else{
msg= mes;
}
var fsrArr = msg.split('@^&');
var fsr = fsrArr[0];
msg = fsrArr[1];
var xxUser = userOnLineMap.get(jsr);
debugger;
if(xxUser!=undefined && xxUser.length>0) { }else{
userOnLineMap.put(jsr, fsr);
$("#main_left").append("<div><input id='"+jsr+"'/><span id='"+jsr+"_span'></span></div>");
$("#"+jsr).attr("value",fsr);
$("#"+jsr).attr("type","button");
$("#"+jsr).attr("onclick","btnClick('"+jsr+"')");
}
if (fsr != user) {
var obj = window.frames["childframe"].document.getElementById("uuid");
if(obj.getAttribute("fsrid")!=undefined&&obj.getAttribute("fsrid")!=jsr) {
var num = document.getElementById(jsr+"_span").innerHTML;
if(num==null) {
num=0;
}
var num1 = num-0+1;
document.getElementById(jsr+"_span").innerHTML=num1;
if(num1>0){
document.getElementById(jsr+"_span").style.display='inline-block';
}
//如果是新用户进来得先走这步,所以再这里就要给它加上日期,如果是老用户的话直接跳到else里去执行了,走到发送里面在给加日期
var now = new Date();
var lrrq = now.toLocaleString();
var date = lrrq.substring(0,10);//截取日期
var time = lrrq.substring(10,20); var dataJson = {'fsr':jsr,'jsr':id,'fsrmc':fsr,'dhnr':msg,'lrrq':lrrq};
document.getElementById("childframe").contentWindow.updateLocalStorage(jsr,fsr,dataJson);
}else{
obj.setAttribute("fsrid",jsr);
obj.setAttribute("fsrmc",fsr);
}
if(obj.getAttribute("bz")==undefined){
//debugger;
btnClick(jsr);
}
obj.setAttribute("bz","Y");
document.getElementById("childframe").contentWindow.hfxx(jsr,fsr,msg);
}
} function btnClick(jsr){
//alert(JSON.stringify(jsonObj2));
//debugger;
document.getElementById(jsr+"_span").style.display='none';
document.getElementById(jsr+"_span").innerHTML='';
window.frames["childframe"].document.getElementById("dhk").innerHTML = '';
localStorage.setItem('WSBS_PT_ZXBS_'+id, JSON.stringify(jsonObj2));
window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrid",jsr);
window.frames["childframe"].document.getElementById("uuid").setAttribute("fsrmc",userOnLineMap.get(jsr));
document.getElementById("childframe").contentWindow.initLocalStorage(jsr,id); //alert(window.frames["childframe"].document.getElementById("uuid").getAttribute("fsrid"));
} //连接关闭的回调方法
websocket.onclose = function() {
localStorage.setItem('WSBS_PT_ZXBS_'+id, JSON.stringify(jsonObj2));
//localStorage.removeItem('WSBS_PT_ZXBS_'+id);
//setMessageInnerHTML("WebSocket连接关闭");
} //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
closeWebSocket();
} //关闭WebSocket连接
function closeWebSocket() {
websocket.close();
} function send(data) {//获取当前tab的id,即接收人
websocket.send(data);
}
其实其中重要的就是那几个JS方法,都写有注释。需要注意的是这里
 //判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://222.90.69.254:7001/mobile/websocket/"
+ user+"/"+id+"/"+ptbz);
} else {
alert('当前浏览器 Not support websocket,请更换浏览器');
}

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">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport"
content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0" />
<title>在线问税</title>
</head>
<script type="text/javascript" src="../zjzz/js/jquery-1.12.0.min.js"
charset="utf-8"></script>
<script type="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" onclick="localStorage.removeItem('WSBS_PT_ZXBS_'+id);" id=" ">清理</button> -->
<div id="zt">
<div id="bqb">
<img src="biaoqing/1.png" style="width: 27px; height: 27px;"
onclick="bqdj(1);" /> <img src="biaoqing/2.png"
style="width: 27px; height: 27px;" onclick="bqdj(2);" /> <img
src="biaoqing/3.png" style="width: 27px; height: 27px;"
onclick="bqdj(3);" /> <img src="biaoqing/4.png"
style="width: 27px; height: 27px;" onclick="bqdj(4);" /> <img
src="biaoqing/5.png" style="width: 27px; height: 27px;"
onclick="bqdj(5);" /> <img src="biaoqing/6.png"
style="width: 27px; height: 27px;" onclick="bqdj(6);" />
</div>
<div id="dhk" style="background: #EEEEEE"></div>
<div id="lts">
<div id="gjl">
<ul>
<li><img onclick="tjbq()" src="picture/1.png"
style="width: 27px; height: 27px; margin-left: 5px; margin-left: 5px;" /></li>
</ul>
</div>
<div id="ltk" contenteditable="true" ></div>
</div>
<div style="margin-top: 3px;">
<!-- <span id="subjectchk">还可输入 <strong id="checklen"
style="color: #FF0000">400</strong> 个字符
</span> -->
<button type="button" class="fs" onclick="fsxx();" id="uuid">发送</button>
<!-- <button type="button" class="qx" onclick="closewindow();">关闭</button> -->
</div>
</div> </body>
</html>
zxws.js
 function fsxx() {
var c = document.getElementById("ltk").innerHTML;
var jsr = document.getElementById("uuid").getAttribute("fsrid");
var fsrmc = document.getElementById("uuid").getAttribute("fsrmc"); var now = new Date();
var nowTime = now.toLocaleString();
var date = nowTime.substring(0,10);//截取日期
var time = nowTime.substring(10,20); //var date = new Date();
//var year = date.getFullYear();
//var month = date.getMonth()+1;
//var day = date.getDate();
//var hour = date.getHours();
//var minute = date.getMinutes();
//var second = date.getSeconds();
//alert(year+'年'+month+'月'+day+'日'+hour+':'+minute+':'+second); window.parent.send(jsr+"&@"+c);
var c1 = document.getElementById("dhk");
var div1 = document.createElement("div");
div1.className = "org_div";
var yhDiv = document.createElement("span");
yhDiv.className = "org_yh";
yhDiv.innerHTML = ":我";
var newDiv = document.createElement("span");
newDiv.className = "org_hf";
var msg = c ;
newDiv.innerHTML = msg;
var sjDiv = document.createElement("span");
sjDiv.className = "org_sj1";
var lrrq = nowTime;
sjDiv.innerHTML = lrrq;
var dataJson = {'fsr':window.parent.id,'jsr':jsr,'fsrmc':fsrmc,'dhnr':c,'lrrq':lrrq};
updateLocalStorage(jsr,fsrmc,dataJson);
div1.appendChild(yhDiv);
div1.appendChild(newDiv);
div1.appendChild(sjDiv);
c1.appendChild(div1);
/*var c2 = document.createElement("br");
c1.appendChild(c2);*/
document.getElementById("ltk").innerHTML = "";
c1.scrollTop = c1.scrollHeight;
}
function hfxx(jsr,fsr,data) {
var a = document.getElementById("uuid").getAttribute("fsrid");
var fsrmc = document.getElementById("uuid").getAttribute("fsrmc");
//var d = document.getElementById("time").innerHTML; var now = new Date();
var nowTime = now.toLocaleString();
var date = nowTime.substring(0,10);//截取日期
var time = nowTime.substring(10,20);
//var date = new Date();
//var year = date.getFullYear();
//var month = date.getMonth()+1;
//var day = date.getDate();
//var hour = date.getHours();
//var minute = date.getMinutes();
//var second = date.getSeconds();
//alert(year+'年'+month+'月'+day+'日'+hour+':'+minute+':'+second); if(a==jsr) {
var c1 = document.getElementById("dhk");
var div1 = document.createElement("div");
div1.className = "org_div";
var yhDiv = document.createElement("span");
yhDiv.className = "org_yh1";
yhDiv.innerHTML = fsr+":";
var newDiv = document.createElement("span");
newDiv.className = "org_box";
newDiv.innerHTML = data;
var sjDiv = document.createElement("span");
sjDiv.className = "org_sj2";
var lrrq = nowTime;
sjDiv.innerHTML = lrrq;
div1.appendChild(yhDiv);
div1.appendChild(newDiv);
div1.appendChild(sjDiv);
c1.appendChild(div1);
/*var c2 = document.createElement("br");
c1.appendChild(c2);*/
c1.scrollTop = c1.scrollHeight;
var dataJson = {'fsr':a,'jsr':window.parent.id,'fsrmc':fsr,'dhnr':data,'lrrq':lrrq};
updateLocalStorage(a,fsrmc,dataJson);
}else if(a=="系统"){
var c1 = document.getElementById("dhk");
var div1 = document.createElement("div");
div1.className = "org_div";
var newDiv = document.createElement("span");
newDiv.className = "org_box";
newDiv.innerHTML = data;
div1.appendChild(newDiv);
c1.appendChild(div1);
/*var c2 = document.createElement("br");
c1.appendChild(c2);*/
c1.scrollTop = c1.scrollHeight;
}
}
//更新对话json串
var jsonObj1 = window.parent.jsonObj2;
function updateLocalStorage(jsr,fsr,data) {
//debugger;
var str = jsr+"@"+fsr;
if(jsonObj1[str]==undefined) {
var temp = [];
jsonObj1[str] = temp;
jsonObj1[str].push(data);
}else{
jsonObj1[str].push(data);
}
}
function initLocalStorage(jsr,id) {
jsonObj1 = window.parent.jsonObj2;
var fsrid = document.getElementById("uuid").getAttribute("fsrid");
var fsrmc = document.getElementById("uuid").getAttribute("fsrmc");
var str = fsrid+"@"+fsrmc;
var jsonStr = localStorage.getItem('WSBS_PT_ZXBS_'+id);
if(jsonStr==null||jsonStr.length==0) { }else{
var jsonMsg = JSON.parse(jsonStr);
if(jsonMsg[str]!=undefined) {
var jsrMsg = jsonMsg[str];
for (var i = 0; i < jsrMsg.length; i++) {
var fsr = jsrMsg[i].fsr;
if(fsr==fsrid) {
var c1 = document.getElementById("dhk");
var div1 = document.createElement("div");
div1.className = "org_div";
var yhDiv = document.createElement("span");
yhDiv.className = "org_yh1";
yhDiv.innerHTML = jsrMsg[i].fsrmc+":";
var newDiv = document.createElement("p");
newDiv.className = "org_box";
newDiv.innerHTML = jsrMsg[i].dhnr;
var sjDiv = document.createElement("span");
sjDiv.className = "org_sj2";
sjDiv.innerHTML = jsrMsg[i].lrrq;
div1.appendChild(yhDiv);
div1.appendChild(newDiv);
div1.appendChild(sjDiv);
c1.appendChild(div1);
c1.scrollTop = c1.scrollHeight;
}else{
var c1 = document.getElementById("dhk");
var div1 = document.createElement("div");
div1.className = "org_div";
var yhDiv = document.createElement("span");
yhDiv.className = "org_yh";
yhDiv.innerHTML = ":我";
var newDiv = document.createElement("p");
newDiv.className = "org_hf";
newDiv.innerHTML = jsrMsg[i].dhnr;
var sjDiv = document.createElement("span");
sjDiv.className = "org_sj1";
sjDiv.innerHTML = jsrMsg[i].lrrq;
div1.appendChild(yhDiv);
div1.appendChild(newDiv);
div1.appendChild(sjDiv);
c1.appendChild(div1);
c1.scrollTop = c1.scrollHeight;
}
}
}
}
} function tjbq() {
if(bqb.style.visibility=='visible'){
bqb.style.visibility="hidden";
}else{
bqb.style.visibility="visible";
}
} function hiddenBq() {
var bqb = document.getElementById("bqb");
bqb.style.visibility = "hidden";
} function bqdj(num) {
var imgsrc = "biaoqing/" + num + ".png";
showImg(imgsrc);
hiddenBq();
} function showImg(imgsrc) {
var img = document.createElement("img");
img.src = imgsrc;
//img.onselectstart= function(){return false;}
var parent = document.getElementById("ltk");
var div = document.createElement("div");
div.appendChild(img);
div.style.display = 'inline-block';
div.onselectstart= function(){return false;}
parent.appendChild(div);
}
function getByteLen(val) {
var len = 0;
for (var i = 0; i < val.length; i++) {
var a = val.charAt(i);
if (a.match(/[^\x00-\xff]/ig) != null) {
len += 2;
}
else {
len += 1;
}
}
return len;
}
// 只要键盘一抬起就验证编辑框中的文字长度,最大字符长度可以根据需要设定
function checkLength(obj) {
var a = document.getElementById("ltk");
debugger;
var maxChars = 400;//最多字符数
var curr = maxChars - getByteLen(obj.innerHTML);
if (curr > 0) {
document.getElementById("checklen").innerHTML = curr.toString();
} else {
document.getElementById("checklen").innerHTML = '0';
document.getElementById("ltk").readOnly = true;
var oBtn = document.getElementById('fs');
oBtn.onclick = function(){
oBtn.disabled = 'disabled';
};
}
} /**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
*
* @param message
* @throws IOException
*/
function xxql() {
this.session.getBasicRemote().sendText(message);
/*if("pt".equals(this.currentUserVo.getPtbz())) {
webSocketSetPt.remove(this.webSocketMap);
}else{
webSocketSet.remove(this.webSocketMap); // 从set中删除
}
subOnlineCount(); // 在线数减
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());*/
}

注意:最后要在web-info下lib文件夹下引入websocket-api.jar,这样打war包才不会出错


基于webSocket的聊天室的更多相关文章

  1. 基于WebSocket实现聊天室(Node)

    基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议,服务端可以主动向前端传递数据,相比比AJAX轮询服务器,WebSocket采用监听的方式,减轻了服务器压力 ...

  2. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  3. 第一节:.Net版基于WebSocket的聊天室样例

    一. 说在前面的话 该篇文章为实时通讯系列的第一节,基于WebSocket编写了一个简易版聊天样例,主要作用是为引出后面SignalR系列的用法及其强大方便之处,通过这个样例与后续的SignalR对比 ...

  4. [已解决][HTML5]基于WebSocket开发聊天室应用

    WebSocket示例java的比较少,大部分是nodejs的,比较有名的是socket.io的chat, 借用下他的前端实现一套java的,后端基于https://github.com/genera ...

  5. websocket+golang聊天室

    原文地址: http://www.niu12.com/article/3 websocket+golang聊天室 main.go和index.html放在同一目录下 main.go package m ...

  6. WebSocket 网页聊天室

    先给大家开一个原始的websocket的连接使用范例 <?php /* * recv是从套接口接收数据,也就是拿过来,但是不知道是什么 * read是读取拿过来的数据,就是要知道recv过来的是 ...

  7. 基于websocket vue 聊天demo 解决方案

    基于websocket vue 聊天demo 解决方案 demo 背景 电商后台管理的客服 相关技术 vuex axios vue websocket 聊天几种模型 一对一模型 一对一 消息只一个客户 ...

  8. 关于websocket制作聊天室的的一些总结

    websocket的总结 在一个聊天室系统中,常常使用websocket作为通信的主要方式.参考地址:https://www.jianshu.com/p/00e... 关于自己的看法:websocke ...

  9. 基于nodejs+webSocket的聊天室(实现:加入聊天室、退出聊天室、在线人数、在线列表、发送信息、接收信息)

    1  安装 socket.io模块 npm install "socket.io": "latest" 2 app.js相关 ws = require('soc ...

随机推荐

  1. 【起航计划 034】2015 起航计划 Android APIDemo的魔鬼步伐 33 App->Service->Local Service Binding 绑定服务 ServiceConnection Binder

    本例和下列Local Service Controller 的Activity代码都定义在LocalServiceActivities.Java 中,作为LocalServiceActivities ...

  2. 去掉iframe白色背景方法

    在iframe内添加如下代码 style="display:none" onload="this.style.display = 'block';" 先让它不显 ...

  3. GitHub无法push的问题

    问题背景 换了台别人用过的电脑想要将文件push到github上,出现下面报错 remote: Permission to *****(我的)/gittest.git denied to *****( ...

  4. mvc4 坑啊

    昨天下午出了个BUG.到今天上午才解决掉.就是mvc页面的属性名跟controller 中action 参数的名相同.导致action无法取得前台的值.这个问题浪费了很多时间.命名要规范. 如 页面 ...

  5. 一个.java源文件中可以有多个类吗?(内部类除外)有什么条件?

    一个.java源文件中可以有多个类吗?(内部类除外)有什么条件?带着这个疑惑,动手建几个测试类, 揭开心中的疑惑.以下是解开疑惑过程: package test;/** * 一个.java源文件中可以 ...

  6. Mantis查看问题列表的列名修改_"P","#"两列

    在使用mantis的时候,点击菜单上的“查看问题”进去,就会罗列出当前的bug列表,可是列表的标题上存在着“P”和“#”的显示,个人觉得这两列在这里完全没有意义,或者说现有的显示使人觉得疑惑,究竟代表 ...

  7. 爬虫入门之urllib库(一)

    1 爬虫概述 (1)互联网爬虫 一个程序,根据Url进行爬取网页,获取有用信息 (2)核心任务 爬取网页 解析数据 难点 :爬虫和反爬虫之间的博弈 (3)爬虫语言 php 多进程和多线程支持不好 ja ...

  8. 腾讯云微信小程序域名变更指南

    1 . 将域名添加到云解析里面, 将解析的地址指向已有的小程序负载均衡地址: https://console.qcloud.com/cns 将域名添加到解析列表 添加成功之后,点击解析按钮,添加二级域 ...

  9. aws查看官方centos镜像imageid

    aws ec2 describe-images --owners aws-marketplace --filters Name=product-code,Values=aw0evgkw8e5c1q41 ...

  10. Linux获取系统当前时间(精确到毫秒)

    #include <stdio.h> #include <time.h> #include <sys/time.h> void sysLocalTime() { t ...