引言:
websocket和socket的区别是什么?
在开始之前,想起刚出校门的时候,曾经想进入软件开发行业,然而一直在学校里面成长的我们忽略了社会的包容。这件事情我记得很清楚:以实习生的身份去面试软件开发,面试官百般刁难,各种看不起没有经验的面试者,在各种嘲讽和蔑视中,他提问了一个问题——html和xml的区别是什么,我竟语塞。我确实说不清楚,但是我敢肯定你从来没有见过这么傲慢的面试官。
再后来,随着工作时间的增长,随着知识的积累,也慢慢的进入了正轨,也面试过不少没有经验或者经验不足的人,但是我一直以包容和相互学习的态度认真和每个人交流,只为了给有想进入这个行业的人一个机会,一个鼓励,当他们不幸遇到上面的那种面试官的时候,可以理直气壮、胸有成竹的回答说:
艹你吗的,html和xml的区别就和周杰和周杰伦的区别是一样的,然后踢门而去。
所以,没错,websocket和socket的区别就像是周杰和周杰伦的区别一样,并没有什么关系。
正文:
本来就不是搞JAVA的,遇到这种问题肯定是去google,去stackoverflow等等,但是,当你发现在网上按照别人说的坐下来之后,就是不成功,要么就是版本问题,要么就是描述不全,要么就是。。。反正就是不能用。
好吧,确实谁也不能说有多么的细心,所以万事还是要靠自己,搞起。(这篇文章针对有spring经验的同学阅读)
首先创建一个maven项目,添加如下依赖,或者普通项目添加spring-websocket的jar包
<!--spring websocket-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
接下来配置web.xml,让spring拦截所有的请求
<servlet>
<display-name>minitoycloud</display-name>
<servlet-name>mvc-dispather</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispather</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
然后就是配置spring来扫描接下来我们需要的@Component包了
首先拦截器的配置需要除去我们对于websocket地址的拦截:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/**"/>
<mvc:exclude-mapping path="/ws/**"/>
<bean class="com.jasson.interceptors.BaseInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
然后配置spring去扫描我们的@Component等组件
<context:component-scan base-package="com.jasson">
<!-- 指定扫包规则 ,只扫描使用@Controller注解的JAVA类 -->
<!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>-->
<!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
</context:component-scan>
有些文章可能会提到其他的配置,或者压根就没有这两次向配置,其实大多数项目一般就配置给spring来管理链接了,所以web.xml基本不用管就行了。
做好了上面的这些,接下来就需要创建配置类了:
创建一个配置类,这里我的是JassonWebSocketConfig:
@Configuration
@EnableWebSocket
public class JassonWebSocketConfig implements WebSocketConfigurer {
@Autowired
JassonWebSocketHandler jassonWebSocketHandler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
System.out.println("register add handler");
webSocketHandlerRegistry.addHandler(jassonWebSocketHandler,"/ws").addInterceptors(new JassonWebSocketInterceptor()).setAllowedOrigins("*");
webSocketHandlerRegistry.addHandler(jassonWebSocketHandler,"/ws/sockjs").addInterceptors(new JassonWebSocketInterceptor()).setAllowedOrigins("*").withSockJS();
}
}
上面的JassonWebSocketHandler我们稍后创建。
接下来我们创建JassonWebSocketInterceptor用来对远端的握手请求进行过滤和标记,这里强调下,本来之前按照网上的方法写好了,但是无论如何都连接不上,不是200就是500或者400,还有302,后来发现,其实就是因为在beforeHandshake这里进行了逻辑判断,并且无意中拒绝了连接,所以,在环境搭建过程中,不建议直接添加逻辑判断,只打印响应log然后返回true即可,逻辑判断在websocket调通之后再一点点添加,这样很容易发现问题所在。
public class JassonWebSocketInterceptor implements HandshakeInterceptor {
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
System.out.println("handshake interceptor");
if (serverHttpRequest instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletHttpRequest = (ServletServerHttpRequest) serverHttpRequest;
HttpSession session = servletHttpRequest.getServletRequest().getSession();
System.out.println("session:"+session);
User user = (User) session.getAttribute("user");
System.out.println("user:"+user.toString());
if (user != null) {
if (user.getDevtype().equals("0"))
{
//标记打印机
map.put("user", user);
}else if (user.getDevtype().equals("1"))
{
//标记用户
map.put("user", user);
}else
{
System.out.println("未知的设备拒绝握手");
return false;
}
} else{
System.out.println("未登录用户拒绝握手");
return false;
}
}
return true;
}
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
}
}
最后就是我们的JassonWebSocketHandler了,这里集成了我们所有的通讯逻辑,也是我们管理session的地方,建议测试阶段可以直接用map或者list用来存储用户的session,如果用户量大则需要考虑使用redis来管理用户session,代码如下:
@Component
public class JassonWebSocketHandler implements WebSocketHandler {
public static final Map<String,WebSocketSession> userSocketSessionMap;
static {
System.out.println("creat socket handler");
userSocketSessionMap = new HashMap<String, WebSocketSession>();
}
//握手实现连接后
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
User user = (User) webSocketSession.getAttributes().get("user");
System.out.println(user.toString());
sendMessageToAllUser(new TextMessage(("welcome ["+ user.getUid()+"] to minitoycloud").getBytes()));
}
//发送信息前的处理
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
System.out.println("收到消息:"+webSocketMessage.getPayload().getClass().getName());
//这个地方之所以加上逻辑,是为了方便大家知道怎么处理过来的消息,环境搭建过程还是不建议做逻辑判断
if (webSocketMessage.getPayloadLength() == 0)
{
return;
}
//获取socket通道中的数据并转化为Message对象
WSMessage message = new Gson().fromJson(webSocketMessage.getPayload().toString(),WSMessage.class);
System.out.println(message.toString());
sendMessageToUser(message.getToId(),message);
}
public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
System.out.println("transport error");
}
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
System.out.println("当前在线人数[" + userSocketSessionMap.size() + "]人");
}
public boolean supportsPartialMessages() {
return false;
}
//发送信息
public void sendMessageToUser(String uid, WSMessage message){
WebSocketSession session = userSocketSessionMap.get(uid) == null ? printerSocketSessionMap.get(uid):userSocketSessionMap.get(uid);
if (session != null && session.isOpen())
{
try {
session.sendMessage(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(message).getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void sendMessageToAllUser(TextMessage message) throws IOException{
Set<String> targetIDs = userSocketSessionMap.keySet();
WebSocketSession session = null;
for (String targetID:targetIDs){
session = userSocketSessionMap.get(targetID);
if (session.isOpen()){
session.sendMessage(message);
}
}
}
}
本来不想更的,项目写好了太长时间,放在那里再去找也麻烦,本着有始有终的原则,继续把流程跑通。
其实上面的代码已经完成了websocket服务端的搭建了,剩下的就是测试了,客户端通过
ws://服务端IP:服务端端口号/ws就可以成功进行连接了
例如:ws://127.0.0.1:8888/ws
网页端可采用上面的方式进行连接,但是对于IE10以下的浏览器,由于不兼容websocket,可使用下面的方式进行连接:
http://服务端IP:服务端端口号/ws/sockjs就可以成功进行连接了
例如:http://127.0.0.1:8888/ws/sockjs
至于接下来的发送消息测试,客户端连接的维持,就请大家在实践中进行测试,毕竟实践才是掌握技术的最有效方式。
网友评论