资源共享
多端口资源共享
需求场景
- 场景 1: 端口 5555 只接受 iOS 的请求。
- 场景 2: 端口 6666 只接受安卓的请求。
- 共同点: 无论是 iOS 还是安卓,应用层协议的格式是相同的。
问题
如果一条消息需要发送给同一个群组的用户,而该群组中可能既有安卓用户也有 iOS 用户,如何高效地发送消息给所有用户?
常规解决办法
通常的做法是先为每个设备类型绑定群组,然后分别发送消息:
// 第一步:先进行群组绑定
Tio.bindGroup(channelContext_IOS, group_A);
Tio.bindGroup(channelContext_Android, group_A);
// 第二步:分别发送
Tio.sendToGroup(tioConfig_IOS, group_A, packet_1);
Tio.sendToGroup(tioConfig_Android, group_A, packet_1);
虽然这种方法可行,但代码显得冗长,容易出错。如果有 3 个或 4 个端口,这种方式会变得更加繁琐。
利用 t-io 的资源共享能力
t-io 提供了资源共享的能力,能够简化多端口资源共享的实现。通过共享 TioConfig
,我们可以避免多次绑定和发送操作。
实现步骤
系统启动时初始化共享配置:
在系统启动时,调用共享函数进行配置:
tioConfig_IOS.share(tioConfig_Android);
绑定群组:
选择任一
ChannelContext
进行群组绑定:Tio.bindGroup(channelContext_IOS, group_A); // 或者 Tio.bindGroup(channelContext_Android, group_A);
发送消息:
使用共享的
TioConfig
进行消息发送:Tio.sendToGroup(tioConfig_IOS, group_A, packet_1); // 或者 Tio.sendToGroup(tioConfig_Android, group_A, packet_1);
总结
通过 t-io 的资源共享功能,可以大大简化多端口的消息发送操作,无需为每个端口单独执行绑定和发送步骤。这个方法让代码更简洁、更易维护,提高了系统的灵活性。
实现 App 客户端(Socket)和 Web 端(WebSocket)消息互通
前言
在实际业务场景中,特别是即时通讯(IM)应用中,通常存在多个客户端,如手机端、PC 端和 Web 端同时登录。这就产生了一个问题:WebSocket 端口和 Socket 端口不一致,如何让它们之间实现消息互通?
解决思路
初始的解决思路是获取 ServerTioConfig
,然后通过它发送消息。以下是一个简单的示例:
// 通过 HTTP 接口获取 WebSocketStarter 的 ServerTioConfig 来发送消息
@Request(HttpMethod.GET, value="/send")
public void sendByHttp(String to, String msg) {
ServerTioConfig context = TioWsServer.starter.getServerTioConfig();
Tio.sendToUser(context, to, Packet(msg));
}
但是,t-io 已经提供了更简便的方法:ServerTioConfig.share()
,可以轻松实现多端口间的资源共享。
利用 t-io 的 ServerTioConfig.share() 方法
share()
方法的定义如下,它将一个 TioConfig
对象中的资源共享到另一个 TioConfig
对象中,实现不同端口间的资源互通:
public void share(TioConfig tioConfig) {
this.clientNodes = tioConfig.clientNodes;
this.connections = tioConfig.connections;
this.groups = tioConfig.groups;
this.users = tioConfig.users;
this.tokens = tioConfig.tokens;
this.ids = tioConfig.ids;
this.bsIds = tioConfig.bsIds;
this.ipBlacklist = tioConfig.ipBlacklist;
this.ips = tioConfig.ips;
}
DEMO 演示
定义一个简单的 ImPacket
:
public class ImPacket extends Packet {
public static ImPacket newPacket(byte[] body) {
ImPacket packet = new ImPacket();
packet.setBody(body);
return packet;
}
public void setBody(byte[] body) {
this.body = body;
}
public byte[] getBody() {
return body;
}
private byte[] body;
}
编解码
客户端和服务端的编解码过程是一样的:
public static Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
// 四字节的 bodyLength + body
int bodyLength = buffer.getInt();
if (bodyLength <= 0) {
return null;
}
// 长度不够解包,返回 null
if (buffer.remaining() < bodyLength) {
return null;
}
byte[] body = new byte[bodyLength];
buffer.get(body);
return ImPacket.newPacket(body);
}
public static ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
ImPacket packet1 = (ImPacket) packet;
int bodyLength = packet1.getBody().length;
ByteBuffer buffer = ByteBuffer.allocate(4 + bodyLength);
buffer.putInt(bodyLength);
buffer.put(packet1.getBody());
return buffer;
}
Socket Server 端
public class TioSocketServer {
private ServerTioConfig serverTioConfig;
public ServerTioConfig getServerTioConfig() {
return serverTioConfig;
}
public void start() throws IOException {
serverTioConfig = new ServerTioConfig("Tio Socket Server", new ImSocketServerTioHandler(), new ImSocketServerTioListener(), Threads.getTioExecutor(), Threads.getGroupExecutor());
TioServer server = new TioServer(serverTioConfig);
server.start("127.0.0.1", 8899);
}
}
Socket Client 端
private static void start() throws Exception {
ClientTioConfig clientTioConfig = new ClientTioConfig(new ImSocketClientTioHandler(), new DefaultClientAioListerner(), new ReconnConf(2000), Threads.getTioExecutor(), Threads.getGroupExecutor());
TioClient client = new TioClient(clientTioConfig);
clientChannelContext = client.connect(new Node("127.0.0.1", 8899));
}
WebSocket Server 端
private ServerTioConfig sharedServerTioConfig;
// 这里将 SocketServer 的 ServerTioConfig 传入
public TioWsServer(ServerTioConfig tioConfig) {
this.sharedServerTioConfig = tioConfig;
}
private ServerTioConfig wsServerTioConfig;
public void start() throws Exception {
WsServerConfig config = new WsServerConfig(9000);
WsServerStarter wsServerStarter = new WsServerStarter(config, new ImWsHandler(), Threads.getTioExecutor(), Threads.getGroupExecutor());
wsServerTioConfig = wsServerStarter.getServerTioConfig();
// 核心代码:共享 TioConfig
wsServerTioConfig.share(sharedServerTioConfig);
// 协议转换器,将 ImPacket 转换为 WsPacket 包
wsServerTioConfig.packetConverter = new ImPackageConvert();
wsServerStarter.start();
}
PacketConverter 代码
public class ImPackageConvert implements PacketConverter {
@Override
public Packet convert(Packet packet, ChannelContext channelContext) {
if (packet instanceof ImPacket) {
ImPacket p = (ImPacket) packet;
WsResponse wsResponse = new WsResponse();
wsResponse.setWsOpcode(Opcode.TEXT);
wsResponse.setBody(p.getBody());
try {
wsResponse.setWsBodyText(new String(p.getBody(), "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return wsResponse;
}
return packet;
}
}
发送消息
在发送消息时,使用 ServerTioConfig
发送即可。ServerTioConfig
可以是 SocketServer.getServerTioConfig()
,也可以是 WebSocketServer.getServerTioConfig()
:
Tio.sendToUser(serverTioConfig, "userId", packet);
运行效果
启动两个服务端(Socket 和 WebSocket),一个客户端和一个浏览器。将用户绑定为 SOCKET-1
和 WEBSOCKET-1
,即可实现 App 客户端与 Web 端消息的互通。