Spring Boot實(shí)戰(zhàn)之netty-socketio實(shí)現(xiàn)簡(jiǎn)單聊天室(給指定用戶推送消息)
網(wǎng)上好多例子都是群發(fā)的,本文實(shí)現(xiàn)一對(duì)一的發(fā)送,給指定客戶端進(jìn)行消息推送
1、本文使用到netty-socketio開源庫(kù),以及MySQL,所以首先在pom.xml中添加相應(yīng)的依賴庫(kù)
<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, 添加端口及主機(jī)數(shù)據(jù)庫(kù)連接等相關(guān)配置,
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的相關(guān)配置信息
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);
//該處可以用來(lái)進(jìn)行身份驗(yàn)證
config.setAuthorizationListener(new AuthorizationListener() {
@Override
public boolean isAuthorized(HandshakeData data) {
//http://localhost:8081?username=test&password=test
//例如果使用上面的鏈接進(jìn)行connect,可以使用如下代碼獲取用戶密碼信息,本文不做身份驗(yàn)證
// 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、添加消息結(jié)構(gòu)類MessageInfo.java
package com.xiaofangtech.sunt.message;
public class MessageInfo {
//源客戶端id
private String sourceClientId;
//目標(biāo)客戶端id
private String targetClientId;
//消息類型
private String msgType;
//消息內(nèi)容
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、添加客戶端信息,用來(lái)存放客戶端的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、添加查詢數(shù)據(jù)庫(kù)接口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事件,當(dāng)客戶端發(fā)起連接時(shí)調(diào)用,本文中將clientid與sessionid存入數(shù)據(jù)庫(kù)
//方便后面發(fā)送消息時(shí)查找到對(duì)應(yīng)的目標(biāo)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事件,客戶端斷開連接時(shí)調(diào)用,刷新客戶端信息
@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);
}
}
//消息接收入口,當(dāng)接收到消息后,查找發(fā)送目標(biāo)客戶端,并且向該客戶端發(fā)送消息,且給自己發(fā)送消息
@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、工程結(jié)構(gòu)

10、運(yùn)行測(cè)試
1) 添加基礎(chǔ)數(shù)據(jù),數(shù)據(jù)庫(kù)中預(yù)置3個(gè)客戶端testclient1,testclient2,testclient3

2) 創(chuàng)建客戶端文件index.html,index2.html,index3.html分別代表testclient1 testclient2 testclient3三個(gè)用戶
本文直接修改的https://github.com/mrniko/netty-socketio-demo/tree/master/client 中的index.html文件
其中clientid為發(fā)送者id, targetclientid為目標(biāo)方id,本文簡(jiǎn)單的將發(fā)送方和接收方寫死在html文件中
使用 以下代碼進(jìn)行連接
io.connect('http://localhost:8081?clientid='+clientid);
index.html 文件內(nèi)容如下
<!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、本例測(cè)試時(shí)
testclient1 發(fā)送消息給 testclient2
testclient2 發(fā)送消息給 testclient1
testclient3發(fā)送消息給testclient1
運(yùn)行結(jié)果如下



以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JavaWeb中Tomcat底層機(jī)制和Servlet運(yùn)行原理詳解
這篇文章主要介紹了JavaWeb中Tomcat底層機(jī)制和Servlet運(yùn)行原理詳解,Tomcat是一個(gè)開源的Java Web服務(wù)器,它是基于Java Servlet和JavaServer Pages(JSP)技術(shù)的,下面是關(guān)于Tomcat底層機(jī)制和Servlet運(yùn)行原理的簡(jiǎn)要說(shuō)明,需要的朋友可以參考下2023-10-10
Java中ArrayList與LinkedList的使用及區(qū)別詳解
這篇文章主要給大家介紹了關(guān)于Java中ArrayList與LinkedList的使用及區(qū)別的相關(guān)資料,ArrayList和LinkedList都是實(shí)現(xiàn)了List接口的容器類,用于存儲(chǔ)一系列的對(duì)象引用,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11
SpringSecurity集成第三方登錄過(guò)程詳解(最新推薦)
在ThirdAuthenticationFilter 類的attemptAuthentication()方法中,我們通過(guò)authType類型,然后創(chuàng)建對(duì)應(yīng)的Authentication實(shí)現(xiàn)來(lái)實(shí)現(xiàn)不同方式的登錄,下面給大家分享SpringSecurity集成第三方登錄過(guò)程,感興趣的朋友一起看看吧2024-05-05
使用Mybatis遇到的坑之Integer類型參數(shù)的解讀
這篇文章主要介紹了使用Mybatis遇到的坑之Integer類型參數(shù)的解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringMVC如何獲取表單數(shù)據(jù)(radio和checkbox)
這篇文章主要介紹了SpringMVC如何獲取表單數(shù)據(jù)(radio和checkbox)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
詳解Spring Boot 使用slf4j+logback記錄日志配置
本篇文章主要介紹了Spring Boot 使用slf4j+logback記錄日志配置,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
logback ThrowableProxyConverter類源碼流程解析
這篇文章主要為大家介紹了logback ThrowableProxyConverter類源碼流程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12

