WebSocket要求服务器与客户端之间要保持连接状态,当然当客户端或服务器端关闭了连接对方是有办法检测到的,这个无需我们关心;可以通过发送心跳保持2者之间连接的有效性。从另外一个方面来看,如果是客户端和服务器之间有交互的应用,客户端长时间没有发送业务数据到服务器则说明用户可能在短时间内不会再使用客户端了,这时服务器端可以将这些闲置的连接关闭以达到减少资源的开销。
为了达到这个目的,首先我们需要知道的是客户端是否已经闲置超过了指定的时限,那我们怎样知道呢。最初考虑的方案是这样的,每次客户端发送业务数据到服务器端时,我们就调用System.currentTimeMillis 函数获取到当前系统的毫秒数并在连接对应的Client对象中记录下这个数量preReceiveTimestamp。在系统中创建一个线程遍历每个客户端连接,取出preReceiveTimestamp 和系统当前的毫秒数System.currentTimeMillis 进行比较,如果 System.currentTimeMillis - preReceiveTimestamp 之差超过了设置的超时限制 则说明客户端已经闲置超时 可以关闭了。
但是System.currentTimeMillis 函数的调用需要从 应用线程 切换 到系统内核线程 然后再切换到应用线程中,他们之间的切换如果太过频繁对系统的性能还是有一定的影响。有人测试过在一般的PC服务器上1000w次System.currentTimeMillis调用大概需要12秒左右,平均每次1.3毫秒。这对于大并发量的服务器来说性能上的影响不得不考虑。
为了避免频繁的调用System.currentTimeMillis,而又要达到检查客户端闲置是否超过时限,在开源WebSocket服务器CshBBrain中进行了优化。优化后的方案为当客户端有发送数据到服务器端时,将连接对应的Client对象中的数据接收标识设置为true;在系统中创建一个线程,线程遍历每个客户端连接,检测客户端Client对象中的数据接收标识是否为true,如果不为true则说明客户端闲置超时可以关闭了,如果数据接收标识为true则说明客户端没有闲置超时,并将客户端Client对象中的数据接收标识设置为false,线程没隔系统设置的超时时限对客户端进行是否闲置超时检查一次。这样就避免了频繁的调用System.currentTimeMillis函数,将影响降低到最低。
客户端闲置检查线程核心代码,截取的MasterServer类中对应代码:
private void startClientMonitor(){
while(noStopRequested){
try {
if(this.timeOut > 0){// 超时阀值
Iterator<Integer> it = clients.keySet().iterator();
while(it.hasNext()){
Integer key = it.next();
Client client = clients.get(key);
if(!client.isReadDataFlag()){// 超时没有收到数据
client.close();// 关闭连接
clients.remove(key);// 从映射表中删除连接
}else{
client.setReadDataFlag(false);// 将读取数据标识设置为false
}
}
this.clientMonitor.sleep(this.timeOut * 60 * 1000);// 隔指定时限检测一次
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务接收到客户端发送的业务数据处理将数据读取标识设置为true,截取的Client类中对应的代码:
private boolean readRequest(ByteBuffer byteBuffer){
SocketChannel socketChannel = (SocketChannel) this.key.channel();
boolean readSuccess = false;// 是否读取到数据标识
boolean returnValue = false;// 返回值
try{
int dataLength = 0;
do{// 读取客户端请求的数据并解码
dataLength = socketChannel.read(byteBuffer);
if(dataLength > 0){
byteBuffer.flip();
this.decoderHandler.process(byteBuffer,this);// 解码处理
byteBuffer.clear();
readSuccess = true;
this.readDataFlag = true;// 将读取数据标识设置为真
this.preBlank = false;// 上次不为空读
}else{
if(this.preBlank){
++this.readCount;
}
this.preBlank = true;
break;
}
}while(dataLength > 0);
if(this.readCount >= zoreFetchCount){// 空读超过指定次数,关闭链接,返回
log.info("the max count read: " + this.readCount);
this.close();
returnValue = false;
return returnValue;
}
}catch(IOException e){
e.printStackTrace();
this.close();
returnValue = false;
return returnValue;
}
if(readSuccess){// 如果读取到数据
if(requestWithFile.isReadFile()){// 是否读取文件
if(requestWithFile.readFinish()){// 是否读取完毕文件
//if(requestWithFile.getFileReceiver().finishWrite()){// 是否读取完毕文件
returnValue = true;
if(MasterServer.keepConnect){//长连接
this.registeRead();
}
}else{// 没有读取完毕文件,注册通道,继续读取
if(MasterServer.keepConnect){//长连接
this.registeRead();
}else{
try{
this.inputMonitorWorker.registeRead(key);
}catch(Exception e){
e.printStackTrace();
this.close();
returnValue = false;
this.requestWithFile.getFileReceiver().close();// 关闭文件
return returnValue;
}
this.inRead.compareAndSet(true, false);
}
returnValue = false;// 将文件内容读取完后再进行处理
}
}else{// 普通请求,没有上传文件
returnValue = true;
if(MasterServer.keepConnect){//长连接
this.registeRead();
}
}
}else{
returnValue = false;
if(MasterServer.keepConnect){//长连接
this.registeRead();
}
}
if(returnValue){// 读取完毕放入处理队列
HashMap<String, String> requestData = requestWithFile.getRequestData();
if(requestData != null){
this.getBizObjects().add(requestData);
}
}
return returnValue;
}
分享到:
相关推荐
《.NET开发中的NetSockets开源库:简化服务器与客户端通信》 在.NET开发领域,高效、便捷地实现服务器和客户端的通信是至关重要的。针对这一需求,开发者们创造了一个名为NetSockets的开源库,它极大地简化了网络...
WebSocket是Web应用中一种先进的通信协议,它允许在客户端和服务器之间建立持久的、低延迟的连接,从而实现双向通信。在传统的HTTP协议下,每次请求-响应都需要重新建立连接,而WebSocket则在初次握手后维持长连接,...
WebSocket是一种在客户端和服务器之间建立持久连接的网络通信协议,它允许双向实时通信,极大地提升了Web应用程序的性能和效率。在IT行业中,WebSocket已经成为实时Web应用程序的重要支柱,尤其是在需要低延迟、高...
WebSocket是一种在客户端和服务器之间建立持久连接的协议,它允许双方进行双向通信,极大地提高了实时交互性。在IT行业中,特别是在Web应用性能测试领域,WebSocket的测试变得越来越重要。Apache JMeter,作为一款...
在现代Web应用程序中,WebSocket已经成为实时通信的重要协议,它提供了双向全双工通信,使得服务器与客户端可以实时交换数据。为了对WebSocket服务进行性能测试和负载测试,我们可以利用Apache JMeter工具,特别是...
WebSocket是一种在客户端与服务器之间建立持久连接的协议,它允许双方进行全双工通信,即双向同时传输数据。在IT行业中,WebSocket常用于实时通信场景,如在线游戏、股票交易、聊天室等。Netty是一个高性能、异步...
WebSocket是一种在客户端和服务器之间建立长连接的通信协议,广泛应用于实时通信、游戏、聊天应用等场景。这个插件的出现,使得测试工程师可以方便地模拟WebSocket客户端的行为,对服务器端的WebSocket服务进行压力...
通过分析这些文件,可以更具体地了解如何在实际项目中应用Mina的连接、心跳和断线重连机制。不过,具体的实现细节和优化策略需结合代码来进一步学习和理解。 总结起来,Apache Mina提供了一套强大的网络通信框架,...
WebSocket是一种在客户端和服务器之间建立持久连接的协议,广泛应用于实时通信场景,如在线游戏、聊天应用和股票交易等。JMeterWebSocketSampler是JMeter的一个扩展,使得测试WebSocket服务成为可能。它的主要特性...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...
WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...