@Override
  protected void channelRead0(io.netty.channel.ChannelHandlerContext ctx, PacketsMessage message)
      throws Exception {
    ByteBuf content = message.getContent();
    MainBaseClient client = message.getClient();

    if (log.isTraceEnabled()) {
      log.trace(
          "In message: {} sessionId: {}",
          content.toString(CharsetUtil.UTF_8),
          client.getSessionId());
    }
    while (content.isReadable()) {
      try {
        Packet packet = decoder.decodePackets(content, client.getSessionId());
        Namespace ns = namespacesHub.get(packet.getEndpoint());
        if (ns == null) {
          log.debug(
              "Can't find namespace for endpoint: {}, sessionId: {} probably it was removed.",
              packet.getEndpoint(),
              client.getSessionId());
          return;
        }

        if (packet.getType() == PacketType.CONNECT) {
          client.addChildClient(ns);
        }

        NamespaceClient nClient = (NamespaceClient) client.getChildClient(ns);
        if (nClient == null) {
          log.debug(
              "Can't find namespace client in namespace: {}, sessionId: {} probably it was disconnected.",
              ns.getName(),
              client.getSessionId());
          return;
        }
        packetListener.onPacket(packet, nClient);
      } catch (Exception ex) {
        String c = content.toString(CharsetUtil.UTF_8);
        log.error(
            "Error during data processing. Client sessionId: "
                + client.getSessionId()
                + ", data: "
                + c,
            ex);
        return;
      }
    }
  }
  @Override
  @SuppressWarnings("unchecked")
  public void addListener(
      Namespace namespace, final Object object, final Method method, Annotation annot) {
    OnEvent annotation = (OnEvent) annot;
    if (annotation.value() == null || annotation.value().trim().length() == 0) {
      throw new IllegalArgumentException("OnEvent \"value\" parameter is required");
    }
    final int socketIOClientIndex = paramIndex(method, SocketIOClient.class);
    final int ackRequestIndex = paramIndex(method, AckRequest.class);
    final List<Integer> dataIndexes = dataIndexes(method);

    if (dataIndexes.size() > 1) {
      List<Class<?>> classes = new ArrayList<Class<?>>();
      for (int index : dataIndexes) {
        Class<?> param = method.getParameterTypes()[index];
        classes.add(param);
      }

      namespace.addMultiTypeEventListener(
          annotation.value(),
          new MultiTypeEventListener() {
            @Override
            public void onData(SocketIOClient client, MultiTypeArgs data, AckRequest ackSender) {
              try {
                Object[] args = new Object[method.getParameterTypes().length];
                if (socketIOClientIndex != -1) {
                  args[socketIOClientIndex] = client;
                }
                if (ackRequestIndex != -1) {
                  args[ackRequestIndex] = ackSender;
                }
                int i = 0;
                for (int index : dataIndexes) {
                  args[index] = data.get(i);
                  i++;
                }
                method.invoke(object, args);
              } catch (InvocationTargetException e) {
                throw new SocketIOException(e.getCause());
              } catch (Exception e) {
                throw new SocketIOException(e);
              }
            }
          },
          classes.toArray(new Class[classes.size()]));
    } else {
      Class objectType = Void.class;
      if (!dataIndexes.isEmpty()) {
        objectType = method.getParameterTypes()[dataIndexes.iterator().next()];
      }

      namespace.addEventListener(
          annotation.value(),
          objectType,
          new DataListener<Object>() {
            @Override
            public void onData(SocketIOClient client, Object data, AckRequest ackSender) {
              try {
                Object[] args = new Object[method.getParameterTypes().length];
                if (socketIOClientIndex != -1) {
                  args[socketIOClientIndex] = client;
                }
                if (ackRequestIndex != -1) {
                  args[ackRequestIndex] = ackSender;
                }
                if (!dataIndexes.isEmpty()) {
                  int dataIndex = dataIndexes.iterator().next();
                  args[dataIndex] = data;
                }
                method.invoke(object, args);
              } catch (InvocationTargetException e) {
                throw new SocketIOException(e.getCause());
              } catch (Exception e) {
                throw new SocketIOException(e);
              }
            }
          });
    }
  }