服务端

 @Configuration
public class NettySocketConfig { private static final Logger logger = LoggerFactory.getLogger(NettySocketConfig.class); @Bean
public SocketIOServer socketIOServer() {
//创建Socket,并设置监听端口
com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
// 设置主机名,默认是0.0.0.0
config.setHostname("192.168.8.107");
// 设置监听端口
config.setPort(9096);
// 协议升级超时时间(毫秒),默认10000。HTTP握手升级为ws协议超时时间
config.setUpgradeTimeout(10000);
// Ping消息间隔(毫秒),默认25000。客户端向服务器发送一条心跳消息间隔
config.setPingInterval(60000);
// Ping消息超时时间(毫秒),默认60000,这个时间间隔内没有接收到心跳消息就会发送超时事件
config.setPingTimeout(180000);
// 这个版本0.9.0不能处理好namespace和query参数的问题。所以为了做认证必须使用全局默认命名空间
config.setAuthorizationListener(new AuthorizationListener() {
@Override
public boolean isAuthorized(HandshakeData data) {
// 可以使用如下代码获取用户密码信息
//String username = data.getSingleUrlParam("username");
//String password = data.getSingleUrlParam("password");
//logger.info("连接参数:username=" + username + ",password=" + password);
//ManagerInfo managerInfo = managerInfoService.findByUsername(username);
//
//String salt = managerInfo.getSalt();
//String encodedPassword = ShiroKit.md5(password, username + salt);
//// 如果认证不通过会返回一个Socket.EVENT_CONNECT_ERROR事件
//return encodedPassword.equals(managerInfo.getPassword()); return true;
}
}); final SocketIOServer server = new SocketIOServer(config);
System.out.println("注入SocketIOServer");
return server;
} @Bean
public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
return new SpringAnnotationScanner(socketServer);
}
}
 @Component
public class MessageEventHandler { private static final Logger logger = LoggerFactory.getLogger(MessageEventHandler.class); /**
* 服务器socket对象
*/
public static SocketIOServer socketIoServer; /**
* 客户端集合
*/
static ArrayList<UUID> listClient = new ArrayList<>(); /**
* 超时时间
*/
static final int limitSeconds = 60; @Autowired
public LoginService loginService; /**
* 初始化消息事件处理器
*
* @param server 服务器socket对象
*/
@Autowired
public MessageEventHandler(SocketIOServer server) {
logger.info("初始化SOCKET消息事件处理器");
this.socketIoServer = server;
} /**
* 客户端发起连接时触发
*
* @param client 客户端Socket对象信息
*/
@OnConnect
public void onConnect(SocketIOClient client) {
logger.info("客户端{}已连接", client.getSessionId());
listClient.add(client.getSessionId());
} /**
* 客户端断开连接时触发
*
* @param client 客户端Socket对象信息
*/
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
logger.info("客户端{}断开连接", client.getSessionId());
if (listClient.contains(client.getSessionId())) {
listClient.remove(client.getSessionId());
}
} /**
* 客户端发送消息时触发
*
* @param client 客户端Socket对象信息
* @param request AckRequest 回调对象
* @param data 消息信息实体
*/
@OnEvent(value = SocketConstants.SocketEvent.MESSAGE)
public void onEvent(SocketIOClient client, AckRequest request, MessageInfo data) {
System.out.println("发来消息:" + data.getMsgContent());
socketIoServer.getClient(client.getSessionId()).sendEvent("messageevent", "back data");
} /**
* 效验连接事件并存储客户端信息
*
* @param client 客户端Socket对象信息
* @param data 客户端数据
* @param request AckRequest 回调对象
*/
@OnEvent(value = SocketConstants.SocketEvent.HEALTH_CHECK)
public void onEventByHealthCheck(SocketIOClient client, String data, AckRequest request) {
//logger.info("客户端{}效验连接请求", client.getSessionId());
////解析请求数据
//HealthCheckRequest healthCheckRequest = JSON.parseObject(data, HealthCheckRequest.class);
//if (healthCheckRequest != null) {
// //存储客户端信息
// SocketInstance instance = SocketInstance.getSocketInstance();
// System.out.println(data);
// instance.insertSocketClient(healthCheckRequest.getEnCode(), client);
// logger.info("客户端{}效验连接响应:{}", client.getSessionId(), "OK");
// //响应客户端
// request.sendAckData("OK");
//}
} /**
* 登录事件
*
* @param client 客户端Socket对象信息
* @param data 客户端数据
* @param request AckRequest 回调对象
*/
@OnEvent(value = SocketConstants.SocketEvent.LOGIN)
public void onEventByLogin(SocketIOClient client, String data, AckRequest request) {
logger.info("客户端{}登录请求:{}", client.getSessionId(), data);
AppResponseBase appResponseBase = new AppResponseBase(0, "通讯成功");
//业务响应对象
LoginResponse loginResponse = null;
try {
//解析请求数据
LoginRequest loginRequest = JSON.parseObject(data, LoginRequest.class);
if (loginRequest == null) {
throw new AppException(AppResultCode.LoginAnalysis_Fail);
}
//调用登陆接口
loginResponse = loginService.appLogin(loginRequest);
if (loginResponse == null) {
throw new AppException(AppResultCode.LoginCloud_Fail);
}
if (EnumResult.Success.equals(loginResponse.getResultCode())) {
//保存客户端Socket信息
SocketInstance instance = SocketInstance.getSocketInstance();
instance.insertSocketClient(loginRequest.getEnCode(), client);
}
} catch (AppException ex) {
loginResponse = new LoginResponse(ex.getAppResultCode().getCode(), ex.getAppResultCode().getMsg());
} catch (Exception ex) {
loginResponse = new LoginResponse(AppResultCode.Exceptions.getCode(), AppResultCode.Exceptions.getMsg());
ex.printStackTrace();
}
appResponseBase.setRespData(loginResponse);
String result = JSON.toJSONString(appResponseBase);
logger.info("客户端{}登录响应:{}", client.getSessionId(), result);
//响应客户端
request.sendAckData(result);
} /**
* 交易下单事件
* @param callPayRequest 下单请求信息实体
* @return
*/
public static String sendByPayEvent(CallPayRequest callPayRequest) {
String result = "";
//获取客户端信息
SocketInstance instance = SocketInstance.getSocketInstance();
SocketIOClient client = instance.getClientSocket(callPayRequest.getEnCode());
if (client != null) {
//请求报文
String requestParam = JSON.toJSONString(callPayRequest);
//请求下单
client.sendEvent(SocketConstants.SocketEvent.PAY, new AckCallback<String>(String.class) {
@Override
public void onSuccess(String s) {
//响应信息
System.out.println("ack from client: " + client.getSessionId() + " data: " + s.toString());
}
}, requestParam); } else {
//客户端已断开连接 }
return result;
}
}
 @Component
@Order(value = 1)
public class MyCommandLineRunner implements CommandLineRunner { private final SocketIOServer server; @Autowired
public MyCommandLineRunner(SocketIOServer server) {
System.out.println("初始化MyCommandLineRunner");
this.server = server;
} @Override
public void run(String... args) {
try {
server.start();
System.out.println("socket.io启动成功!");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
 public class SocketConstants {

     /**
* Socket事件类
*/
public class SocketEvent { /**
* 效验连接状况
*/
public static final String HEALTH_CHECK = "HEALTH_CHECK"; /**
* 消息接收事件名称
*/
public static final String MESSAGE = "message"; /**
* 登录事件名称
*/
public static final String LOGIN = "LOGIN"; /**
* 获取交易要素事件名称
*/
public static final String QUERY_PAY_FIELDS = "QUERY_PAY_FIELDS"; /**
* 创建订单事件名称
*/
public static final String CREATE_ORDER = "CREATE_ORDER"; /**
* 监控订单状态事件名称
*/
public static final String CHECK_ORDER_STATUS = "CHECK_ORDER_STATUS"; /**
* 获取订单事件名称
*/
public static final String QUERY_ORDER = "QUERY_ORDER"; /**
* 支付事件名称
*/
public static final String PAY = "PAY";
}
}
 public class SocketInstance {

     /**
* 客户端Socket连接对象容器
*/
private static Map<String, SocketIOClient> socketClients = null; /**
* 私有构造
*/
private SocketInstance() {
//从缓存中获取socketClients
socketClients = new HashMap<>();
} /**
* 定义一个私有的内部类,在第一次用这个嵌套类时,会创建一个实例。而类型为SocketInstanceHolder的类,只有在SocketInstance.getSocketInstance()中调用,
* 由于私有的属性,他人无法使用SocketInstanceHolder,不调用SocketInstance.getSocketInstance()就不会创建实例。
* 优点:达到了lazy loading的效果,即按需创建实例。
* 无法适用于分布式集群部署
*/
private static class SocketInstanceHolder {
/**
* 创建全局唯一实例
*/
private final static SocketInstance instance = new SocketInstance();
} /**
* 获取全局唯一实例
*
* @return SocketInstance对象
*/
public static SocketInstance getSocketInstance() {
return SocketInstanceHolder.instance;
} /**
* 新增客户端连接到容器
*
* @param encode 设备En号
* @param socketIOClient 客户端socket对象
*/
public void insertSocketClient(String encode, SocketIOClient socketIOClient) {
SocketIOClient oldSocketIOClient = socketClients.get(encode);
if (oldSocketIOClient != null) {
try {
//关闭客户端连接
oldSocketIOClient.disconnect();
} catch (Exception ex) {
ex.printStackTrace();
}
}
socketClients.put(encode, socketIOClient);
} /**
* 获取客户端Socket对象
*
* @param encode 设备encode
* @return 客户端Socket对象
*/
public SocketIOClient getClientSocket(String encode) {
return socketClients.get(encode);
}
}

Android客户端

 public class SocketClient {

     /**
* 最大重连次数
*/
private int maxReConnectionCount = 5; /**
* 重连次数
*/
private int reConnectionCount = 0; /**
* 等待框对象
*/
private static ProgressDialog progressdialog; /**
* 提示框
*/
private static AlertDialog.Builder dialogExitBuilder; /**
* Toast提示对象
*/
private static Toast toast; /**
* Socket客户端对象信息
*/
public static Socket socket; /**
* 主页面对象,每个页面onCreate时必须设置,可在每个页面监控Socket连接状况
*/
public static Context nowContext; /**
* Socket连接提示handler(等待框)
*/
Handler dialogMessageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String message = bundle.getString(MessageUtil.MESSAGE);
setDialogMessage(message);
}
}; /**
* Socket连接失败退出提示handler(提示框)
*/
Handler dialogExitHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String message = bundle.getString(MessageUtil.MESSAGE);
dialogExit(message);
}
}; /**
* Socket连接提示handler(Toast)
*/
Handler toastMessageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String message = bundle.getString(MessageUtil.MESSAGE);
showToast(message, Toast.LENGTH_SHORT);
}
}; /**
* 等待框
*
* @param message 提示文字
*/
private static void setDialogMessage(String message) {
if (progressdialog == null) {
progressdialog = new ProgressDialog(nowContext);
}
progressdialog.setTitle("学通宝收银");
progressdialog.setMessage(message);
progressdialog.setCancelable(false);
progressdialog.show();
} /**
* 退出提示框
*
* @param message 提示文字
*/
private void dialogExit(String message) {
//初始化退出builder
if (dialogExitBuilder == null) {
dialogExitBuilder = new AlertDialog.Builder(nowContext);
}
dialogExitBuilder.setMessage(message);
dialogExitBuilder.setTitle("提示");
dialogExitBuilder.setIcon(R.mipmap.warning);
dialogExitBuilder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
//参数用作状态码;根据惯例,非 0 的状态码表示异常终止。
System.exit(0);
}
});
dialogExitBuilder.create().show();
} /**
* Toast消息提醒
*
* @param text 标题
* @param duration 时长
*/
public void showToast(String text, int duration) {
//只创建一次
if (toast == null) {
toast = Toast.makeText(nowContext, text, duration);
} else {
toast.setText(text);
toast.setDuration(duration);
}
toast.show();
} public void startSocket() throws URISyntaxException {
//初始化Socket配置
IO.Options options = new IO.Options();
options.transports = new String[]{"websocket"};
options.reconnectionAttempts = maxReConnectionCount; // 设置一个重连的最大尝试次数,超过这个值后Socket.io会使用所有允许的其他连接方式尝试重连,直到最终失败。
options.reconnectionDelay = 500; //为Socket.io的重连设置一个时间间隔,内部会在多次重连尝试时采用该值的指数值间隔,用来避免性能损耗(500 > 1000 > 2000 > 4000 > 8000)
options.reconnection = true; //当连接终止后,是否允许Socket.io自动进行重连
options.timeout = 9000; //连接超时时间(ms)
options.forceNew = true;
options.query = "appid=cn.xuetongbao.xtbpay";
socket = IO.socket("http://192.168.8.107:9096/", options);
//连接成功
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
@Override
public void call(Object... args) {
//重连机制
if (reConnectionCount > 0) {
//连接存储客户端信息
DeviceInfoInstance instance = DeviceInfoInstance.getSocketInstance();
HealthCheckRequest healthCheckRequest = new HealthCheckRequest();
healthCheckRequest.setEnCode(instance.getDeviceInfo().getEnCode());
socket.emit(SocketConstants.SocketEvent.HEALTH_CHECK, RequestUtil.createObject(healthCheckRequest), (Ack) args1 -> {
System.out.println("args1:" + args1.toString());
});
}
System.out.println("连接成功...");
toastMessageHandler.sendMessage(MessageUtil.createMessage("服务器连接成功"));
//关闭等待框
if (progressdialog != null) {
progressdialog.dismiss();
}
}
}); //连接失败事件
socket.on(Socket.EVENT_CONNECT_ERROR, new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("Socket.EVENT_CONNECT_ERROR");
System.out.println("reConnectionCount:" + reConnectionCount);
if (reConnectionCount >= maxReConnectionCount) {
dialogExitHandler.sendMessage(MessageUtil.createMessage("服务器连接失败,请稍后再试"));
} else {
dialogMessageHandler.sendMessage(MessageUtil.createMessage("服务器连接失败,正在重新连接..."));
}
}
}); //连接中事件
socket.on(Socket.EVENT_RECONNECTING, new Emitter.Listener() {
@Override
public void call(Object... args) {
reConnectionCount++;
System.out.println("Socket.EVENT_RECONNECTING");
dialogMessageHandler.sendMessage(MessageUtil.createMessage("正在连接服务器..."));
}
}); //连接超时事件
socket.on(Socket.EVENT_CONNECT_TIMEOUT, new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("Socket.EVENT_CONNECT_TIMEOUT");
if (nowContext != null) {
dialogMessageHandler.sendMessage(MessageUtil.createMessage("与服务器连接超时,正在重新建立连接..."));
socket.connect();
}
}
}); //心跳包
socket.on(Socket.EVENT_PING, new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("Socket.EVENT_PING");
}
});
//心跳包
socket.on(Socket.EVENT_PONG, new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("Socket.EVENT_PONG");
}
}); //消息接收事件
socket.on(Socket.EVENT_MESSAGE, new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("-----------接受到消息啦--------" + Arrays.toString(args));
}
}); //连接断开事件
socket.on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
@Override
public void call(Object... args) {
reConnectionCount = 0;
System.out.println("客户端断开连接啦。。。");
if (nowContext != null) {
dialogMessageHandler.sendMessage(MessageUtil.createMessage("似乎与服务器断开连接,正在重新建立连接..."));
socket.connect();
}
}
}); //交易事件
socket.on(SocketConstants.SocketEvent.PAY, new Emitter.Listener() {
@Override
public void call(Object... args) {
Object data = args[0];
Object ackCallBack = args[1];
System.out.println("接收到服务端交易下单消息" + data);
CallPayRequest callPayRequest = JSON.parseObject(data.toString(), CallPayRequest.class);
if (callPayRequest != null) { }
//data
CallPayResponse callPayResponse = new CallPayResponse();
callPayResponse.setResultCode(AppResultCode.Success.getCode());
callPayResponse.setResultMsg(AppResultCode.Success.getMsg()); //响应服务端
((Ack) ackCallBack).call(JSON.toJSONString(callPayResponse));
}
});
System.out.println("准备连接服务器...");
socket.connect();
}
}

 注:仅供学习参考

【Spring Boot】集成Netty Socket.IO通讯框架的更多相关文章

  1. spring boot集成MyBatis 通用Mapper 使用总结

    spring boot集成MyBatis 通用Mapper 使用总结 2019年 参考资料: Spring boot集成 MyBatis 通用Mapper SpringBoot框架之通用mapper插 ...

  2. Spring Boot集成Jasypt安全框架

    Jasypt安全框架提供了Spring的集成,主要是实现 PlaceholderConfigurerSupport类或者其子类. 在Sring 3.1之后,则推荐使用PropertySourcesPl ...

  3. Spring Boot集成Reactor事件处理框架的简单示例

    1. Reactor简介 Reactor 是 Spring 社区发布的基于事件驱动的异步框架,不仅解耦了程序之间的强调用关系,而且有效提升了系统的多线程并发处理能力. 2. Spring Boot集成 ...

  4. Spring Boot 2.X(六):Spring Boot 集成Redis

    Redis 简介 什么是 Redis Redis 是目前使用的非常广泛的免费开源内存数据库,是一个高性能的 key-value 数据库. Redis 与其他 key-value 缓存(如 Memcac ...

  5. Spring boot集成swagger2

    一.Swagger2是什么? Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件. Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格 ...

  6. Spring Boot 集成 Swagger,生成接口文档就这么简单!

    之前的文章介绍了<推荐一款接口 API 设计神器!>,今天栈长给大家介绍下如何与优秀的 Spring Boot 框架进行集成,简直不能太简单. 你所需具备的基础 告诉你,Spring Bo ...

  7. spring boot 集成 zookeeper 搭建微服务架构

    PRC原理 RPC 远程过程调用(Remote Procedure Call) 一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样,通过网络传输去访问远程系统资源,R ...

  8. Quartz与Spring Boot集成使用

    上次自己搭建Quartz已经是几年前的事了,这次项目中需要定时任务,需要支持集群部署,想到比较轻量级的定时任务框架就是Quartz,于是来一波. 版本说明 通过搜索引擎很容易找到其官网,来到Docum ...

  9. Spring Boot集成MyBatis开发Web项目

    1.Maven构建Spring Boot 创建Maven Web工程,引入spring-boot-starter-parent依赖 <project xmlns="http://mav ...

随机推荐

  1. win10+anaconda环境下pyqt5+qt tools+eric6.18安装及汉化过程

    最近需要用python编写一个小程序的界面,选择了pyqt5+eric6的配套组合,安装过程中遇到一些坑,特此记录.参考书籍是电子工业出版社的<PyQt5快速开发与实战>. 因为我使用an ...

  2. CentOS 中查看软件的版本号

    CentOS  中查看软件的版本号 1. rpm 查看 [root@hadoop110 ~]# rpm -qa | grep mysql mysql-community-client--.el6.x8 ...

  3. HTML <form> target 属性

    浏览器支持 所有主流浏览器都支持 target 属性. 定义和用法 target 属性规定一个名称或一个关键词,指示在何处打开 action URL,即在何处显示提交表单后接收到的响应. target ...

  4. 【KMP】【字符串】KMP字符串匹配算法 学习笔记

    一.简介     KMP是由Knuth.Morris和Prat发明的字符串匹配算法,它的时间复杂度是均摊\(O(n+m)\).其实用Hash也可以做到线性,只不过Hash存在极其微小的难以避免的冲突. ...

  5. AngularJS 中ng-model通过$watch动态取值

    这个例子的意思是,当xxxx的长度不超过6时,xxxx和yyyy两个input的model是无关的,但当xxxx超过6,则yyyy会跟随其值而变化. 另一种做法是在input的ng-model后面添加 ...

  6. XFire创建WebService实例应用

    [转自] http://clq9761.iteye.com/blog/1261963 XFire创建WebService实例应用 XFire使得在JavaEE应用中发布Web服务变得轻而易举.和其他W ...

  7. BankNote

    # coding=utf-8 import pandas as pd import numpy as np from sklearn import cross_validation import te ...

  8. 关于表格合并span-method方法的补充(表格数据由后台动态返回)

    之前写了一些关于element-ui表格合并的方法,不过用的数据都是确定的数据(死数据),但是很多时候我们的数据都是通过后台获得的,数据不稳定,这个时候使用表格合并就需要先处理一下数据,先看一下一种很 ...

  9. ActiveMQ的单节点和集群部署

    平安寿险消息队列用的是ActiveMQ. 单节点部署: 下载解压后,直接cd到bin目录,用activemq start命令就可启动activemq服务端了. ActiveMQ默认采用61616端口提 ...

  10. Gradle发布项目到 maven 之novoda/bintray-release(3)

    novoda/bintray-release 使用这个插件上传比较简单,只需要两步就可以 1.在项目根目录下的 build.gradle 添加插件依赖 // Top-level build file ...