复盘聊天系统

项目部署到tomcat容器中以后,首先加载项目根目录下WEB-INF中的web.xml文件

根据配置<load-on-startup>1</load-on-startup>转到InitServlet中进行初始话

 

初始化会去加载整个项目最核心的一个类WebSocketTest

(自然就是①先加载这个类的字节码②初始话其成员变量③再执行initMsgQue()方法)

(InitServlet中一句简单的WebSocketTest.initMsgQue();就是包含这么多内容的一个过程)

 

WebSocketTest类的注解为,说起来又有很多文章了,它主要起一个拦截器的作用,先记着

@ServerEndpoint(value = "/websocket",configurator=GetHttpSessionConfigurator.class)

还间歇获取session失败,隔代遗传,应该是我的websocket的每个动作的session没有管理好。

这个类的几个重要静态成员变量初始话之后,在内存中只有一份拷贝

private static Hashtable<Integer, WebSocketTest> connections = new Hashtable<>();

private static QueueCircle msgQue;

private static IMessageSV iMessageSV = MessageSVImp.getInstance();

private static IStaffSV iStaffSV = StaffSVImpl.getInstance();

 

connections

看长相和名字就知道Hashtable<Integer, WebSocketTest>,它里边存的每一对键值,就表示了与客户端建立的一个连接

键:工号,值:核心对象WebSocketTest(是WebSocketTest对象在不同时期的多份拷贝(一个新连接start()时拷贝进去的),那么在内存中是多个不同WebSocketTest对象)

(该系统,最重要的两个主体:和人相关的(左边iStaffSV),和消息相关的(右边iMessageSV))

(这两个类都是有很强的业务能力的,那么它们都是两个接口的实现类,属于service层)

iStaffSV

提供全生命周期,全实况的,人员的增删改查,就是这么牛皮

(它有一个静态成员变量,就是这个类本身的一个实例)

(它还有一个静态工厂方法,就是返回它的那个静态成员变量)

(那么,产生一个不好理解的东西,它返回的这个实例在内存中就只存在一份,而它又被WebSocketTest的静态成员变量iStaffSV所引用)

iMessageSV

同样,它提供消息相关的所有功能

它的套路和iStaffSV一毛一样

msgQue

它叫作消息环,就是消息队列的作用

(关注这个聊天窗口,注意倒这是一个在线实时聊天系统,窗口里的消息会实时滚动的(窗口里展示的消息有条数限制,并且它还需要实时的广播到每一个客户机,这是我们直观上看到的系统功能特点))

(这个类没什么静态成员变量和静态方法,它的实例本身就是作为别人的静态成员变量,因此在内存中有一份)

它的成员变量:

private int size;//应该只要是环状的消息队列就有这个东西吧,我猜

//不知道是不是所有的消息队列都是环状的,是的话,你应该有一把锁,对它进行操作的时候要锁上

private final Lock lock = new ReentrantLock();

private MsgNode start, current;//所谓队列,队列里边总要有个体吧

它的方法:

构造方法(构造的时候,你告诉它应该存几个队列就行了)

cover()

remove()

insert()

clear()

display()

getMessages()

getEleNum()

getSize()

setSize()

getStart()

setStart()

getCurrent()

setCurrent()

(联想:是事也就那一会,一会就没事;通感:重要的东西,核心的难的内容也就那么一点,就如同消息队列,它是很复杂,代码风格像jdk源码一样,不过把这个东西封装好了,其他的东西只是围绕这个核心进行服务罢了)

(msgQue这个成员变量的初始化是在initMsgQue()这个静态方法中一开始就被调用的)

 

(这里顺带先说下配置文件的操作,Constant类)

这个类不仅可以读取配置文件而用,而且还可以用来配置一些静态常量

骚操作:

它加载配置文件的代码,主要用到PropertiesReader,都是写在static{}静态代码块里边的(不需要写在某个方法内部,一次性把配置文件中的键值对全加载到HashMap中,然后赋值给静态变量)

 

(这样,在有浏览器访问该项目之前;在tomcat加载此项目之后。静态成员变量已经初始化完成)

(诗歌在表达深意之前,它的意象首先要丰富)

Staff

(id,name,code,pwd,headUrl,power,extra,state)

(code工号,power(S超管N普通),extra备用)

StaffToFore(继承自Staff)

(messageType连接前台时的类型,用来判断是说话,还是上线,下线)

1.群聊文本消息

2.建立连接消息

3.关闭连接消息

4.消息缓存消息

5.私聊文本消息

Message

(id,content,staffId,createdDt,type,targetId,extra)

MessageToFore(继承自Message)

(staffName,messageType,headUrl)

SharedFile

(id,uploader,createDate,fileType,foreName,backName)

MsgJson

(targetType,message,targetId,type)

 

(完了之后,当用户访问该项目,来到web.xml,被告知去访问chat.html页面)

<body onLoad="startWebSocket()">

浏览器首先去找服务器建立websocket连接(ws这一套是一个固定的模子)

ws = new WebSocket("ws://"+localhost+"/SmartChat_EAP/websocket");

当然在此之前,会去检查cookie,没有对应内容则进入LogoutServlet

进入方式是

url = "LogoutServlet?reason="+reason;

window.location.href = url;

在LogoutServlet内重写了cookie(写到了这个域中setPath("/SmartChat_EAP"),注意这个域,回去要改一下),然后重定向到response.sendRedirect("login.html")

在login.js进行登录function ajaxLogin(username, password)进而请求到LoginServlet中,校验完密码之后,更新session和cookie,完了回到login.js中,去确认cookie是否没问题,没问题再转到chat.html中(window.location.href = "/SmartChat_EAP/chat.html")

这会儿再去建立websocket连接,就不会再有cookie的障碍了

 

建立websocket连接,转到WebSocketTest类中,首先去注解(拦截器)

@ServerEndpoint(value = "/websocket",configurator=GetHttpSessionConfigurator.class)中处理(其中HttpSession httpSession = (HttpSession)request.getHttpSession();动不动获取不到,间歇性!)

WebSocket connection to 'ws://possible2dream.cn:8080/SmartChat_EAP/websocket' failed: Error during WebSocket handshake: Unexpected response code: 500

然后往下进入start()方法中,为该用户的连接建立一个WebSocketTest对象(connections.put(this.staff.getId(), this);)

@OnOpen

public void start(Session session, EndpointConfig config)

在此之前,还会校验是否已经登录(若登录则先且移出连接,并通过session的操作,通知前台已经被顶掉了(connections.get(stfId).session.getBasicRemote().sendText("1");这样改变session,前台在ws.onmessage = function(evt)方法中会收到响应))start()方法中,还有三件重要的事情:更新聊天成员列表和更新聊天消息框以及通知其他在线用户更新用户列表(新增或删除此用户)

(静态成员变量QueueCircle中就是存的当前时刻的消息列表,发消息给前台的方式同样是this.session.getBasicRemote().sendText(jsonUtil.toJson(msgs));)

(更新成员类表的信息是从connections中取的;还更新了该员工的个人信息(从当前对象中取的(其实从这里可以看出一个事情,就是这个核心类WebSocketTest,其实每次前台过来请求建立websocket连接成功之后,就已经实例化了,只是在start()方法中才塞到connections中)))

(广播的过程中需要对connections中的WebSocketTest对象加同步锁synchronized)

(start()方法执行的过程中,多次传消息给前台,前台都是在ws.onmessage = function(evt)方法中进行响应的(全双工),并且前台除了收到字符串的消息之外,就是收到json消息了,并且前台收到的json消息是分两种进行处理的(普通json和数组类型的json)(并且数组列表只包含两种:消息记录列表4和在线成员列表2))

(到此,服务端与客户端的websocket连接就已经建立完成了(浏览器页面也加载完成)。可以肯定,此后该客户端再与服务器进行通信时,服务器响应的对象就是之前建立的该用户对应的WebSocketTest对象了,那么也就是说,前文没有提到的一些WebSocketTest的方法,实际上都是在具体用户对应的WebSocketTest实例中来用的。(服务端和客户端的通信都在各自的onMessage方法中进行相应))

 

(好的开头=成功了一半;但是呢,行百里者半九十!)

继续把WebSocketTest类中剩下的方法介绍完

@OnClose

public void end()

(断开连接的时候会触发这个方法,去做:更新connections变量,广播通知此人离开)

//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。前后对应

window.onbeforeunload = function(){

ws.close();

};

@OnError

public void onError(Throwable t) throws Throwable

(另外的方法遇到了再细看)

(时间太宝贵了啊,我得去抓住大头,这个还需要反复多来调试,温故而知新,吾以复观)

 

小操作

一个类的静态成员变量,用另一个类的静态方法获得该类的一个实例(这个套路叫着静态工厂方法,单例模式)

感悟

惊叹小伙伴技艺的同时,也就明白了我们团队是为何脱颖而出,有大神带啊

 

 

猜你喜欢

转载自www.cnblogs.com/lovesufang/p/12616185.html
今日推荐