preface

As is known to all, push and IM in Android applications are common, but the real to achieve less, most of us will go to choose a third party to provide mature scheme, such as the aurora push, cloud letter, because the mobile network is uncertain, so oneself achieve a stable solution will cost a lot of energy, it is not worth for a small company.

Push and IM we usually use a lot, but really understand the principle of not much, really hands-on implementation of not much. In essence, push and IM are both long connections, which are nothing more than different business directions, so we collectively call them long connections below. Today we are going to unveil the mystery of the long link.

nettyWhat is is

Although many people are familiar with Netty, but there may be some students who do not know, so let’s first briefly introduce netty.

Netty is a Java open source framework developed by JBOSS

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

Netty is an asynchronous event-driven network application framework for the rapid development of maintainable, high-performance protocol servers and clients.

This is an excerpt from netty’s official website. I’ve translated ^ _ ^ for you

Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server. ‘Quick and easy’ doesn’t mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.

Netty is a NIO client server framework that makes it quick and easy to develop network applications such as protocol servers and clients. It greatly simplifies and simplifies network programming such as TCP and UDP socket servers. “Fast and easy” does not mean that the resulting application will suffer from maintainability or performance issues. Netty has extensive design experience, including FTP, SMTP, HTTP, and a variety of traditional protocols based on binary and text. As a result, Netty has successfully found a way to achieve easy development, performance, stability and flexibility without compromising.

Once you copy, you can’t stop. = Mainly feel that the official website is very accurate.

Event driven is actually very simple. For example, you click the mouse and the software performs the corresponding operation. This is an event driven model. Looper sends a message through the Handler, which is equivalent to an event. Looper retrieves the event, which is processed by the Handler.

These features make Netty well suited for high concurrency persistent connections.

Today, we’re going to use Netty to implement an Android IM, both client and server.

conceived

As an IM application, we need to identify the user. After establishing a long connection, the client needs to report its own information. After the server authenticates it, it caches the information, indicating that the user is online.

For example, A sends A message to B. A first sends the message to the server and tells the server who to send the message to, and then the server sends the message to B.

After receiving the message, the server can store the message. If USER B is not online, the server can send the message after user B is online.

In actual combat

Create a new project

  1. Write client-side code

Add a Netty dependency

implementation Ty: 'io.net netty - all: 4.1.9. Final'
Copy the code

Netty already has a beta version of 5.x, so we’re using the latest stable version for stability.

  • Establish a connection to the server
// Change the HOST and port to your own. Private static final String HOST ="10.240.78.82";
private static final int PORT = 8300;

private SocketChannel socketChannel;

NioEventLoopGroup group = new NioEventLoopGroup();
new Bootstrap()
    .channel(NioSocketChannel.class)
    .group(group)
    .option(ChannelOption.TCP_NODELAY, true// Send.option(channeloption.so_keepalive,trueHandler (new ChannelInitializer<SocketChannel>() {@override protected void initChannel(SocketChannel) socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new IdleStateHandler(0, 30, 0)); pipeline.addLast(new ObjectEncoder()); pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null))); pipeline.addLast(new ChannelHandle()); } }) .connect(new InetSocketAddress(HOST, PORT)) .addListener((ChannelFutureListener) future -> {if(future.isSuccess()) {socketChannel = (socketChannel) future.channel(); }else {
            Log.e(TAG, "connect failed"); // This must be closed, otherwise OOM Future.channel ().close(); group.shutdownGracefully(); }});Copy the code
  • The identity authentication
LoginInfo loginInfo = new LoginInfo();
loginInfo.setAccount(account);
loginInfo.setToken(token);
CMessage loginMsg = new CMessage();
loginMsg.setFrom(account);
loginMsg.setType(MsgType.LOGIN);
loginMsg.setContent(loginInfo.toJson());
socketChannel.writeAndFlush(loginMsg.toJson())
        .addListener((ChannelFutureListener) future -> {
            if(Future.isSuccess ()) {// Send successful, wait for server response}else{// close(); // Close the connection to save resources}});Copy the code
  • Process messages from the server
private class ChannelHandle extends SimpleChannelInboundHandler<String> { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); Pushservice.this.close (); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { super.userEventTriggered(ctx, evt);if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;
            if(e.state() == idlestate.writer_idle) {CMessage message = new CMessage(); message.setFrom(myInfo.getAccount()); message.setType(MsgType.PING); ctx.writeAndFlush(message.toJson()); } } } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { Gson gson = new Gson(); CMessage message = gson.fromJson(msg, CMessage.class);if(message.gettype () == msgType.login) {// Server returns LOGIN result}else if (message.getType() == MsgType.PING) {
            Log.d(TAG, "receive ping from server"); // Heartbeat received from server}else if (message.getType() == MsgType.TEXT) {
            Log.d(TAG, "receive text message "+ message.getContent()); / / messages are received} ReferenceCountUtil. Release (MSG); }}Copy the code

This code executes in the background for a long time, so we put it in a Service.

  1. Writing server code

Create a new Android Library module as the server and add the same dependencies

  • Start the Netty service and bond ports
new ServerBootstrap()
    .group(new NioEventLoopGroup(), new NioEventLoopGroup())
    .channel(NioServerSocketChannel.class)
    .option(ChannelOption.SO_BACKLOG, 128)
    .option(ChannelOption.TCP_NODELAY, true) // Send.childOption(channeloption.so_keepalive,trueChildHandler (new ChannelInitializer<SocketChannel>() {@override protected void initChannel(SocketChannel) socketChannel) { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new ObjectEncoder()); pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null))); pipeline.addLast(new NettyServerHandler()); } }) .bind(port) .addListener((ChannelFutureListener) future -> {if (future.isSuccess()) {
            System.out.println("netty server start");
        } else {
            System.out.println("netty server start failed"); }});Copy the code
  • Process messages from the client
public class NettyServerHandler extends SimpleChannelInboundHandler<String> { @Override public void Nettychannelmap.remove (ctx.channel()); channelInactive(ChannelHandlerContext CTX) {nettyChannelMap.remove (ctx.channel()); } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) { Gson gson = new Gson(); CMessage message = gson.fromJson(msg, CMessage.class);if (message.getType() == MsgType.PING) {
            System.out.println("received ping from "+ message.getFrom()); Channel Channel = nettyChannelMap.get (message.getfrom ());if (channel != null) {
                channel.writeAndFlush(message.toJson());
            }
        } else if(message.getType() == msgtype.login) {// LoginInfo LoginInfo = gson.fromjson (message.getContent(), LoginInfo.class);if (UserManager.get().verify(loginInfo)) {
                loginInfo.setCode(200);
                loginInfo.setMsg("success");
                message.setContent(loginInfo.toJson());
                ctx.channel().writeAndFlush(message.toJson());
                NettyChannelMap.add(loginInfo.getAccount(), ctx.channel());
                System.out.println(loginInfo.getAccount() + " login");
            } else {
                loginInfo.setCode(400);
                loginInfo.setMsg("Incorrect user name or password"); message.setContent(loginInfo.toJson()); ctx.channel().writeAndFlush(message.toJson()); }}else if(message.getType() == msgtype.text) {Channel Channel = nettyChannelMap.get (message.getto ());if(channel ! = null) { channel.isWritable(); channel.writeAndFlush(message.toJson()).addListener((ChannelFutureListener) future -> {if(! future.isSuccess()) { System.out.println("send msg to " + message.getTo() + " failed"); }}); } } ReferenceCountUtil.release(msg); }}Copy the code

The logged in users are cached in NettyChannelMap.

The offline message cache logic can be added here. If the message fails to be sent, it needs to be cached and sent after the user goes online.

If the server is running on the local PC, the server must be on the same LAN as the client. If the server is running on the public network, this parameter is not required.

Running effect

The source code

Only look at the above code may still be a little confused, I suggest you run the source code, netty will have a clearer understanding. Github.com/wangchenyan…

conclusion

Today we got to know Netty and realized a simple IM application with Netty. Here we only implement the core functions of IM. Other functions such as keepalive mechanisms and disconnection are beyond the scope of this article.

There is not much difference between the long-link service we implement today and the long-link service provided by third-party long-link service providers, except that the latter has a mature mechanism for keeping alive and reconnecting short lines.

After reading this article, do you think long links are not so mysterious after all?

But don’t be arrogant, we’ve only scratched the surface of the simplest usage today, and it’s going to take a lot of work to fully understand how it works.

A Brief book on Migrating self, December 27, 2017