开始学习WebSocket,准备用它来实现一个在页面实时输出log4j的日志以及控制台的日志。

首先知道一些基础信息:

  1. java7 开始支持WebSocket,并且只是做了定义,并未实现
  2. tomcat7及以上,jetty 9.1及以上实现了WebSocket,其他容器没有研究
  3. spring 4.0及以上增加了WebSocket的支持
  4. spring 支持STOMP协议的WebSocket通信
  5. WebSocket 作为java的一个扩展,它属于javax包目录下,通常需要手工引入该jar,以tomcat为例,可以在 tomcat/lib 目录下找到 websocket-api.jar

开始实现

先写一个普通的WebSocket客户端,直接引入tomcat目录下的jar,主要的jar有:websocket-api.jar、tomcat7-websocket.jar

     public static void f1() {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer(); // 获取WebSocket连接器,其中具体实现可以参照websocket-api.jar的源码,Class.forName("org.apache.tomcat.websocket.WsWebSocketContainer");
String uri = "ws://localhost:8081/log/log";
Session session = container.connectToServer(Client.class, new URI(uri)); // 连接会话
session.getBasicRemote().sendText("123132132131"); // 发送文本消息
session.getBasicRemote().sendText("4564546");
} catch (Exception e) {
e.printStackTrace();
}
}

其中的URL格式必须是ws开头,后面接注册的WebSocket地址

Client.java 是用于收发消息

@ClientEndpoint
public class Client { @OnOpen
public void onOpen(Session session) {
System.out.println("Connected to endpoint: " + session.getBasicRemote());
} @OnMessage
public void onMessage(String message) {
System.out.println(message);
} @OnError
public void onError(Throwable t) {
t.printStackTrace();
}
}

到这一步,客户端的收发消息已经完成,现在开始编写服务端代码,用Spring 4.0,其中pom.xml太长就不贴出来了,会用到jackson,spring-websocket,spring-message

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import com.gionee.log.client.LogWebSocketHandler; /**
* 注册普通WebScoket
* @author PengBin
* @date 2016年6月21日 下午5:29:00
*/
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer { @Autowired
@Lazy
private SimpMessagingTemplate template; /** {@inheritDoc} */
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(logWebSocketHandler(), "/log"); // 此处与客户端的 URL 相对应
} @Bean
public WebSocketHandler logWebSocketHandler() {
return new LogWebSocketHandler(template);
} }
 import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler; /**
*
* @author PengBin
* @date 2016年6月24日 下午6:04:39
*/
public class LogWebSocketHandler extends TextWebSocketHandler { private SimpMessagingTemplate template; public LogWebSocketHandler(SimpMessagingTemplate template) {
this.template = template;
System.out.println("初始化 handler");
} @Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String text = message.getPayload(); // 获取提交过来的消息
System.out.println("handMessage:" + text);
// template.convertAndSend("/topic/getLog", text); // 这里用于广播
session.sendMessage(message);
}
}

这样,一个普通的WebSocket就完成了,自己还可以集成安全控制等等

Spring还支持一种注解的方式,可以实现订阅和广播,采用STOMP格式协议,类似MQ,其实应该就是用的MQ的消息格式,下面是实现

同样客户端:

     public static void main(String[] args) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://localhost:8081/log/hello/hello/websocket";
Session session = container.connectToServer(Client.class, new URI(uri));
char lf = 10; // 这个是换行
char nl = 0; // 这个是消息结尾的标记,一定要
StringBuilder sb = new StringBuilder();
sb.append("SEND").append(lf); // 请求的命令策略
sb.append("destination:/app/hello").append(lf); // 请求的资源
sb.append("content-length:14").append(lf).append(lf); // 消息体的长度
sb.append("{\"name\":\"123\"}").append(nl); // 消息体 session.getBasicRemote().sendText(sb.toString()); // 发送消息
Thread.sleep(50000); // 等待一小会
session.close(); // 关闭连接 } catch (Exception e) {
e.printStackTrace();
}
}

这里一定要注意,换行符和结束符号,这个是STOMP协议规定的符号,错了就不能解析到

服务端配置

 /**
* 启用STOMP协议WebSocket配置
* @author PengBin
* @date 2016年6月24日 下午5:59:42
*/
@Configuration
@EnableWebMvc
@EnableWebSocketMessageBroker
public class WebSocketBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer { /** {@inheritDoc} */
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
System.out.println("注册");
registry.addEndpoint("/hello").withSockJS(); // 注册端点,和普通服务端的/log一样的
// withSockJS()表示支持socktJS访问,在浏览器中使用
} /** {@inheritDoc} */
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
System.out.println("启动");
config.enableSimpleBroker("/topic"); //
config.setApplicationDestinationPrefixes("/app"); // 格式前缀
} }

Controller

 @Controller
public class LogController { private SimpMessagingTemplate template; @Autowired
public LogController(SimpMessagingTemplate template) {
System.out.println("init");
this.template = template;
} @MessageMapping("/hello")
@SendTo("/topic/greetings") // 订阅
public Greeting greeting(HelloMessage message) throws Exception {
System.out.println(message.getName());
Thread.sleep(3000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
} }

到这里就已经全部完成。

template.convertAndSend("/topic/greetings", "通知"); // 这个的意思就是向订阅了/topic/greetings进行广播

对于用socktJS连接的时候会有一个访问 /info 地址的请求

如果在浏览器连接收发送消息,则用sockt.js和stomp.js

  function connect() {
var socket = new SockJS('/log/hello/hello');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function(greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
} function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
} function sendName() {
var name = document.getElementById('name').value;
stompClient.send("/app/hello", {}, JSON.stringify({
'name' : name
}));
}

在浏览器中可以看到请求返回101状态码,意思就是切换协议

更多信息参考:

  1. STOMP协议  https://stomp.github.io/stomp-specification-1.2.html
  2. Spring官方WebSocket demo  https://github.com/rstoyanchev/spring-websocket-test
  3. 官方文档 http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
  4. http://assets.spring.io/wp/WebSocketBlogPost.html

java WebSocket的实现以及Spring WebSocket的更多相关文章

  1. 【转】Spring websocket 使用

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html https://spr ...

  2. 【Java Web开发学习】Spring MVC整合WebSocket通信

    Spring MVC整合WebSocket通信 目录 ========================================================================= ...

  3. spring websocket源码分析续Handler的使用

    1. handler的定义 spring websocket支持的消息有以下几种: 对消息的处理就使用了Handler模式,抽象handler类AbstractWebSocketHandler.jav ...

  4. 学习WebSocket(一):Spring WebSocket的简单使用

    1.什么是websocket? WebSocket协议定义了一种web应用的新功能,它实现了服务器端和客户端的全双工通信.全双工通信即通信的双方可以同时发送和接收信息 的信息交互方式.它是继Java ...

  5. spring websocket源码分析

    什么是websocket? 摘录于wiki[1]: WebSocket is a protocol providing full-duplex communication channels over ...

  6. spring+websocket整合

    java-websocket的搭建非常之容易,没用框架的童鞋可以在这里下载撸主亲自调教好的java-websocket程序: Apach Tomcat 8.0.3+MyEclipse+maven+JD ...

  7. web即时通讯2--基于Spring websocket达到web聊天室

    如本文所用,Spring4和websocket要构建web聊天室,根据框架SpringMVC+Spring+Hibernate的Maven项目,后台使用spring websocket进行消息转发和聊 ...

  8. spring+websocket综合(springMVC+spring+MyBatis这是SSM框架和websocket集成技术)

    java-websocket该建筑是easy.儿童无用的框架可以在这里下载主线和个人教学好java-websocket计划: Apach Tomcat 8.0.3+MyEclipse+maven+JD ...

  9. Spring Websocket实现简易在线聊天功能

    针对Spring Websocket的实现,我参照了其他博主的文章https://www.cnblogs.com/leechenxiang/p/5306372.html 下面直接给出实现: 一.引入相 ...

随机推荐

  1. 线程相关函数(2)-pthread_self()获取调用线程ID

    获取调用线程tid #include <pthread.h>pthread_t pthread_self(void); 示例: #include <pthread.h> #in ...

  2. 【C语言】22-枚举

    上一讲介绍了结构体类型,这讲就介绍C语言中的另一种数据类型---枚举类型.枚举类型在iOS中也是很常用的,用法跟Java中的枚举类似. 一.枚举的概念 枚举是C语言中的一种基本数据类型,并不是构造类型 ...

  3. Scrum培训心得体会

    # Scrum培训心得体会 非常荣幸能够参加公司组织的这场为期两天的培训,赛宝的老师讲的非常好.通过这次学习,理解了当前最流行的Scrum开发框架,下面总结了我对Scrum的理解. ## scrum的 ...

  4. Windows Phone 修改系统定义的资源颜色

    [问题的背景] 相信有些经验的WP研发同学都会遇到下面的问题: 系统控件以及WPToolkit中大量使用了PhoneAccentBrush这个画刷(这个画刷定义的是系统的强调色,即用户选择的主题颜色) ...

  5. 知乎日报 API 分析

    声明 下面全部 API 均由 知乎(Zhihu.Inc) 提供,本人採取非正常手段获取. 获取与共享之行为或有侵犯知乎权益的嫌疑.若被告知需停止共享与使用.本人会及时删除此页面与整个项目. 请您暸解相 ...

  6. beaglebone black ubuntu display x11 server的配置

     Change default resolution on BeagleBone modesetting vs fbdev digiteltlc May 7th, 2014, 03:28 PM Hi ...

  7. ASP.NET 5 将于2016年一季度公布

    简单介绍:微软ASP.NET团队在GitHub宣布ASP.NET 5的公布时间表和发展蓝图. 该团队宣布在2015年还将公布三个Beta版.一个ASP.NET 5的抢先版(RC 1).到2016年一季 ...

  8. Oracle 错误:ORA-06413: Connection not open 解决办法

    http://blog.csdn.net/neso520/article/details/6037411 ——————————————————————————————————————————————— ...

  9. 回文树(统计所有回文串的个数) - MCCME 1750 Подпалиндромы

    Подпалиндромы Problem's Link: http://informatics.mccme.ru//mod/statements/view.php?chapterid=1750# M ...

  10. 点击edittext并显示其内容

    package com.example.sum;//sum import com.example.sum.R;//sum import android.app.Activity; import and ...