Skip to content

Latest commit

 

History

History
184 lines (140 loc) · 5.93 KB

56.单体IM基础实战:消息发送器.md

File metadata and controls

184 lines (140 loc) · 5.93 KB

56 单体IM基础实战: 消息发送器

消息发送的基本流程

客户端构建消息 =》 客户端连接 =》 客户端发送消息 =》 服务端解码并处理 =》服务端返回值写入session的channle =》 客户端收到消息并打印

代码设计

Sender基类定义基本功能

这里有两个重要成员,User 和 Session .

之前在第 27节中已经提及过channel如何设计和存储,其中session起到了一个中间人的角色,连接用户和渠道。

基本的发送器定义了功能如下:

  • 判断连接是否正常
  • 判断用户是否登录
  • 发送方法

具体的每个功能的发送器来实现基类的功能,重写相关的方法。

public abstract class BaseSender {
    private User user;
    private ClientSession session;

    public boolean isConnected() {
        if (null == session) {
            log.info("session is null");
            return false;
        }
        return session.isConnected();
    }

    public boolean isLogin() {
        if (null == session) {
            log.info("session is null");
            return false;
        }

        return session.isLogin();
    }

    public void sendMsg(ProtoMsg.Message message) {
        if (null == getSession() || !isConnected()) {
            log.info("连接还没成功");
            return;
        }

        Channel channel = getSession().getChannel();
        ChannelFuture f = channel.writeAndFlush(message);
        f.addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future)
                    throws Exception {
                // 回调
                if (future.isSuccess()) {
                    sendSucced(message);
                } else {
                    sendfailed(message);

                }
            }
        });
    }

以登录为例

登录消息发送器的功能比较简单,其实就是构建用户报文,然后调用基类的send方法。

public class LoginSender extends BaseSender {
    public void sendLoginMsg() {
        if (!isConnected()) {
            log.info("还没有建立连接!");
            return;
        }
        log.info("构造登录消息");

        ProtoMsg.Message message = LoginMsgConverter.build(getUser(), getSession());
        log.info("发送登录消息");
        super.sendMsg(message);
    }
}

功能测试

首先启动服务端8081端口,然后客户端连接,并且发送登录报文

@Test
public void testLoginSender() throws IOException {
    initBootstrap();
    // 设置通道初始化
    b.handler(
            new ChannelInitializer<SocketChannel>() {
                public void initChannel(SocketChannel ch) {
                    ch.pipeline().addLast("decoder", new SimpleProtobufDecoder());
                    ch.pipeline().addLast("encoder", new SimpleProtobufEncoder());
                }
            }
    );
    log.info("测试用例:客户端开始连接 [疯狂创客圈IM]");

    ChannelFuture f = b.connect();//异步发起连接
    f.addListener(connectedListener2);

    try {
        f.channel().closeFuture().sync();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("测试用例执行完成");

    f.channel().close();
}

发送逻辑代码如下

GenericFutureListener<ChannelFuture> connectedListener2 = (ChannelFuture f) ->
    {
        final EventLoop eventLoop = f.channel().eventLoop();
        if (!f.isSuccess()) {
            log.info("连接失败!在10s之后准备尝试重连!");
        } else {
            log.info("测试用例 :疯狂创客圈 IM 服务器 连接成功!");
            channel = f.channel();
            // 创建会话
            ClientSession session = new ClientSession(channel);
            session.setConnected(true);
//            channel.closeFuture().addListener(closeListener);
            startLogin(session);
        }
    };

    private void startLogin(ClientSession session) {
        //登录

        User user = new User();
        user.setUid("1");
        user.setToken(UUID.randomUUID().toString());
        user.setDevId(UUID.randomUUID().toString());

        loginSender.setUser(user);
        loginSender.setSession(session);
        loginSender.sendLoginMsg();
    }

测试结果:

客户端:

2022-04-01 22:02:27.128  INFO 39885 --- [           main] client.TestConnectAndLoginSender         : 测试用例:客户端开始连接 [疯狂创客圈IM]
2022-04-01 22:02:27.368  INFO 39885 --- [ntLoopGroup-2-1] client.TestConnectAndLoginSender         : 测试用例 :疯狂创客圈 IM 服务器 连接成功!
2022-04-01 22:02:27.385  INFO 39885 --- [ntLoopGroup-2-1] c.c.imClient.sender.LoginSender          : 构造登录消息
2022-04-01 22:02:27.446  INFO 39885 --- [ntLoopGroup-2-1] c.c.imClient.sender.LoginSender          : 发送登录消息
SimpleProtobufEncoder:encode0:encoder length=130
2022-04-01 22:02:27.529  INFO 39885 --- [ntLoopGroup-2-1] c.c.imClient.sender.BaseSender           : 发送成功
SimpleProtobufDecoder:decode0:decoder length=60

服务端:

2022-04-01 22:02:27.592  INFO 39873 --- [ppool-2-mixed-1] c.crazymakercircle.im.common.bean.User   LN:58 登录中: User{uid='1', devId='85417830-a3d0-4921-aef7-e778a078a26a', token='ed405b47-f1af-450e-b39e-91400c200a1e', nickName='nickName', platform=WINDOWS}
2022-04-01 22:02:27.621  INFO 39873 --- [ppool-2-mixed-1] c.c.imServer.session.ServerSession       LN:69  ServerSession 绑定会话 /127.0.0.1:54217
2022-04-01 22:02:27.625  INFO 39873 --- [ppool-2-mixed-1] c.c.imServer.session.SessionMap          LN:35 用户登录:id= 1   在线总数: 1
2022-04-01 22:02:27.658  INFO 39873 --- [ppool-2-mixed-1] c.c.i.handler.LoginRequestHandler        LN:73 登录成功:User{uid='1', devId='85417830-a3d0-4921-aef7-e778a078a26a', token='ed405b47-f1af-450e-b39e-91400c200a1e', nickName='nickName', platform=WINDOWS}