服务端

 @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. Sql Server两个数据库中有一张表的结构一样,怎么快速将一张表中的数据复制到另一个表中

    1,下面这句会把表2数据删除,然后把表1复制到表一,两表内容一样 SELECT * into 表2 FROM 表1 2,这句只追加,不删除表2的数据 insert into 表1 select * f ...

  2. LongAdder源码学习

    原文链接:https://blog.csdn.net/u011392897/article/details/60480108 LongAdder是jdk8新增的用于并发环境的计数器,目的是为了在高并发 ...

  3. P2645 斯诺克 题解

    P2645 斯诺克 题目背景 镇海中学开设了很多校本选修课程,有体育类.音乐类.美术类.无线电测向.航空航海航天模型制作等,力争使每位学生高中毕业后,能学到一门拿得出手的兴趣爱好,为将来的终身发展打下 ...

  4. Codeforces Round #549 (Div. 2) Solution

    传送门 A.The Doors 看懂题目就会写的题 给一个 $01$ 序列,找到最早的位置使得 $0$ 或 $1$ 已经全部出现 #include<iostream> #include&l ...

  5. dataTable 加了竖向滚动条导致列头样式错位的问题 / 亲测可用,不好用你打我,用好了记得点推荐

    tab在没有显示之前,容器是没有高度宽度的,而dt在自动计算高度和宽度时是获取的外部容器的高度和宽度,当切换tab时,dt获取不到这个高度宽度,导致列头都挤在一起,是用下面代码解决此问题 $('a[d ...

  6. HDU 1232 (畅通工程) 并查集经典模板题

    Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都可以实现交通 ...

  7. Linux(1)-CentOS7下解决ifconfig command not found

    第一步: 输入ip addr 确认IP地址是否设置正常,设置好如下所示,如果没有获取到IP地址则设置一个即可. 第二步 确认sbin目录是否存在. cd /sbin 第三步 确认ifconfig命令是 ...

  8. Apache Shiro(二)-登录认证和权限管理数据库操作

    数据库支持 在上一篇中使用ini 配置文件进行了相关权限数据的配置. 但是实际工作中,我们都会把权限相关的内容放在数据库里. 所以本知识点讲解如何放在数据库里来撸. RBAC 概念 RBAC 是当下权 ...

  9. [转] Mysql命令基础

    [From] http://c.biancheng.net/cpp/u/mysql_ml/ 连接Mysql数据库 mysql命令格式: mysql -h主机地址 -u用户名 -p用户密码 1) 连接到 ...

  10. [转] 你并不需要Underscore/Lodash

    [From] https://segmentfault.com/a/1190000004460234 Lodash 和 Underscore 是非常优秀的当代JavaScript的工具集合框架,它们被 ...