Actual combat of network programming project based on Netty

Posted by pinacoladaxb on Fri, 28 Jan 2022 21:25:16 +0100

Introduction to Netty and related basic knowledge

  • Netty provides asynchronous event driven network application framework and tools to quickly develop high-performance and reliable network server and client programs.
    • Netty provides an easy-to-use API
    • Write network communication program based on event driven programming
    • Higher throughput
    • Low learning difficulty

Introduction and difference of BIO, NIO and AIO

  • Blocking and non blocking

  • Synchronous and asynchronous

  • BIO synchronization blocks IO, and block IO will block threads during IO operation, resulting in low concurrent processing capacity.

  • NIO synchronizes non blocking IO. NIO is an improvement of BIO and is based on Reactor model.

  • AIO(NIO2.0) asynchronous non blocking IO completes the client request processing and notifies the server to start the thread for processing.

Netty Reactor model - single thread model, multi thread model, master-slave multi thread model

  • Single thread model

  • Reactor multithreading model

    However, if the concurrency is still large, Reactor still cannot handle a large number of client requests

  • Reactor master-slave multithreading model

This thread model is recommended by Netty,
This model is suitable for high concurrency scenarios. A group of thread pools receive requests and a group of thread pools process IO.

Netty - DEMO implementation of simple chat based on web socket

  • Import dependency
   <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.15.Final</version>
        </dependency>
    </dependencies>
  • Writing back-end code for Netty Server
public class WebsocketServer {
    public static void main(String[] args) {
        //Create two thread pools
        NioEventLoopGroup mainGroup = new NioEventLoopGroup();
        NioEventLoopGroup subGroup = new NioEventLoopGroup();

        try {
            //Create Netty server startup object
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //Initialize container startup object
            serverBootstrap
                    //Specify to use the two thread pools created above
                    .group(mainGroup, subGroup)
                    //Specify Netty channel type
                    .channel(NioServerSocketChannel.class)
                    //Specifies that the Channel initializer is used to load when the Channel receives an event
                    //Conduct business processing
                    .childHandler(new WebSocketChanelInitailizer());
            //Bind the server port and start the server in a synchronized manner
            ChannelFuture future = serverBootstrap.bind(9090).sync();
            //Wait for the server to shut down
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //Shut down the server
            mainGroup.shutdownGracefully();
            subGroup.shutdownGracefully();
        }
    }
}
/**
 * The channel initializer is used to load the channel handler
 *
 * @Author chenppeng
 * @Date 2021-06-03 10:58
 * @Version 1.0
 */
@SuppressWarnings("all")
public class WebSocketChanelInitailizer extends ChannelInitializer<SocketChannel> {

    /**
     * Initialize the channel. This method mainly loads the corresponding ChannelHandler
     *
     * @param socketChannel
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        //Get the pipeline and load the channelhandlers one by one into the pipeline
        ChannelPipeline pipeline = socketChannel.pipeline();
        //Add an http codec
        pipeline.addLast(new HttpServerCodec());
        //Add a user to support large data flow
        pipeline.addLast(new ChunkedWriteHandler());
        //Add an aggregator, which is mainly used to aggregate HttpMessage into FullHttpRequest/Response
        pipeline.addLast(new HttpObjectAggregator(1024 * 64));
        //You need to specify a route to accept requests
        //You must use a url ending with the ws suffix to access
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        //Add custom hanlder
        pipeline.addLast(new ChatHandler());
    }
}
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * The user saves all client connections
     */
    private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * Time format parser
     */
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd hh:MM:ss");

    /**
     * When there is a new event in the Channel, the message will be called automatically
     *
     * @param channelHandlerContext
     * @param textWebSocketFrame
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        //It will be called automatically after receiving the data
        //Get the text message sent by the client
        String text = textWebSocketFrame.text();
        System.out.println("The received message data is:" + text);
        for (Channel client : clients) {
            //Send messages to all clients
            client.writeAndFlush(new TextWebSocketFrame(simpleDateFormat.format(new Date()) + ":" + text));
        }
    }

    /**
     * This method will be called automatically when a new client li connects to the server
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        //Add new channels to clients
        clients.add(ctx.channel());
    }
}

  • Write front-end code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Online chat room</title>
</head>
<body>
<label for="message"><input type="text" id="message"></label>
<input type="button" value="send message"  onclick="SendMessage()">

Received message
<p id="server_message" style="background-color: #AAAAAA"></p>

<script>
    let websocket = null;
    //Judge whether the current browser supports web socket
    if (window.WebSocket) {
        //Connect to the server and send a message
        websocket = new WebSocket("ws://127.0.0.1:9090/ws");
        websocket.onopen = function () {
            console.log("Establish connection");
        };

        websocket.onclose = function () {
            console.log("Disconnect")
        };

        websocket.onmessage = function (e) {
            console.log("Message received from server:" + e.data);
            document.getElementById("server_message").innerHTML += e.data + "<br>";
        }
    } else {
        alert("The current browser does not support web socket")
    }

    function SendMessage() {
        let message = document.getElementById("message");
        websocket.send(message.value);
    }
</script>
</body>
</html>

Introduction to MUI, HTML5 +, HBuilder

MUI is a lightweight front-end framework. MUI is based on iOS platform UI and complements some UI controls unique to Android platform. MUI does not rely on any third-party JS library. The compressed JS and CSS files are only 100+K and 60+K. you can customize and download the corresponding modules according to your own needs. And the front end written by MUI can be packaged into APK and IPA installation files and run on the mobile terminal. That is, write a set of code, which can be used in Android
Run under IOS.

H5 + provides enhancements to HTML5 and 40WAPI for programmers. Using H5+ API, you can easily develop QR code scanning, camera, map location, message push and other functions

User login function


  • Background business code
 @Override
    public User login(String username, String password) {
        if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
            TbUserExample tbUserExample = new TbUserExample();
            TbUserExample.Criteria criteria = tbUserExample.createCriteria();
            criteria.andUsernameEqualTo(username);
            List<TbUser> userList = tbUserMapper.selectByExample(tbUserExample);
            if (userList != null && userList.size() == 1) {
                String pwd = DigestUtils.md5DigestAsHex(password.getBytes());
                if (pwd.equals(userList.get(0).getPassword())) {
                    User user = new User();
                    BeanUtils.copyProperties(userList.get(0), user);
                    return user;
                }
            }
        }
        return null;
    }

Registration function


  • Background business code
@Override
    public void register(TbUser tbUser) {
        //Judge whether the user exists in the database
        TbUserExample example = new TbUserExample();
        TbUserExample.Criteria criteria = example.createCriteria();
        criteria.andUsernameEqualTo(tbUser.getUsername());
        List<TbUser> userList = tbUserMapper.selectByExample(example);
        if (userList != null && userList.size() > 0) {
            throw new RuntimeException("login has failed");
        }
        //Save user information to database
        tbUser.setId(idWorker.nextId());
        tbUser.setPassword(DigestUtils.md5DigestAsHex(tbUser.getPassword().getBytes()));
        tbUser.setPicSmall("");
        tbUser.setPicNormal("l");
        tbUser.setNickname(tbUser.getUsername());
        tbUser.setCreatetime(new Date());
        tbUserMapper.insert(tbUser);
    }

Introduction and construction of FastDFS file server

FastDFS is an open source distributed file system written in c language. FastDFS is tailor-made for the Internet. It fully considers redundant backup, load balancing, linear capacity expansion and other mechanisms, and pays attention to high availability, high performance and other indicators. It is easy to build a set of high-performance file server cluster using FastDFS to provide file upload, download and other services.

  • springboot integrates fastdfs to upload user avatars
    • application.properties configuration file
#Consolidate FASTDFS
fdfs.soTimeout=1501
fdfs.connectTimeout=601 
#Thumbnail generation parameters
fdfs.thumbImage.width=150
fdfs.thumbImage.height=150
#TrackerList parameter, supporting multiple
fdfs.trackerList[0]=192.168.25.135:22122

#HTTP URL
fdfs.httpurl=http://192.168.25.135/

Upload avatar business layer

 @Override
    public User upload(MultipartFile file, String userid) {
        try {
            //The url path in the returned fastdfs without http://192.168.1.133/..
            String url = fastDFSClient.uploadFile(file);
            //When fastDFS uploads, it will automatically generate a thumbnail file name_ 150 * 150, suffix
            String[] fileNameList = url.split("\\.");
            String fileName = fileNameList[0];
            String ext = fileNameList[1];
            String picSmallUrl = fileName + "_150x150." + ext;
            String prefix = environment.getProperty("fdfs.httpurl");
            TbUser tbUser = tbUserMapper.selectByPrimaryKey(userid);
            //Set avatar big picture
            tbUser.setPicNormal(prefix + url);
            //Set avatar small picture
            tbUser.setPicSmall(prefix + picSmallUrl);
            //Update the new avatar url to the database
            tbUserMapper.updateByPrimaryKey(tbUser);
            User user = new User();
            BeanUtils.copyProperties(tbUser, user);
            return user;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

Topics: Java network