我正在尝试使用带有tomcat 8.5的websocket来监视日志文件,但我的代码并没有按预期工作:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Writer;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value = "/ws/log")
public class LogEndPoint extends Endpoint {
private Thread tailer = null;
@Override
public void onOpen(Session session, EndpointConfig config) {
System.out.println("---------------- Connection Established ----------------");
System.out.println("----message handlers's size in current size=" + session.getMessageHandlers().size());
// session.addMessageHandler(new MessageHandler.Whole<String>() {
//
// @Override
// public void onMessage(String message) {
// System.out.println("<<<< " + message);
// try {
// tailer = new Thread(new FileTailer(getFilePath(message), session.getBasicRemote().getSendWriter(), 200));
// } catch (IOException e) {
// e.printStackTrace();
// try {
// session.close();
// } catch (IOException e1) {
// e1.printStackTrace();
// }
// }
// }
// });
}
@OnMessage
public void doOnMessage(String message, Session session) {
System.out.println("<<<< " + message);
try {
tailer = new Thread(new FileTailer(getFilePath(message), session.getBasicRemote().getSendWriter(), 200));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onClose(Session session, CloseReason closeReason) {
System.out.println("---------------- Connection Closed ----------------");
if(tailer != null)
tailer.interrupt();
}
private String getFilePath(String name){
switch (name) {
case "app": {
return "d:\\myApp.log";
}
case "server": {
return "d:\\myServer.log";
}
}
throw new RuntimeException("invalid log type");
}
@Override
public void onError(Session session, Throwable throwable) {
System.out.println("---------------- Error Occured ----------------");
throwable.printStackTrace();
if(tailer != null)
tailer.interrupt();
}
class FileTailer implements Runnable{
private int interval = 200;
private long lastKnownPosition;
private RandomAccessFile file;
private Writer writer;
FileTailer(String file, Writer writer, int interval){
this.interval = interval;
this.writer = writer;
try(RandomAccessFile tmp = new RandomAccessFile(file, "r")){
this.file = tmp;
lastKnownPosition = file.length();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
followFile();
}
private void followFile(){
while(!Thread.currentThread().isInterrupted()){
try {
Thread.sleep(interval);
if(file.length() > lastKnownPosition){
file.seek(lastKnownPosition);
String line = null;
while((line = file.readLine()) != null){
writer.write(line);
}
lastKnownPosition = file.getFilePointer();
}else{
System.out.println("after " + interval + " ms, no new data has been written into the file");
}
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}finally{
try {
if(file != null)
file.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
}
我使用javascript向此终点发送一些短信,但doOnMessage
方法永远不会被调用。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Logging Demo</title>
<style>
#shell{
background-color : #000;
color : #fff;
}
</style>
<script>
var socket = null;
function start(){
console.log('start');
socket = new WebSocket('ws://localhost:9080/labws/ws/log');
socket.onopen = function(event) {
console.log('onopen');
var label = document.getElementById('shell');
label.value = label.value + '-------------Connection Established-------------\n';
socket.send('app');
label.value = label.value + '>>>> app\n';
};
socket.onmessage = function(event) {
console.log('onmessage: ' + event.data);
var label = document.getElementById('shell');
label.value = label.value + '<<<< ' + event.data + '\n';
};
}
function stop(){
console.log('stop');
var label = document.getElementById('shell');
label.value = label.value + '-------------Connection Closed-------------\n';
// if (socket.readyState === WebSocket.OPEN) {
socket.close(1000, 'byebye');
// }
}
</script>
</head>
<body>
<div>
<h3>WebSocket Logging Demo</h3>
<input type="button" value="start" onclick="start()"/> <input type="button" value="stop" onclick="stop()" /><br />
<textarea id="shell" rows="10" cols="300"></textarea>
</div>
</body>
</html>
感兴趣的是onOpen
方法中的注释部分,如果我手动添加消息处理程序,它就像我预期的那样工作。我刚开始学习websocket,我有没有想念它?
对于其他人面临同样的问题,接受的anwser有一个好点,但我想加我的两分钱:
JSR 356第2章说:
There are two main means by which an endpoint can be created. The first means is to implement certain of
the API classes from the Java WebSocket API with the required behavior to handle the endpoint lifecycle,
consume and send messages, publish itself, or connect to a peer. Often, this specification will refer to this
kind of endpoint as a programmatic endpoint. The second means is to decorate a Plain Old Java Object
(POJO) with certain of the annotations from the Java WebSocket API. The implementation then takes these
annotated classes and creates the appropriate objects at runtime to deploy the POJO as a websocket endpoint.
Often, this specification will refer to this kind of endpoint as an annotated endpoint. The specification will
refer to an endpoint when it is talking about either kind of endpoint: programmatic or annotated.
基本上,这是接受的答案中提到的注释驱动模式和界面模式,但是说明并没有说我们不能使用混合模式! 所以这取决于实现,例如,我的上述代码适用于jetty。
答案 0 :(得分:0)
我认为你在两个编程模型之间混合,这两个编程模型作为JSR 356的一部分存在(它指定了Java开发人员在将WebSockets集成到他们的应用程序时可以使用的API):注释驱动的方法和界面驱动的方法。
使用注释驱动方法,您可以创建注释为@ServerEndpoint
注释的POJO作为端点。它的生命周期也由注释决定:@onOpen
,@OnMessage
等。
使用界面驱动的方法,您应该扩展javax.websocket.Endpoint类并覆盖onOpen
,onClose
和onError
方法。邮件处理程序仅在注释代码中的onOpen
方法中注册。
因此,在您的情况下,我建议不要扩展Endpoint类并添加所有生命周期注释,它应该可以工作。
Here is a more detailed article about JSR 365
修改强>
指定不说我们不能使用混合模式! 所以这取决于实现,例如,问题中的代码适用于jetty。