socket.io netty java,SpringBoot+Netty-socketio实现websocket

socket.io是js实现的,websocket框架,为了解决浏览器不兼容问题而设计

socket.io.js下载地址:https://cdnjs.com/libraries/socket.io

常用的方式是,前端使用socket.io.js,后端使用node.js实现socket.io的接口,可是我们的架构后端使用的是java,所以我使用的是netty-socketio,基于spring-boot实现;

一.pom.xml中添加依赖

com.corundumstudio.socketio

netty-socketio

1.7.11

二.修改SpringBoot启动类

package domain;

import com.corundumstudio.socketio.SocketIOServer;

import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;

import domain.util.YmlConfig;

import org.mybatis.spring.annotation.MapperScan;

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

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.builder.SpringApplicationBuilder;

import org.springframework.boot.web.support.SpringBootServletInitializer;

import org.springframework.context.annotation.Bean;

import org.springframework.transaction.annotation.EnableTransactionManagement;

/**

* Spring Boot 应用启动类

*

* @author : liangxifeng

* @date : 2018-1-19

*/

// Spring Boot 应用的标识

@SpringBootApplication

//如果mybatis中service实现类中加入事务注解,需要此处添加该注解

@EnableTransactionManagement

// mapper 接口类扫描包配置

@MapperScan("domain.dao")

public class AdverCenterApplication extends SpringBootServletInitializer {

//读取配置文件

@Autowired

private YmlConfig ymlConfig;

@Override

protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

return application.sources(AdverCenterApplication.class);

}

public static void main(String[] args) {

SpringApplication.run(AdverCenterApplication.class, args);

}

/**

* 注册netty-socketio服务端

* @author liangxifeng 2018-07-07

* @return

*/

@Bean

public SocketIOServer socketIOServer() {

com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();

String os = System.getProperty("os.name");

if(os.toLowerCase().startsWith("win")){ //在本地window环境测试时用localhost

System.out.println("this is windows");

config.setHostname("localhost");

} else {

config.setHostname("192.168.9.209");

}

config.setPort("9092");

/*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;

}

/**

* tomcat启动时候,扫码socket服务器并注册

* @param socketServer

* @return

*/

@Bean

public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {

return new SpringAnnotationScanner(socketServer);

}

}

三.在项目服务启动的时候启动socket.io服务, 新增ServerRunner.java

package domain.websocketio;

import com.corundumstudio.socketio.SocketIOServer;

import lombok.extern.slf4j.Slf4j;

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

import org.springframework.boot.CommandLineRunner;

import org.springframework.core.annotation.Order;

import org.springframework.stereotype.Component;

/**

* 在项目服务启动的时候启动socket.io服务

* @author liangxifeng 2018-07-07

*/

@Component

@Order(value=1)

@Slf4j

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();

log.info("socket.io启动成功!");

}

}

四.接收前台用户信息类 MessageInfo.java

package domain.websocketio;

import lombok.ToString;

import org.springframework.stereotype.Component;

/**

* 接收前台用户信息类

* @author liangxifeng 2018-07-07

*/

@Component

@ToString

public class MessageInfo {

String msgContent;

public String getMsgContent() {

return this.msgContent;

}

public void setMsgContent(String msgContent) {

this.msgContent = msgContent;

}

}

消息事件,作为后端与前台交互MessageEventHandler.java

package domain.websocketio;

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 domain.websocket.MyWebSocket;

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

import org.springframework.stereotype.Component;

import java.util.ArrayList;

import java.util.UUID;

import java.util.concurrent.ConcurrentHashMap;

/**

* 消息事件,作为后端与前台交互

* @authoer liangxifeng 2018-07-07

*/

@Component

public class MessageEventHandler {

public static SocketIOServer socketIoServer;

static ArrayList listClient = new ArrayList();

static final int limitSeconds = 60;

//线程安全的map

public static ConcurrentHashMap webSocketMap = new ConcurrentHashMap();

@Autowired

public MessageEventHandler(SocketIOServer server) {

this.socketIoServer = server;

}

/**

* 客户端连接的时候触发,前端js触发:socket = io.connect("http://192.168.9.209:9092");

* @param client

*/

@OnConnect

public void onConnect(SocketIOClient client) {

String mac = client.getHandshakeData().getSingleUrlParam("mac");

listClient.add(client.getSessionId());

//以mac地址为key,SocketIOClient 为value存入map,后续可以指定mac地址向客户端发送消息

webSocketMap.put(mac,client);

//socketIoServer.getClient(client.getSessionId()).sendEvent("message", "back data");

System.out.println("客户端:" + client.getSessionId() + "已连接,mac="+mac);

}

/**

* 客户端关闭连接时触发:前端js触发:socket.disconnect();

* @param client

*/

@OnDisconnect

public void onDisconnect(SocketIOClient client) {

System.out.println("客户端:" + client.getSessionId() + "断开连接");

}

/**

* 自定义消息事件,客户端js触发:socket.emit('messageevent', {msgContent: msg}); 时触发

* 前端js的 socket.emit("事件名","参数数据")方法,是触发后端自定义消息事件的时候使用的,

* 前端js的 socket.on("事件名",匿名函数(服务器向客户端发送的数据))为监听服务器端的事件

* @param client 客户端信息

* @param request 请求信息

* @param data 客户端发送数据{msgContent: msg}

*/

@OnEvent(value = "messageevent")

public void onEvent(SocketIOClient client, AckRequest request, MessageInfo data) {

System.out.println("发来消息:" + data);

//服务器端向该客户端发送消息

//socketIoServer.getClient(client.getSessionId()).sendEvent("messageevent", "你好 data");

client.sendEvent("messageevent","我是服务器都安发送的信息");

}

public static void sendBuyLogEvent() { //这里就是向客户端推消息了

//String dateTime = new DateTime().toString("hh:mm:ss");

for (UUID clientId : listClient) {

if (socketIoServer.getClient(clientId) == null) continue;

socketIoServer.getClient(clientId).sendEvent("enewbuy", "当前时间", 1);

}

}

}

五.目录结构

image.png

六.html内容

websocket-java-socketio

Socket.io Test

Waiting for input

hello world!

Connect

Disconnect

Send Message

/**

* 前端js的 socket.emit("事件名","参数数据")方法,是触发后端自定义消息事件的时候使用的,

* 前端js的 socket.on("事件名",匿名函数(服务器向客户端发送的数据))为监听服务器端的事件

**/

var socket = io.connect("http://192.168.9.209:9092?mac=2");

var firstconnect = true;

function connect() {

if(firstconnect) {

//socket.on('reconnect', function(){ status_update("Reconnected to Server"); });

//socket.on('reconnecting', function( nextRetry ){ status_update("Reconnecting in "

//+ nextRetry + " seconds"); });

//socket.on('reconnect_failed', function(){ message("Reconnect Failed"); });

//firstconnect = false;

} else {

socket.socket.reconnect();

}

}

//监听服务器连接事件

socket.on('connect', function(){ status_update("Connected to Server"); });

//监听服务器关闭服务事件

socket.on('disconnect', function(){ status_update("Disconnected from Server"); });

//监听服务器端发送消息事件

socket.on('messageevent', function(data) {

message(data)

//console.log("服务器发送的消息是:"+data);

});

//断开连接

function disconnect() {

socket.disconnect();

}

function message(data) {

document.getElementById('message').innerHTML = "Server says: " + data;

}

function status_update(txt){

document.getElementById('status').innerHTML = txt;

}

function esc(msg){

return msg.replace(//g, '>');

}

//点击发送消息触发

function send() {

//console.log("点击了发送消息,开始向服务器发送消息")

var msg = "我很好的,是的.";

socket.emit('messageevent', {msgContent: msg});

};

七.测试

访问浏览器

socketio-1.jpg

服务器端日志内容:

socketio-2.jpg

服务器指定mac地址用户向客户端发送消息

package domain.controller;

import com.corundumstudio.socketio.SocketIOClient;

import domain.domain.DomainResponse;

import domain.domain.Exhibition;

import domain.service.interfaces.exhibition.InsertExhibitionService;

import domain.websocket.PayWebSocket;

import domain.websocketio.MessageEventHandler;

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

import org.springframework.web.bind.annotation.*;

@RestController

@RequestMapping(value = "/exhibiton")

public class ExhibitonController {

@Autowired

private InsertExhibitionService insertExhibitionService;

@GetMapping(value = "/sendMsg/{mac}")

/**

* 指定某个websocket发送从服务器发送到浏览器,服务器主动推送

*/

private DomainResponse testSendMsg(@PathVariable("mac") String mac ) throws Exception{

DomainResponse msg = new DomainResponse(Integer.parseInt(mac),mac,"指定给"+mac+"用户发消息");

PayWebSocket payWebSocket = PayWebSocket.payWebSocketMap.get(mac);

if(payWebSocket == null)

{

return new DomainResponse(0,"没有该用户",0);

}

payWebSocket.sendMessage(msg);

return msg;

}

/**

* web socketio方式指定用户发送消息

* @param mac

* @return

*/

@GetMapping(value = "/socketIo/{mac}")

private DomainResponse socketIoTest(@PathVariable("mac") String mac){

SocketIOClient client = MessageEventHandler.webSocketMap.get(mac);

DomainResponse msg = new DomainResponse(Integer.parseInt(mac),mac,"指定给"+mac+"用户发消息");

client.sendEvent("messageevent","haha======++");

return msg;

}

}

测试

指定mac地址为2的用户发送消息

image.png

客户端会收到消息

socketio-3.jpg

你可能感兴趣的:(socket.io,netty,java)