Android 长连接Netty

Posted 吹着空调哼着歌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 长连接Netty相关的知识,希望对你有一定的参考价值。

netty

准备工作

依赖:

			implementation 'io.netty:netty-all:4.1.36.Final'
            compileOnly "org.projectlombok:lombok:1.16.18"
            implementation 'org.glassfish:javax.annotation:10.0-b28'

导入jar包:
libs–>fastjson-1.2.49.jar
*下方有demo下载获取

netty

ChatProto自定义消息格式与消息文体

/**
 * Created by ICE on 2021年1月11日.
 */
public class ChatProto 

    //header
    public static final int PING_PROTO  = 1 << 8 | 220; //ping消息
    public static final int PONG_PROTO  = 2 << 8 | 220; //pong消息
    public static final int SYS_PROTO   = 3 << 8 | 220; //系统消息
    public static final int ERROR_PROTO = 4 << 8 | 220; //错误消息
    public static final int AUTH_PROTO  = 5 << 8 | 220; //认证消息
    public static final int MSG_PROTO   = 6 << 8 | 220; //普通消息

    public int getHead() 
        return head;
    

    public void setHead(int head) 
        this.head = head;
    

    public String getBody() 
        return timestamp;
    

    public void setBody(String timestamp) 
        this.timestamp = timestamp;
    

    public Map<String, Object> getExtend() 
        return extend;
    

    public void setExtend(Map<String, Object> extend) 
        this.extend = extend;
    

    private int head;
    private String timestamp;
    private Map<String, Object> extend;

    public ChatProto(int head, String timestamp) 
        this.head = head;
        this.timestamp = timestamp;
        this.extend = new HashMap<>();
    

    public static MessageProtocol buildPingProto() 
//        Log.i("TAG","ping");
        return buildProto(PING_PROTO);
    

    public static MessageProtocol buildMsgProto(@NonNull Integer code) 
        ChatProto chatProto = new ChatProto(MSG_PROTO, null);
        Log.i("TAG", JSONObject.toJSONString(chatProto)+"");
        return buildProto(MSG_PROTO, code, null);
    

    public static MessageProtocol buildPongProto() 
        return buildProto(PONG_PROTO);
    

    public static MessageProtocol buildSysProto(int code, @NonNull String msg) 
        return buildProto(SYS_PROTO, code, msg);
    

    public static MessageProtocol buildErrProto(int code, @NonNull String msg) 
        return buildProto(ERROR_PROTO, code, msg);
    





    public static MessageProtocol buildMsgProto(@NonNull Integer code, @NonNull Long apkId) 
        ChatProto chatProto = new ChatProto(MSG_PROTO, null);
        chatProto.extend.put("code", code);
        chatProto.extend.put("apkId", apkId);
        chatProto.extend.put("time", System.currentTimeMillis());
        return new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
    

    public static MessageProtocol buildMsgProto(@NonNull Integer code, @Nullable Integer count) 
        ChatProto chatProto = new ChatProto(MSG_PROTO, null);
        chatProto.extend.put("code", code);
        if (null != count) 
            chatProto.extend.put("count", count);
        
        chatProto.extend.put("time", System.currentTimeMillis());
        return new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
    

    public static MessageProtocol buildAuthProto(@Nullable @NonNull String token, @Nullable @NonNull Long giftId, Object o) 
        ChatProto chatProto = new ChatProto(AUTH_PROTO, null);
//        if (null != token) 
//            chatProto.extend.put("nickName", token);
//        
//        if (null != giftId) 
//            chatProto.extend.put("giftId", giftId);
//        
//        chatProto.extend.put("time", System.currentTimeMillis());
        chatProto.extend.put("发送数据","发送数据");

        return new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
    

    public static MessageProtocol buildMsgProto(@Nullable Long giftId, @NonNull String nickName, @Nullable @NonNull Integer level, String msg) 
        ChatProto chatProto = new ChatProto(MSG_PROTO, null);
        if (null != level) 
            chatProto.extend.put("level", level);
        
        if (!nickName.isEmpty()) 
            chatProto.extend.put("nickName", nickName);
        
        if (null != giftId) 
            chatProto.extend.put("giftId", giftId);
        
        chatProto.extend.put("time", System.currentTimeMillis());
        return new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
    


    public static MessageProtocol buildMsgProto(int code, @NonNull String nickName, @Nullable Integer level, @Nullable Long giftId, @Nullable Integer count, String msg) 
        ChatProto chatProto = new ChatProto(MSG_PROTO, msg);
        chatProto.extend.put("code", code);
        if (!nickName.isEmpty()) 
            chatProto.extend.put("nickName", nickName);
        
        if (null != level) 
            chatProto.extend.put("level", level);
        
        if (null != giftId) 
            chatProto.extend.put("giftId", giftId);
        
        if (null != count) 
            chatProto.extend.put("count", count);
        
        chatProto.extend.put("time", System.currentTimeMillis());
        return new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
    

    private static MessageProtocol buildProto(int head) 
        ChatProto chatProto = new ChatProto(head, null);
        Log.i("TAG","发送数据-------JSONObject    "+ JSONObject.toJSONString(chatProto));
        return new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
    

    @NonNull
    private static MessageProtocol buildProto(int head, int code, @NonNull String msg) 
        ChatProto chatProto = new ChatProto(head, null);
        chatProto.extend.put("code", code);
        chatProto.extend.put("msg", msg);
        String bytes = JSONObject.toJSONString(chatProto);
        MessageProtocol messageProtocol = new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
        Log.i("TAG","发送数据-------    "+messageProtocol);
        return new MessageProtocol(JSONObject.toJSONString(chatProto).getBytes());
    

    public static void main(String[] args)
        ChatProto chatProto = new ChatProto(AUTH_PROTO, "哈喽,world!!!");
        chatProto.extend.put("userId", 2L);
        chatProto.extend.put("userName", "n_7777777");
        chatProto.extend.put("level", 1);
        chatProto.extend.put("time", System.currentTimeMillis());
        String message = JSONObject.toJSONString(chatProto);


        System.out.println(">>>>>" + message);
        System.out.println("======" + Unpooled.copiedBuffer(message.getBytes()).toString(CharsetUtil.UTF_8));

        ChatProto chatProto1 = new ChatProto(PING_PROTO, null);

        String message1 = JSONObject.toJSONString(chatProto1);
        System.out.println(">>>>>" + message1);

        try 
            String enStr = URLEncoder.encode(message, "UTF-8");
            System.out.println(">>>>>" + enStr);

            String enStr1 = URLEncoder.encode(message1, "UTF-8");
            System.out.println(">>>>>" + enStr1);
        catch (Exception e) 
            e.printStackTrace();
        

    



MessageEncoder MessageDecoder 消息加密处理类


/**
 * Created by ICE on 2021年1月11日.
 */
public class MessageDecoder extends ByteToMessageDecoder 

    /**
     * <pre>
     * 协议开始的标准head_data,int类型,占据4个字节.
     * 表示数据的长度contentLength,int类型,占据4个字节.
     * </pre>
     */
    public static final int BASE_LENGTH = 4 + 4;

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, @NonNull ByteBuf byteBuf, @NonNull List<Object> list) throws Exception 
        //可读长度大于基本长度
        if (byteBuf.readableBytes() > BASE_LENGTH) 
            //防止socket字节流攻击
            //防止客户端传来的数据过大
            if (byteBuf.readableBytes() > 2048) 
                byteBuf.skipBytes(byteBuf.readableBytes());
            

            //记录包头开始的index
            int beginReader;

            while (true) 
                //获取包头开始的index
                beginReader = byteBuf.readerIndex();
                //标记包头开始的index
                byteBuf.markReaderIndex();
                //读到了协议的开始标志,结束while循环
                if (byteBuf.readInt() == ConstantValue.HEAD_DATA) 
                    break;
                

                //未读到包头,略过一个字符,继续读取包头的开始标记信息
                byteBuf.resetReaderIndex();
                byteBuf.readByte();

                //当略过一个字符后,数据包长度不符合,等待后续数据到达
                if (byteBuf.readableBytes() < BASE_LENGTH) 
                    return;
                
            

            //消息的长度
            int length = byteBuf.readInt();
            //判断数据包是否完整
            if (byteBuf.readableBytes() < length) 
                //还原读指针
                byteBuf.readerIndex(beginReader);
                return;
            

            //读取消息内容
            byte[] data = new byte[length];
            byteBuf.readBytes(data);

            MessageProtocol protocol = new MessageProtocol(data);
            list.add(protocol);
        
    




/**
 * Created by ICE on 2021年1月11日.
 */
public class MessageEncoder extends MessageToByteEncoder<MessageProtocol> 


    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, @NonNull MessageProtocol messageProtocol, @NonNull ByteBuf byteBuf) throws Exception 
        //写入消息
        //1.写入消息的开头信息标志(int)
        byteBuf.writeInt(messageProtocol.getHeadData());
        //2.写入消息的长度(int)
        byteBuf.writeInt(messageProtocol.getContentLength());
        //3.写入消息的内容(byte[])
        byteBuf.writeBytes(messageProtocol.getContent());
    


SocketUtils文本消息转换类


/**
 * Created by ICE on 2021年1月11日.
 */
public class SocketUtils 

    @RequiresApi(api = Build.VERSION_CODES.N)
    public static String transfer(@NonNull ByteBuf buf) 
        if (Objects.isNull(buf)) 
            return "";
        
        String str;
        if (buf.hasArray()) 
            str = new String(buf.array(), buf.arrayOffset() + buf.readerIndex(), buf.readableBytes());
        else 
            byte[] bytes = new byte[buf.readableBytes()];
            buf.getBytes(buf.readerIndex(), bytes);
            str = new String(bytes, 0, buf.readableBytes());
        
        return str;
    




SocketClient启动类


/**
 * Created by ICE on 2019-06-10.
 */
@Slf4j
public class SocketClient implements Start 
    private Channel channel;
    private final Bootstrap bs = new Bootstrap();
    private final EventLoopGroup group = new NioEventLoopGroup();
    private final SocketClientHandler handler = new SocketClientHandler(this);

    private String host;
    private int port;

    private MessageListener listener;
    private String token;

    public MessageListener getListener() 
        return listener;
    

    public String getToken() 
        return token;
    

    public void addListener(MessageListener listener) 
        this.listener = listener;
    

    public SocketClient() 
        bs.group(group)
                .channel(NiosocketChannel.class)
//          

以上是关于Android 长连接Netty的主要内容,如果未能解决你的问题,请参考以下文章

Netty 实现长连接服务的难点和优化点

Netty 实现长连接服务的难点和优化点

netty 是否长连接

Netty 实现长连接服务的难点和优化点

Netty SpringBoot 整合长连接心跳机制

长连接开发踩坑之netty OOM问题排查实践