文档章节

t-io 入门篇(三)即时消息发送demo学习

卡尔码农
 卡尔码农
发布于 2017/05/11 15:25
字数 1630
阅读 9378
收藏 118
点赞 16
评论 34

前言

     t-io作者在开源其框架的同时还附带了几个demo,如:简单的hello world、im等。接下来这篇博客将会围绕tio-examples-im-simple-client、tio-examples-im-simple-server展开分析和学习。

demo项目结构

    im-simple分成三个maven子项目:

  • tio-examples-im-simple-client就是客户端聊天工具项目
  • tio-examples-im-simple-common保存了一些公用的类型定义和utils,
  • tio-examples-im-simple-server包含了聊天系统的服务端和一套附带了nginx的网页聊天工具。

   整片博客是围绕着网页版聊天工具而写的。

聊天系统

  1. 概述

      首先介绍一下这个聊天系统demo大概交互的逻辑是怎么样子的,如下图:

  • demo中web聊天系统使用了websocket协议来和服务端进行通信。
  • demo中数据交互无论是浏览器端、还是服务端数据传输方式都使用了proto-buf(这个让我眼前一亮,之前只听说过客户端和服务器端采用proto-buf,demo里面竟然使用了js的proto-buf,因为之前接触js这一块不多,第一次看到就给跪了)。

    2.代码分析 

  • 服务端初始化demo:
..
// 定义handler,所有的请求数据全部都由这个handler来处理,decode/encode/handler等等
// 如果您做web开发一定知道dispatcher的概念,这个handler会将数据解码,然后将数据分发给对应的handler处理业务
aioHandler = new ImServerAioHandler();
// listenr 可以在连接上、接收到消息、发送消息后等等回调其内部方法
aioListener = new ImServerAioListener();
// 服务端上下文初始化
serverGroupContext = new ServerGroupContext<>(aioHandler, aioListener);

serverGroupContext.setEncodeCareWithChannelContext(true);		
aioServer = new AioServer<>(serverGroupContext);
aioServer.start(ip, port);
..

从初始化的几句代码中可以看出ImServerAioHandler 是聊天系统中的重中之重,我们来看一下里面的代码

/..
// 握手请求
handlerMap.put(Command.COMMAND_HANDSHAKE_REQ, new HandshakeReqHandler());
// 授权请求
handlerMap.put(Command.COMMAND_AUTH_REQ, new AuthReqHandler());
// 聊天请求
handlerMap.put(Command.COMMAND_CHAT_REQ, new ChatReqHandler());
// 加入群组请求
handlerMap.put(Command.COMMAND_JOIN_GROUP_REQ, new JoinReqHandler());
// 心跳请求
handlerMap.put(Command.COMMAND_HEARTBEAT_REQ, new HeartbeatReqHandler());
// 关闭连接请求
handlerMap.put(Command.COMMAND_CLOSE_REQ, new CloseReqHandler());
/..

/.. 在这里通过解析消息头之后获取下一步调用哪个handler来处理业务
public Object handler(ImPacket packet, ChannelContext<ImSessionContext, ImPacket, Object> channelContext) throws Exception
	{
		Command command = packet.getCommand();
		ImBsHandlerIntf handler = handlerMap.get(command);
		if (handler != null)
		{
			Object obj = handler.handler(packet, channelContext);
			CommandStat.getCount(command).handled.incrementAndGet();
			return obj;
		} else
		{
			CommandStat.getCount(command).handled.incrementAndGet();
			log.warn("找不到对应的命令码[{}]处理类", command);
			return null;
		}

	}

/..

当然,上面的其实都是业务代码,还没有很明显的看到框架代码,接下来我们看看怎样发送一条聊天的消息给一个群组。

  • ChatReqHandler.java是处理发送聊天消息的类:
        // protobuf反序列化消息体         
        ChatReqBody chatReqBody = ChatReqBody.parseFrom(packet.getBody());
        // demo这里写死了一些信息,消息发送者
		Integer fromId = 111;
		String fromNick = "test";
        // 把消息发送给谁
		Integer toId = chatReqBody.getToId();
		String toNick = chatReqBody.getToNick();
		String toGroup = chatReqBody.getGroup();
        // 其实demo的代码这里写的不太好,如果chatReqBody==null,前面就报错了
		if (chatReqBody != null)
		{
            // 构建消息体响应类
			ChatRespBody.Builder builder = ChatRespBody.newBuilder();
			builder.setType(chatReqBody.getType());
			builder.setText(chatReqBody.getText());
			builder.setFromId(fromId);
			builder.setFromNick(fromNick);
			builder.setToId(toId);
			builder.setToNick(toNick);
			builder.setGroup(toGroup);
			builder.setTime(SystemTimer.currentTimeMillis());
            //同样protobuf序列化
			ChatRespBody chatRespBody = builder.build();
			byte[] bodybyte = chatRespBody.toByteArray();
            
            //组建即时聊天响应包
			ImPacket respPacket = new ImPacket();
			respPacket.setCommand(Command.COMMAND_CHAT_RESP);

			respPacket.setBody(bodybyte);
            // 如果是对群组发送,直接调用Aio.sendToGroup即可(框架代码)
			if (Objects.equals(ChatType.CHAT_TYPE_PUBLIC, chatReqBody.getType()))
			{
				Aio.sendToGroup(channelContext.getGroupContext(), toGroup, respPacket);
			} else if (Objects.equals(ChatType.CHAT_TYPE_PRIVATE, chatReqBody.getType()))
			{   // 如果是对单个人发送,也有Aio.sendToUser方法(框架代码)
				if (toId != null)
				{
					Aio.sendToUser(channelContext.getGroupContext(), toId + "", respPacket);
				}
			}
		}

     看看,真正使用到框架的代码实际上就一行:Aio.sendToXXX静态方法。

  • 那也许会有人问,框架是怎么知道这个group里面有哪些连接啊?框架怎么知道会有哪些连接的user呢?

     注意看众多handler中,其中有一个JoinReqHandler是在客户端调用加入群组命令的时候执行的:

public Object handler(ImPacket packet, ChannelContext<ImSessionContext, ImPacket, Object> channelContext) throws Exception
{
// ..
// 连接上下文绑定群组
Aio.bindGroup(channelContext, group);
// ..

}

     一个简单的bindGroup即可把这个连接绑定到一个组里面,从而通过Aio.sendToGroup即可轻松的给一个组发送消息。而在AuthReqHandler中同样调用了Aio.bindUser方法来绑定有用户连接(这个不贴代码了,贴多了界面太长,让人不想看)。

     保存user和group,作者使用的是一个带了读写锁的自己封装的DualHashBidiMap (apache工具包里面的,双向map,可以通过key获取value,可以通过value获取key)。

     总结t-io在聊天系统中的使用方法

  • 创建了一个server端
  • 客户端在鉴权的时候Aio.bindUser
  • 客户端在加入群组的时候Aio.bindGroup
  • 客户端在发送消息时Aio.sendToUser/Aio.sendToGroup即可
  • 当然,只要你实现一下ClientAioHandler的heartbeatPacket方法,框架本身给实现了自动心跳检测,重连也只需要在初始化连接上下文时传入ReconnConf即可。
  • 后面我自己设计config-server的时候,给客户端推送一组服务IP过去也是相当容易的事情啦。
  • 棋牌类游戏交互也类似了,一桌四个人,一个的操作,发送到服务端做算法逻辑校验后,同时推送给其他三个人,好像这样实现起来也不复杂了。而需要关心的仅仅只是server本身的集群问题了。

    demo中我最感兴趣的和我学到的

  • 框架提供的user和group概念很方便
  • ByteBuffer消息头的定义方法和解析,demo里面定义消息头定义得很节省空间
    ByteBuffer buffer = ByteBuffer.allocate(allLen);
    buffer.order(groupContext.getByteOrder());
    //这里比较有迷惑性,这个version并非随便乱定,而是需要根据后面是否使用压缩、序号同步等等标记出来的二进制
    buffer.put(ImPacket.VERSION);
    buffer.put((byte) packet.getCommand().getNumber());
    buffer.put(isCompress ? (byte)1 : (byte)0);
    buffer.putInt(packet.getSynSeq());
    buffer.putShort((short)bodyLen);
    

    这里注意,我差点被作者绕进去了,ImPacket.VERSION比较有迷惑性

    //这是客户端解析第一个字节的定义,不是浏览器解析规则
    //其实作者定义得version自己规定的只占用4个比特位,如:0B00001111,后四位都是用来真正标示版本号
    //实际上前面的几位是另外分出来标记是否压缩,是否同步序号等等
    byte version = ImPacket.decodeVersion(firstbyte);
    boolean isCompress = ImPacket.decodeCompress(firstbyte);
    boolean hasSynSeq = ImPacket.decodeHasSynSeq(firstbyte);
    boolean is4ByteLength = ImPacket.decode4ByteLength(firstbyte);
    

     

  • 消息点对点发送和消息的群组发送性能和稳定性还有待我做做测试,后续在全面了解完t-io框架后准备弄一份关于性能测试的报告出来试试。

     同时感谢t-io作者对我的指导 !

© 著作权归作者所有

共有 人打赏支持
卡尔码农
粉丝 89
博文 14
码字总数 18165
作品 0
常德
其他
加载中

评论(34)

talent-tan
talent-tan

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
if (synSeq != null && synSeq > 0) {
        ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
        Packet initPacket = syns.remove(synSeq);
        if (initPacket != null) {
          synchronized (initPacket) {
            syns.put(synSeq, packet);
            initPacket.notify();
          }
        } else {
          log.error("[{}]同步消息失败, synSeq is {}, 但是同步集合中没有对应key值", synFailCount.incrementAndGet(), synSeq);
        }
      } else {
        channelContext.traceClient(ChannelAction.BEFORE_HANDLER, packet, null);
        groupContext.getAioHandler().handler(packet, channelContext);
        channelContext.traceClient(ChannelAction.AFTER_HANDLER, packet, null);
      }
根据代码来看,只有synSeq 不大于0你这里才调用自定义的AioHandler来处理packet。那么我这里使用了synSeq>0,就没法处理packet,没法给packet 定义synSeq 了啊。。

回复@lblin : 你手工调呀,框架来调显得不灵活可控,所以这个地方如何设计API还要仔细考虑,所以我没有公开宣传这个强悍的功能

引用来自“lblin”的评论

大神,QQ群不让进。
135861388
talent-tan
talent-tan

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid

引用来自“lblin”的评论

另外
代码中
ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
Packet initPacket = syns.remove(synSeq);
我发送消息是客户端的context
接收消息是服务端的context
此时channelContext.getGroupContext().getWaitingResps() 肯定是个空。syns.remove(synSeq); 取不到任何内容啊,我怎么看都觉得这里好像是将channelContext 当做服务端和客户端共享的一个类来使用了,客户端发消息的时候put synSeq到syns里,然后服务端remove 获取,如果是单机没毛病,可这不是服务端和客户端2个应用么,context都不通用啊
你再仔细看,这个功能只是API我还没想好一个更优雅的,功能早就测试过了:smile:
l
lblin

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
if (synSeq != null && synSeq > 0) {
        ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
        Packet initPacket = syns.remove(synSeq);
        if (initPacket != null) {
          synchronized (initPacket) {
            syns.put(synSeq, packet);
            initPacket.notify();
          }
        } else {
          log.error("[{}]同步消息失败, synSeq is {}, 但是同步集合中没有对应key值", synFailCount.incrementAndGet(), synSeq);
        }
      } else {
        channelContext.traceClient(ChannelAction.BEFORE_HANDLER, packet, null);
        groupContext.getAioHandler().handler(packet, channelContext);
        channelContext.traceClient(ChannelAction.AFTER_HANDLER, packet, null);
      }
根据代码来看,只有synSeq 不大于0你这里才调用自定义的AioHandler来处理packet。那么我这里使用了synSeq>0,就没法处理packet,没法给packet 定义synSeq 了啊。。

回复@lblin : 进群讨论吧,红薯要说我们刷屏了
public static Packet synSend(ChannelContext channelContext, Packet packet, long timeout) {
    Integer synSeq = packet.getSynSeq();
    if (synSeq == null || synSeq <= 0) {
      throw new RuntimeException("synSeq必须大于0");
    }

    ChannelContextMapWithLock waitingResps = channelContext.getGroupContext().getWaitingResps();
    try {
      waitingResps.put(synSeq, packet);

      synchronized (packet) {
        send(channelContext, packet);
        try {
          packet.wait(timeout);
        } catch (InterruptedException e) {
          log.error(e.toString(), e);
        }
      }
    } catch (Exception e) {
      log.error(e.toString(), e);
    }

这里的packet 加锁后休眠 ,在哪里唤醒呢。 加锁是客户端请求服务端时候wait,但是我看代码。好想是服务端notify的:cold_sweat:
l
lblin

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
if (synSeq != null && synSeq > 0) {
        ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
        Packet initPacket = syns.remove(synSeq);
        if (initPacket != null) {
          synchronized (initPacket) {
            syns.put(synSeq, packet);
            initPacket.notify();
          }
        } else {
          log.error("[{}]同步消息失败, synSeq is {}, 但是同步集合中没有对应key值", synFailCount.incrementAndGet(), synSeq);
        }
      } else {
        channelContext.traceClient(ChannelAction.BEFORE_HANDLER, packet, null);
        groupContext.getAioHandler().handler(packet, channelContext);
        channelContext.traceClient(ChannelAction.AFTER_HANDLER, packet, null);
      }
根据代码来看,只有synSeq 不大于0你这里才调用自定义的AioHandler来处理packet。那么我这里使用了synSeq>0,就没法处理packet,没法给packet 定义synSeq 了啊。。

回复@lblin : 你手工调呀,框架来调显得不灵活可控,所以这个地方如何设计API还要仔细考虑,所以我没有公开宣传这个强悍的功能
大神,QQ群不让进。
l
lblin

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
if (synSeq != null && synSeq > 0) {
        ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
        Packet initPacket = syns.remove(synSeq);
        if (initPacket != null) {
          synchronized (initPacket) {
            syns.put(synSeq, packet);
            initPacket.notify();
          }
        } else {
          log.error("[{}]同步消息失败, synSeq is {}, 但是同步集合中没有对应key值", synFailCount.incrementAndGet(), synSeq);
        }
      } else {
        channelContext.traceClient(ChannelAction.BEFORE_HANDLER, packet, null);
        groupContext.getAioHandler().handler(packet, channelContext);
        channelContext.traceClient(ChannelAction.AFTER_HANDLER, packet, null);
      }
根据代码来看,只有synSeq 不大于0你这里才调用自定义的AioHandler来处理packet。那么我这里使用了synSeq>0,就没法处理packet,没法给packet 定义synSeq 了啊。。

回复@lblin : 你手工调呀,框架来调显得不灵活可控,所以这个地方如何设计API还要仔细考虑,所以我没有公开宣传这个强悍的功能
好吧。谢谢
l
lblin

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
另外
代码中
ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
Packet initPacket = syns.remove(synSeq);
我发送消息是客户端的context
接收消息是服务端的context
此时channelContext.getGroupContext().getWaitingResps() 肯定是个空。syns.remove(synSeq); 取不到任何内容啊,我怎么看都觉得这里好像是将channelContext 当做服务端和客户端共享的一个类来使用了,客户端发消息的时候put synSeq到syns里,然后服务端remove 获取,如果是单机没毛病,可这不是服务端和客户端2个应用么,context都不通用啊
talent-tan
talent-tan

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
if (synSeq != null && synSeq > 0) {
        ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
        Packet initPacket = syns.remove(synSeq);
        if (initPacket != null) {
          synchronized (initPacket) {
            syns.put(synSeq, packet);
            initPacket.notify();
          }
        } else {
          log.error("[{}]同步消息失败, synSeq is {}, 但是同步集合中没有对应key值", synFailCount.incrementAndGet(), synSeq);
        }
      } else {
        channelContext.traceClient(ChannelAction.BEFORE_HANDLER, packet, null);
        groupContext.getAioHandler().handler(packet, channelContext);
        channelContext.traceClient(ChannelAction.AFTER_HANDLER, packet, null);
      }
根据代码来看,只有synSeq 不大于0你这里才调用自定义的AioHandler来处理packet。那么我这里使用了synSeq>0,就没法处理packet,没法给packet 定义synSeq 了啊。。

回复@lblin : 你手工调呀,框架来调显得不灵活可控,所以这个地方如何设计API还要仔细考虑,所以我没有公开宣传这个强悍的功能
talent-tan
talent-tan

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
if (synSeq != null && synSeq > 0) {
        ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
        Packet initPacket = syns.remove(synSeq);
        if (initPacket != null) {
          synchronized (initPacket) {
            syns.put(synSeq, packet);
            initPacket.notify();
          }
        } else {
          log.error("[{}]同步消息失败, synSeq is {}, 但是同步集合中没有对应key值", synFailCount.incrementAndGet(), synSeq);
        }
      } else {
        channelContext.traceClient(ChannelAction.BEFORE_HANDLER, packet, null);
        groupContext.getAioHandler().handler(packet, channelContext);
        channelContext.traceClient(ChannelAction.AFTER_HANDLER, packet, null);
      }
根据代码来看,只有synSeq 不大于0你这里才调用自定义的AioHandler来处理packet。那么我这里使用了synSeq>0,就没法处理packet,没法给packet 定义synSeq 了啊。。

回复@lblin : 进群讨论吧,红薯要说我们刷屏了
l
lblin

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
if (synSeq != null && synSeq > 0) {
        ChannelContextMapWithLock syns = channelContext.getGroupContext().getWaitingResps();
        Packet initPacket = syns.remove(synSeq);
        if (initPacket != null) {
          synchronized (initPacket) {
            syns.put(synSeq, packet);
            initPacket.notify();
          }
        } else {
          log.error("[{}]同步消息失败, synSeq is {}, 但是同步集合中没有对应key值", synFailCount.incrementAndGet(), synSeq);
        }
      } else {
        channelContext.traceClient(ChannelAction.BEFORE_HANDLER, packet, null);
        groupContext.getAioHandler().handler(packet, channelContext);
        channelContext.traceClient(ChannelAction.AFTER_HANDLER, packet, null);
      }
根据代码来看,只有synSeq 不大于0你这里才调用自定义的AioHandler来处理packet。那么我这里使用了synSeq>0,就没法处理packet,没法给packet 定义synSeq 了啊。。
l
lblin

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

引用来自“lblin”的评论

引用来自“talent-tan”的评论

写得超级棒,那个协议的第一个字节是略复杂,主要是为了节约带宽,所以我后面出了个im-simple版,就是简化了这个协议,因为很多人不会bit操作,所以也就看不懂这个协议的第一个字节。。。。博主这个都看明白了,玩t-io肯定会非常顺手!!!@天蓬小猪 @小徐同学 你们一定要关注这位博主。
大神,我想知道调用Aio类的send发送消息后,怎么串行获取消息的回应?

回复@lblin : Aio.synSend() 不过这个方法还没有相关文档,目前估计也就只有我会用
出现异常1同步消息失败, synSeq is 1, 但是同步集合中没有对应key值。不知道为何

用法不对,需要设置seqid,否则框架没办法知道业务消息如何对应,而且回的消息中也要加上同样的seqid
想请问下,是哪一段代码调用AioHandler 的handler方法,我发现这个方法可能是被代理类执行的,没找到触发执行的位置。
NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战

前言 本文将演示一个iOS客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo。服务端将分别用MINA2和Netty4进行实现,而通信时服务端你只需选其一就行了。同时...

JackJiang- ⋅ 2016/06/28 ⋅ 0

Android与MINA2、Netty4的跨平台UDP双向通信实战

概述 本文演示的是一个Android客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo。 当前由于NIO框架的流行,使得开发大并发、高性能的互联网服务端成为可能。...

JackJiang- ⋅ 2016/06/30 ⋅ 1

移动开发之微信小程序——资料集合

本文转载自:知乎 有需要下载的客官可可以点击知乎去下载相关资料 一:官方地址集合: 1:官方工具:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=1476434678461 2:...

DJY1992 ⋅ 2016/12/21 ⋅ 0

网络编程懒人入门(五):快速理解为什么说UDP有时比TCP更有优势

本文观点仅作参考,请根据自已系统的应用场景合理地选择数据传输层协议即可,无需盲目崇拜大牛言论。 1、前言 对于即时通讯开者新手来说,在开始着手编写IM或消息推送系统的代码前,最头疼的...

JackJiang2011 ⋅ 2017/12/19 ⋅ 0

NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

前言 NIO框架的流行,使得开发大并发、高性能的互联网服务端成为可能。这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2、而Netty的主要版本是Netty3和Netty4(Netty5已经被...

JackJiang- ⋅ 2016/06/24 ⋅ 0

Netty干货分享:京东京麦的生产级TCP网关技术实践总结

1、引言 京东的京麦商家后台2014年构建网关,从HTTP网关发展到TCP网关。在2016年重构完成基于Netty4.x+Protobuf3.x实现对接PC和App上下行通信的高可用、高性能、高稳定的TCP长连接网关。 早期...

JackJiang2011 ⋅ 2017/12/01 ⋅ 0

成才 '中©/Wei.IM2A

#Wei.IM2A 即时通信框架客户端 Android 版。本实现是基于可靠的 TCP Socket 连接,通过自定义简单协议(约定)与服务端通信。本实现架构合理,并且有一个非常高效的“有效消息实体字节流碎片...

成才 '中© ⋅ 2014/12/10 ⋅ 0

IM消息送达保证机制实现(二):保证离线消息的可靠投递

1、前言 本文的上篇《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》中,我们讨论了在线实时消息的投递可以通过应用层的确认、发送方的超时重传、接收方的去重等手段来保证业务层...

JackJiang- ⋅ 2016/11/18 ⋅ 0

react学习资源汇总

react-tutorial a tutorial react collection and sort,let you easily get started and organized 主要是搜集整理生态从入门到深入的教程、工具和自己做的demo,以便日后查阅 :blush: 设计思...

逆蝶_Snow ⋅ 2016/12/01 ⋅ 0

Netty5入门学习笔记001

Netty官网:http://netty.io/ 本例程使用最新的netty5.x版本编写 服务器端: TimeServer 时间服务器 服务端接收客户端的连接请求和查询当前时间的指令,判断指令正确后响应返回当前服务器的校...

山东小木 ⋅ 2014/12/17 ⋅ 10

没有更多内容

加载失败,请刷新页面

加载更多

下一页

中标麒麟(龙芯版)7.0优盘安装

########################################## 制作U盘安装盘: 1.准备U盘: PMON环境下U盘必须格式化成ext3; 昆仑固件环境下可以格式化成ext3,ext4 2.把整个镜像 xxx.iso 复制到U盘下面 3....

gugudu ⋅ 21分钟前 ⋅ 0

老司机写的大数据建模五步走

本文将尝试来梳理一下数据建模的步骤,以及每一步需要做的工作。 01 第一步:选择模型或自定义模式 这是建模的第一步,我们需要基于业务问题,来决定可以选择哪些可用的模型。 比如,如果要预...

gulf ⋅ 30分钟前 ⋅ 0

PacificA 一致性协议解读

PacificA 的 paper 在 08 年左右发出来的,比 Raft 早了 6,7 年。 在 PacificA 论文中,他们强调该算法使用范围是 LAN (Local Area Network),讲白了就是对跨机房不友好。 不管是 ZAB,Raf...

黑客画家 ⋅ 32分钟前 ⋅ 0

盘符图标个性化

设置自己的专属盘符图标 准备ico格式的图片文件一个,在根目录下创建autorun.inf文件 文件内容 [Autorun]icon=logo.ico 重新启动或者插拔U盘即可看到结果...

阿豪boy ⋅ 33分钟前 ⋅ 0

Windows下QQ聊天记录中图片的默认存放位置

Windows下QQ聊天记录中图片的默认存放位置在设置中是没有说明的。 实测位置在:D:\Documents\Tencent Files\974101467\Image 其中: “974101467”为对应的QQ号; “C2C”为个人之间的聊天图...

临江仙卜算子 ⋅ 39分钟前 ⋅ 0

GC 的三种基本实现方式

参考资料《代码的未来》(作者: [日] 松本行弘)。 由于并非本人原著(我只是个“搬运工“),SO 未经本人允许请尽情转载。 另外个人像说明一下这里所说的GC指泛指垃圾回收机制,而单指Jav...

xixingzhe ⋅ 40分钟前 ⋅ 0

Android双击退出

/** * 菜单、返回键响应 */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // TODO Auto-generated method stub if(keyCode......

王先森oO ⋅ 44分钟前 ⋅ 0

idea 整合 vue 启动

刚学习Vue 搭建了一个项目 只能命令启动 Idea里面不会启动 尝试了一下修改启动的配置 如下: 1.首先你要保证你的package.json没有修改过 具体原因没有看 因为我改了这个name的值 就没办法启动...

事儿爹 ⋅ 50分钟前 ⋅ 0

redis在windows环境的后台运行方法

在后台运行,首先需要安装redis服务,命令为 redis-server.exe --service-install redis.windows.conf --loglevel verbose 启动,命令为 redis-server --service-start 停止,命令为 redis-...

程序羊 ⋅ 53分钟前 ⋅ 0

比特币现金开发者提出新的交易订单规则

本周,四位比特币现金的四位开发者和研究员:Joannes Vermorel(Lokad),AmaurySéchet(比特币ABC),Shammah Chancellor(比特币ABC)和Tomas van der Wansem(Bitcrust)共同发表了一篇关...

lpy411 ⋅ 57分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部