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

 更新时间:2017年03月16日 11:31:01   作者:sun_t89  
本篇文章主要介绍了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

运行结果如下


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

相关文章

  • Spring Cloud如何切换Ribbon负载均衡模式

    Spring Cloud如何切换Ribbon负载均衡模式

    这篇文章主要介绍了Spring Cloud如何切换Ribbon负载均衡模式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • MyBatis超详细讲解如何实现分页功能

    MyBatis超详细讲解如何实现分页功能

    MyBatis-Plus 是一个 Mybatis 增强版工具,在 MyBatis 上扩充了其他功能没有改变其基本功能,为了简化开发提交效率而存在,本篇文章带用它实现分页功能
    2022-03-03
  • SpringBoot应用监控Actuator使用隐患及解决方案

    SpringBoot应用监控Actuator使用隐患及解决方案

    SpringBoot的Actuator 模块提供了生产级别的功能,比如健康检查,审计,指标收集,HTTP 跟踪等,帮助我们监控和管理Spring Boot 应用,本文将给大家介绍SpringBoot应用监控Actuator使用隐患及解决方案,需要的朋友可以参考下
    2024-07-07
  • Java编程倒计时实现方法示例

    Java编程倒计时实现方法示例

    这篇文章主要介绍了Java编程倒计时实现的三个示例,三种实现方法,具有一定参考价值,需要的朋友可以了解下。
    2017-09-09
  • Java ASM使用logback日志级别动态切换方案展示

    Java ASM使用logback日志级别动态切换方案展示

    这篇文章主要介绍了Java ASM使用logback日志级别动态切换方案展示,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • hibernate通过session实现增删改查操作实例解析

    hibernate通过session实现增删改查操作实例解析

    这篇文章主要介绍了hibernate通过session实现增删改查操作实例解析,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • 自定义注解基本概念和使用方式

    自定义注解基本概念和使用方式

    这篇文章主要介绍了自定义注解基本概念和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • YGC前后新生代是否变大分析详解

    YGC前后新生代是否变大分析详解

    要解释这个问题,我们先要弄清楚YGC的过程,parNew是新生代的gc算法,简单来说从gc roots开始扫描对象,当扫到一个只要是属于新生代的对象就将其挪到to space,但是老的对象还不会做释放,直到gc完成之后再看是否释放老的对象
    2022-01-01
  • Spring实战之协调作用域不同步的Bean操作示例

    Spring实战之协调作用域不同步的Bean操作示例

    这篇文章主要介绍了Spring实战之协调作用域不同步的Bean操作,结合实例形式分析了Spring协调作用域不同步的Bean相关配置及使用技巧,需要的朋友可以参考下
    2019-11-11
  • sentinel 整合spring cloud限流的过程解析

    sentinel 整合spring cloud限流的过程解析

    这篇文章主要介绍了sentinel 整合spring cloud限流,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03

最新评论