Spring Boot实战之netty-socketio实现简单聊天室(给指定用户推送消息)

网上好多例子都是群发的,本文实现一对一的发送,给指定客户端进行消息推送

1、本文使用到netty-socketio开源库,以及MySQL,所以首先在pom.xml中添加相应的依赖库

<dependency>
    <groupId>com.corundumstudio.socketio</groupId>
    <artifactId>netty-socketio</artifactId>
    <version>1.7.11</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency> 

2、修改application.properties, 添加端口及主机数据库连接等相关配置,

wss.server.port=8081
wss.server.host=localhost 

spring.datasource.url = jdbc:mysql://127.0.0.1:3306/springlearn
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driverClassName = com.mysql.jdbc.Driver 

# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect 

3、修改Application文件,添加nettysocket的相关配置信息

package com.xiaofangtech.sunt; 

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean; 

import com.corundumstudio.socketio.AuthorizationListener;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.HandshakeData;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner; 

@SpringBootApplication
public class NettySocketSpringApplication { 

  @Value("${wss.server.host}")
  private String host; 

  @Value("${wss.server.port}")
  private Integer port; 

  @Bean
  public SocketIOServer socketIOServer()
  {
    Configuration config = new Configuration();
    config.setHostname(host);
    config.setPort(port); 

    //该处可以用来进行身份验证
    config.setAuthorizationListener(new AuthorizationListener() {
      @Override
      public boolean isAuthorized(HandshakeData data) {
        //http://localhost:8081?username=test&password=test
        //例如果使用上面的链接进行connect,可以使用如下代码获取用户密码信息,本文不做身份验证
//       String username = data.getSingleUrlParam("username");
//       String password = data.getSingleUrlParam("password");
        return true;
      }
    }); 

    final SocketIOServer server = new SocketIOServer(config);
    return server;
  } 

  @Bean
  public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
    return new SpringAnnotationScanner(socketServer);
  } 

  public static void main(String[] args) {
    SpringApplication.run(NettySocketSpringApplication.class, args);
  }
}

4、添加消息结构类MessageInfo.java

package com.xiaofangtech.sunt.message; 

public class MessageInfo {
  //源客户端id
  private String sourceClientId;
  //目标客户端id
  private String targetClientId;
  //消息类型
  private String msgType;
  //消息内容
  private String msgContent; 

  public String getSourceClientId() {
    return sourceClientId;
  }
  public void setSourceClientId(String sourceClientId) {
    this.sourceClientId = sourceClientId;
  }
  public String getTargetClientId() {
    return targetClientId;
  }
  public void setTargetClientId(String targetClientId) {
    this.targetClientId = targetClientId;
  }
  public String getMsgType() {
    return msgType;
  }
  public void setMsgType(String msgType) {
    this.msgType = msgType;
  }
  public String getMsgContent() {
    return msgContent;
  }
  public void setMsgContent(String msgContent) {
    this.msgContent = msgContent;
  }
}

5、添加客户端信息,用来存放客户端的sessionid

package com.xiaofangtech.sunt.bean; 

import java.util.Date; 

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull; 

@Entity
@Table(name="t_clientinfo")
public class ClientInfo {
  @Id
  @NotNull
  private String clientid;
  private Short connected;
  private Long mostsignbits;
  private Long leastsignbits;
  private Date lastconnecteddate;
  public String getClientid() {
    return clientid;
  }
  public void setClientid(String clientid) {
    this.clientid = clientid;
  }
  public Short getConnected() {
    return connected;
  }
  public void setConnected(Short connected) {
    this.connected = connected;
  }
  public Long getMostsignbits() {
    return mostsignbits;
  }
  public void setMostsignbits(Long mostsignbits) {
    this.mostsignbits = mostsignbits;
  }
  public Long getLeastsignbits() {
    return leastsignbits;
  }
  public void setLeastsignbits(Long leastsignbits) {
    this.leastsignbits = leastsignbits;
  }
  public Date getLastconnecteddate() {
    return lastconnecteddate;
  }
  public void setLastconnecteddate(Date lastconnecteddate) {
    this.lastconnecteddate = lastconnecteddate;
  } 

}

6、添加查询数据库接口ClientInfoRepository.java

package com.xiaofangtech.sunt.repository; 

import org.springframework.data.repository.CrudRepository; 

import com.xiaofangtech.sunt.bean.ClientInfo; 

public interface ClientInfoRepository extends CrudRepository<ClientInfo, String>{
  ClientInfo findClientByclientid(String clientId);
}

7、添加消息处理类MessageEventHandler.Java

package com.xiaofangtech.sunt.message; 

import java.util.Date;
import java.util.UUID; 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.xiaofangtech.sunt.bean.ClientInfo;
import com.xiaofangtech.sunt.repository.ClientInfoRepository; 

@Component
public class MessageEventHandler
{
  private final SocketIOServer server; 

  @Autowired
  private ClientInfoRepository clientInfoRepository; 

  @Autowired
  public MessageEventHandler(SocketIOServer server)
  {
    this.server = server;
  }
  //添加connect事件,当客户端发起连接时调用,本文中将clientid与sessionid存入数据库
  //方便后面发送消息时查找到对应的目标client,
  @OnConnect
  public void onConnect(SocketIOClient client)
  {
    String clientId = client.getHandshakeData().getSingleUrlParam("clientid");
    ClientInfo clientInfo = clientInfoRepository.findClientByclientid(clientId);
    if (clientInfo != null)
    {
      Date nowTime = new Date(System.currentTimeMillis());
      clientInfo.setConnected((short)1);
      clientInfo.setMostsignbits(client.getSessionId().getMostSignificantBits());
      clientInfo.setLeastsignbits(client.getSessionId().getLeastSignificantBits());
      clientInfo.setLastconnecteddate(nowTime);
      clientInfoRepository.save(clientInfo);
    }
  } 

  //添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息
  @OnDisconnect
  public void onDisconnect(SocketIOClient client)
  {
    String clientId = client.getHandshakeData().getSingleUrlParam("clientid");
    ClientInfo clientInfo = clientInfoRepository.findClientByclientid(clientId);
    if (clientInfo != null)
    {
      clientInfo.setConnected((short)0);
      clientInfo.setMostsignbits(null);
      clientInfo.setLeastsignbits(null);
      clientInfoRepository.save(clientInfo);
    }
  } 

  //消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息
  @OnEvent(value = "messageevent")
  public void onEvent(SocketIOClient client, AckRequest request, MessageInfo data)
  {
    String targetClientId = data.getTargetClientId();
    ClientInfo clientInfo = clientInfoRepository.findClientByclientid(targetClientId);
    if (clientInfo != null && clientInfo.getConnected() != 0)
    {
      UUID uuid = new UUID(clientInfo.getMostsignbits(), clientInfo.getLeastsignbits());
      System.out.println(uuid.toString());
      MessageInfo sendData = new MessageInfo();
      sendData.setSourceClientId(data.getSourceClientId());
      sendData.setTargetClientId(data.getTargetClientId());
      sendData.setMsgType("chat");
      sendData.setMsgContent(data.getMsgContent());
      client.sendEvent("messageevent", sendData);
      server.getClient(uuid).sendEvent("messageevent", sendData);
    } 

  }
}

8、添加ServerRunner.java

package com.xiaofangtech.sunt.message; 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component; 

import com.corundumstudio.socketio.SocketIOServer; 

@Component
public class ServerRunner implements CommandLineRunner {
  private final SocketIOServer server; 

  @Autowired
  public ServerRunner(SocketIOServer server) {
    this.server = server;
  } 

  @Override
  public void run(String... args) throws Exception {
    server.start();
  }
}

9、工程结构

10、运行测试

1) 添加基础数据,数据库中预置3个客户端testclient1,testclient2,testclient3

2) 创建客户端文件index.html,index2.html,index3.html分别代表testclient1 testclient2 testclient3三个用户

本文直接修改的https://github.com/mrniko/netty-socketio-demo/tree/master/client 中的index.html文件

其中clientid为发送者id, targetclientid为目标方id,本文简单的将发送方和接收方写死在html文件中

使用 以下代码进行连接

io.connect('http://localhost:8081?clientid='+clientid); 

index.html 文件内容如下

<!DOCTYPE html>
<html>
<head> 

    <meta charset="utf-8" /> 

    <title>Demo Chat</title> 

    <link href="bootstrap.css" rel="external nofollow" rel="stylesheet"> 

  <style>
    body {
      padding:20px;
    }
    #console {
      height: 400px;
      overflow: auto;
    }
    .username-msg {color:orange;}
    .connect-msg {color:green;}
    .disconnect-msg {color:red;}
    .send-msg {color:#888}
  </style> 

  <script src="js/socket.io/socket.io.js"></script>
    <script src="js/moment.min.js"></script>
    <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> 

  <script> 

    var clientid = 'testclient1';
    var targetClientId= 'testclient2'; 

    var socket = io.connect('http://localhost:8081?clientid='+clientid); 

    socket.on('connect', function() {
      output('<span class="connect-msg">Client has connected to the server!</span>');
    }); 

    socket.on('messageevent', function(data) {
      output('<span class="username-msg">' + data.sourceClientId + ':</span> ' + data.msgContent);
    }); 

    socket.on('disconnect', function() {
      output('<span class="disconnect-msg">The client has disconnected!</span>');
    }); 

        function sendDisconnect() {
            socket.disconnect();
        } 

    function sendMessage() {
            var message = $('#msg').val();
            $('#msg').val(''); 

            var jsonObject = {sourceClientId: clientid,
                     targetClientId: targetClientId,
                     msgType: 'chat',
                     msgContent: message};
            socket.emit('messageevent', jsonObject);
    } 

    function output(message) {
            var currentTime = "<span class='time'>" + moment().format('HH:mm:ss.SSS') + "</span>";
            var element = $("<div>" + currentTime + " " + message + "</div>");
      $('#console').prepend(element);
    } 

    $(document).keydown(function(e){
      if(e.keyCode == 13) {
        $('#send').click();
      }
    });
  </script>
</head> 

<body> 

  <h1>Netty-socketio Demo Chat</h1> 

  <br/> 

  <div id="console" class="well">
  </div> 

    <form class="well form-inline" onsubmit="return false;">
      <input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/>
      <button type="button" onClick="sendMessage()" class="btn" id="send">Send</button>
      <button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
    </form>
</body>
</html>

3、本例测试时

testclient1 发送消息给 testclient2

testclient2 发送消息给 testclient1

testclient3发送消息给testclient1

运行结果如下

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • springboot + mybatis配置多数据源示例

    在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法 3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用Da

  • 详解SpringBoot文件上传下载和多文件上传(图文)

    最近在学习SpringBoot,以下是最近学习整理的实现文件上传下载的Java代码: 1.开发环境: IDEA15+ Maven+JDK1.8 2.新建一个maven工程: 3.工程框架 4.pom.xml文件依赖项 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation

  • 详解在Spring Boot框架下使用WebSocket实现消息推送

    spring Boot的学习持续进行中.前面两篇博客我们介绍了如何使用Spring Boot容器搭建Web项目以及怎样为我们的Project添加HTTPS的支持,在这两篇文章的基础上,我们今天来看看如何在Spring Boot中使用WebSocket. 什么是WebSocket WebSocket为浏览器和服务器之间提供了双工异步通信功能,也就是说我们可以利用浏览器给服务器发送消息,服务器也可以给浏览器发送消息,目前主流浏览器的主流版本对WebSocket的支持都算是比较好的,但是在实际开发中使

  • springboot实现拦截器之验证登录示例

    整理文档,搜刮出一个springboot实现拦截器之验证登录示例,稍微整理精简一下做下分享. 添加jar包,这个jar包不是必须的,只是在拦截器里用到了,如果不用的话,完全可以不引入 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dep

  • BootStrap与validator 使用笔记(JAVA SpringMVC实现)

    BootStrap 是一个强大的前面框架,它用优雅的方式解决了网页问题.最近正在使用其开发网站的表单验证,一点体会记录如下: 注:本文中借鉴了博客Franson 的文章使用bootstrap validator的remote验证代码经验分享(推荐) 一.准备工作 1.你的网站环境中要有 BootStrap,中文网地址:http://www.bootcss.com/ 2.下载BootStrap Validator相关材料,地址:http://bv.doc.javake.cn/ 当然,如果你不想一个

  • Spring boot实现数据库读写分离的方法

    背景 数据库配置主从之后,如何在代码层面实现读写分离? 用户自定义设置数据库路由 Spring boot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库. 实现可动态路由的数据源,在每次数据库查询操作前执行 ReadWriteSplitRoutingDataSource.java import org.springframework.jdbc.datasource.lookup.Abst

  • spring boot实现过滤器和拦截器demo

    整理文档,搜刮出一个spring boot实现过滤器和拦截器demo ,稍微整理精简一下做下分享. 拦截器定义: @WebServlet public class ActionInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Ex

  • Spring Boot Mysql 数据库操作示例

    本文默认你的开发环境.数据库已经安装好 想用使用数据库.我们需要现在pom文件中添加相应的依赖 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:sc

  • Spring Boot 启动端口如何启动

    Spring Boot 启动端口 Spring Boot 其默认是集成web容器的,启动方式由像普通Java程序一样,main函数入口启动.其内置Tomcat容器或Jetty容器,具体由配置来决定(默认Tomcat).当然你也可以将项目打包成war包,放到独立的web容器中(Tomcat.weblogic等等),当然在此之前你要对程序入口做简单调整. spring boot是个好东西,可以不用容器直接在main方法中启动,而且无需配置文件,方便快速搭建环境.可是当我们要同时启动2个springb

  • Spring Boot实战之netty-socketio实现简单聊天室(给指定用户推送消息)

    网上好多例子都是群发的,本文实现一对一的发送,给指定客户端进行消息推送 1.本文使用到netty-socketio开源库,以及MySQL,所以首先在pom.xml中添加相应的依赖库 <dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.11</version&

  • java基于netty NIO的简单聊天室的实现

    一.为何要使用netty开发 由于之前已经用Java中的socket写过一版简单的聊天室,这里就不再对聊天室的具体架构进行细致的介绍了,主要关注于使用netty框架重构后带来的改变.对聊天室不了解的同学可以先看下我的博客(<JAVA简单聊天室的实现>) 本篇博客所使用的netty版本为4.1.36,完整工程已上传到Github(https://github.com/Alexlingl/Chatroom),其中lib文件夹下有相应的netty jar包和source包,自行导入即可. 1.为何要

  • 实例详解Spring Boot实战之Redis缓存登录验证码

    本章简单介绍redis的配置及使用方法,本文示例代码在前面代码的基础上进行修改添加,实现了使用redis进行缓存验证码,以及校验验证码的过程. 1.添加依赖库(添加redis库,以及第三方的验证码库) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency&

  • spring boot实战之内嵌容器tomcat配置

    本文介绍了spring boot实战之内嵌容器tomcat配置,分享给大家,具体如下: 默认容器 spring boot默认web程序启用tomcat内嵌容器tomcat,监听8080端口,servletPath默认为 / 通过需要用到的就是端口.上下文路径的修改,在spring boot中其修改方法及其简单: 在资源文件中配置: server.port=9090 server.contextPath=/lkl 启动spring boot 2015-10-04 00:06:55.768 INFO

  • 详解Spring Boot实战之Filter实现使用JWT进行接口认证

    本文介绍了spring Boot实战之Filter实现使用JWT进行接口认证,分享给大家 jwt(json web token) 用户发送按照约定,向服务端发送 Header.Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api jwt使用流程 本文示例接上面几篇文章中的代码进行编写,请阅读本文的同时可以参考前面几篇文章 1.添加依赖库jjwt,本文中构造jwt及解析jwt都使用了jjwt库

  • 详解Spring Boot实战之单元测试

    本文介绍使用Spring测试框架提供的MockMvc对象,对Restful API进行单元测试 Spring测试框架提供MockMvc对象,可以在不需要客户端-服务端请求的情况下进行MVC测试,完全在服务端这边就可以执行Controller的请求,跟启动了测试服务器一样. 测试开始之前需要建立测试环境,setup方法被@Before修饰.通过MockMvcBuilders工具,使用WebApplicationContext对象作为参数,创建一个MockMvc对象. MockMvc对象提供一组工具

  • Spring Boot的filter(过滤器)简单使用实例详解

    过滤器(Filter)的注册方法和 Servlet 一样,有两种方式:代码注册或者注解注册 1.代码注册方式 通过代码方式注入过滤器 @Bean public FilterRegistrationBean indexFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(new IndexFilter()); registration.addUrlPatterns("/&quo

  • 详解Spring Boot实战之Rest接口开发及数据库基本操作

    本文介绍了Spring Boot实战之Rest接口开发及数据库基本操作,分享给大家 1.修改pom.xml,添加依赖库,本文使用的是mysql <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <

  • Java Spring Boot实战练习之单元测试篇

        一.关于JUnit的一些东西   在我们开发Web应用时,经常会直接去观察结果进行测试.虽然也是一种方式,但是并不严谨.作为开发者编写测试代码来测试自己所写的业务逻辑是,以提高代码的质量.降低错误方法的概率以及进行性能测试等.经常作为开发这写的最多就是单元测试.引入spring-boot-starter-testSpringBoot的测试依赖.该依赖会引入JUnit的测试包,也是我们用的做多的单元测试包.而Spring Boot在此基础上做了很多增强,支持很多方面的测试,例如JPA,Mo

  • Spring Boot实战解决高并发数据入库之 Redis 缓存+MySQL 批量入库问题

    目录 前言 架构设计 代码实现 测试 总结 前言 最近在做阅读类的业务,需要记录用户的PV,UV: 项目状况:前期尝试业务阶段: 特点: 快速实现(不需要做太重,满足初期推广运营即可)快速投入市场去运营 收集用户的原始数据,三要素: 谁在什么时间阅读哪篇文章 提到PV,UV脑海中首先浮现特点: 需要考虑性能(每个客户每打开一篇文章进行记录)允许数据有较小误差(少部分数据丢失) 架构设计 架构图: 时序图 记录基础数据MySQL表结构 CREATE TABLE `zh_article_count`

随机推荐