Netty Protocol Development

Keywords: Netty network Java Web Server

Http

HTTP protocol is the abbreviation of Hyper Text Transfer Protocol (HTTP), which is used to transfer hypertext from WWW:World Wide Web server to local browser.

HTTP is a communication protocol based on TCP/IP to transfer data (HTML files, image files, query results, etc.).

HTTP is an object-oriented protocol belonging to the application layer. Because of its simple and fast way, it is suitable for distributed hypermedia information system. It was put forward in 1990. After several years of use and development, it has been continuously improved and expanded. At present, the sixth edition of HTTP/1.0 is used in WWW. The standardization of HTTP/1.1 is under way, and the proposal of HTTP-NG(Next Generation of HTTP) has been put forward.

HTTP protocol works on client-server architecture. As an HTTP client, the browser sends all requests to the HTTP server through the URL. The Web server sends the response information to the client after receiving the request.

main features

  • Simple and fast: When a client requests a service from a server, it only needs to send the request method and path. GET, HEAD and POST are commonly used in request methods. Each method specifies different types of client-server connections. Because of the simplicity of HTTP protocol, the program size of HTTP server is small, so the communication speed is very fast.
  • Flexibility: HTTP allows the transfer of arbitrary types of data objects. The type being transmitted is marked by Content-Type.
  • Connectionless: Connectionless means that only one request is processed per connection. After the server has processed the customer's request and received the customer's reply, the connection is disconnected. This method can save transmission time.
  • Stateless: HTTP is a stateless protocol. Statelessness means that the protocol has no memory for transaction processing. Lack of state means that if the previous information is required for subsequent processing, it must be retransmitted, which may result in an increase in the amount of data transmitted per connection. On the other hand, the server responds faster when it does not need previous information.
  • Support B/S and C/S mode.
public class HttpServer {

    private static final String DEFAULT_URL = "/src/main/java/com/heqing/netty/";

    public void run(final int port, final String url) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                            ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
                            ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                            ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                            ch.pipeline().addLast("fileServerHandler", new HttpServerHandler(url));
                        }
                    });
            ChannelFuture future = b.bind("192.168.1.195", port).sync();
            System.out.println("HTTP File Directory Server is started. The address is : " + "http://192.168.1.195:"
                    + port + url);
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        String url = DEFAULT_URL;
        new HttpServer().run(port, url);
    }
}
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private final String url;
    private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
    private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");

    public HttpServerHandler(String url) {
        this.url = url;
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx,FullHttpRequest request) throws Exception {
        if (!request.getDecoderResult().isSuccess()) {
            sendError(ctx, BAD_REQUEST);
            return;
        }
        if (request.getMethod() != GET) {
            sendError(ctx, METHOD_NOT_ALLOWED);
            return;
        }
        final String uri = request.getUri();
        final String path = sanitizeUri(uri);
        if (path == null) {
            sendError(ctx, FORBIDDEN);
            return;
        }
        File file = new File(path);
        if (file.isHidden() || !file.exists()) {
            sendError(ctx, NOT_FOUND);
            return;
        }
        if (file.isDirectory()) {
            if (uri.endsWith("/")) {
                sendListing(ctx, file);
            } else {
                sendRedirect(ctx, uri + '/');
            }
            return;
        }
        if (!file.isFile()) {
            sendError(ctx, FORBIDDEN);
            return;
        }
        RandomAccessFile randomAccessFile = null;
        try {
            randomAccessFile = new RandomAccessFile(file, "r");// Open files in a read-only manner
        } catch (FileNotFoundException fnfe) {
            sendError(ctx, NOT_FOUND);
            return;
        }
        long fileLength = randomAccessFile.length();
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
        setContentLength(response, fileLength);
        setContentTypeHeader(response, file);
        if (isKeepAlive(request)) {
            response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
        }
        ctx.write(response);
        ChannelFuture sendFileFuture;
        sendFileFuture = ctx.write(new ChunkedFile(randomAccessFile, 0,
                fileLength, 8192), ctx.newProgressivePromise());
        sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
            @Override
            public void operationProgressed(ChannelProgressiveFuture future,
                    long progress, long total) {
                if (total < 0) { // total unknown
                    System.err.println("Transfer progress: " + progress);
                } else {
                    System.err.println("Transfer progress: " + progress + " / "
                            + total);
                }
            }

            @Override
            public void operationComplete(ChannelProgressiveFuture future)
                    throws Exception {
                System.out.println("Transfer complete.");
            }
        });
        ChannelFuture lastContentFuture = ctx
                .writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        if (!isKeepAlive(request)) {
            lastContentFuture.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        if (ctx.channel().isActive()) {
            sendError(ctx, INTERNAL_SERVER_ERROR);
        }
    }

    private String sanitizeUri(String uri) {
        try {
            uri = URLDecoder.decode(uri, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            try {
                uri = URLDecoder.decode(uri, "ISO-8859-1");
            } catch (UnsupportedEncodingException e1) {
                throw new Error();
            }
        }
        if (!uri.startsWith(url)) {
            return null;
        }
        if (!uri.startsWith("/")) {
            return null;
        }
        uri = uri.replace('/', File.separatorChar);
        if (uri.contains(File.separator + '.')
                || uri.contains('.' + File.separator) || uri.startsWith(".")
                || uri.endsWith(".") || INSECURE_URI.matcher(uri).matches()) {
            return null;
        }
        return System.getProperty("user.dir") + File.separator + uri;
    }

    private static void sendListing(ChannelHandlerContext ctx, File dir) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
        response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
        StringBuilder buf = new StringBuilder();
        String dirPath = dir.getPath();
        buf.append("<!DOCTYPE html>\r\n");
        buf.append("<html><head><title>");
        buf.append(dirPath);
        buf.append(" Catalog:");
        buf.append("</title></head><body>\r\n");
        buf.append("<h3>");
        buf.append(dirPath).append(" Catalog:");
        buf.append("</h3>\r\n");
        buf.append("<ul>");
        buf.append("<li>Links:<a href=\"../\">..</a></li>\r\n");
        for (File f : dir.listFiles()) {
            if (f.isHidden() || !f.canRead()) continue;
            String name = f.getName();
            if (!ALLOWED_FILE_NAME.matcher(name).matches())  continue;
            buf.append("<li>Links:<a href=\""+name+"\">"+name+"</a></li>\r\n");
        }
        buf.append("</ul></body></html>\r\n");
        ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);
        response.content().writeBytes(buffer);
        buffer.release();
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
        response.headers().set(LOCATION, newUri);
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,status, 
                Unpooled.copiedBuffer("Failure: " + status.toString()+ "\r\n", CharsetUtil.UTF_8));
        response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    private static void setContentTypeHeader(HttpResponse response, File file) {
        MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
        response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
    }
}

WebSocket

Websocket is a protocol specification proposed by HTML 5, referring to rfc6455.

Websocket stipulates a communication specification. Through a handshake mechanism, a tcp-Like connection can be established between client (browser) and server (webserver), thus facilitating communication between c-s. Before the advent of websocket, web interaction was usually based on short or long connections of http protocol.

WebSocket is a technology for real-time communication between client and server. Websocket protocol is essentially a TCP-based protocol. It first initiates a special http request through HTTP/HTTPS protocol to shake hands and then creates a TCP connection for data exchange. After that, the server and client communicate in real time through this TCP connection.

main features

  • The greatest thing about the WebSocket API is that servers and clients can push information to each other at any time in a given time range. Browsers and servers only need to shake hands. After establishing a connection, the server can actively transmit data to the client, and the client can also send data to the server at any time. In addition, the header information exchanged between the server and the client is very small.
  • WebSocket is not limited to Ajax (or XHR) communication, because Ajax technology requires clients to initiate requests, while WebSocket servers and clients can push information to each other;
public class WebSocketServer {
    public void run(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("http-codec", new HttpServerCodec());
                            pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
                            pipeline.addLast("http-chunked", new ChunkedWriteHandler());
                            pipeline.addLast("handler", new WebSocketServerHandler());
                        }
                    });

            Channel ch = b.bind(port).sync().channel();
            System.out.println("Web socket server started at port " + port + '.');
            System.out.println("Open your browser and navigate to http://localhost:" + port + '/');

            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new WebSocketServer().run(port);
    }
}
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger logger = Logger.getLogger(WebSocketServerHandler.class.getName());

    private WebSocketServerHandshaker handshaker;

    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
        // Traditional HTTP Access
        if (msg instanceof FullHttpRequest) handleHttpRequest(ctx, (FullHttpRequest) msg);
        // WebSocket Access
        else if (msg instanceof WebSocketFrame) handleWebSocketFrame(ctx, (WebSocketFrame) msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {

        // If HTTP decoding fails, return the HHTP exception
        if (!req.getDecoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
            return;
        }

        // Construct handshake response return, local test
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:8080/websocket", null, false);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
          WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), req);
        }
    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        // Instructions to determine whether a link is closed
        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        // Determine if it's a Ping message
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // This routine only supports text messages, not binary messages.
        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass().getName()));
        }

        // Return reply message
        String request = ((TextWebSocketFrame) frame).text();
        if (logger.isLoggable(Level.FINE)) {
            logger.fine(String.format("%s received %s", ctx.channel(), request));
        }
        ctx.channel().write(
                new TextWebSocketFrame(request+ " , Welcome to use Netty WebSocket Service, now:"+ new java.util.Date().toString()));
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx,
            FullHttpRequest req, FullHttpResponse res) {
        // Return the reply to the client
        if (res.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
            setContentLength(res, res.content().readableBytes());
        }

        // If it is not Keep-Alive, close the connection
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!isKeepAlive(req) || res.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

UDP

UDP yes User Datagram Protocol In short, the Chinese name is the User Datagram Protocol. OSI(Open System Interconnection,Open System Interconnection) A connectionless transport layer protocol in the reference model that provides simple and unreliable transactional information delivery services. IETF RFC 768 yes UDP Formal specification. UDP stay IP The protocol number of the message is 17.

UDP The full name of the protocol is the user datagram protocol[1] ,In the network it and TCP The protocol is also used to process data packets. It is a connectionless protocol. stay OSI In the model, at Layer 4 - Transport Layer, in IP The upper layer of the protocol. UDP The disadvantage of not providing packet grouping, assembly and sorting is that when a message is sent, it is impossible to know whether it arrives safely and completely. UDP Used to support network applications that need to transfer data between computers. Numerous customers including network video conferencing system/Server-mode network applications need to use UDP Agreement. UDP The protocol has been in use for many years since its inception, although its initial glory has been masked by some similar agreements, even today. UDP It is still a very practical and feasible network transport layer protocol.

To be familiar with TCP(The transmission control protocol (TCP) protocol is the same. UDP The protocol is directly located in IP(Internet Protocol) Top-level protocol. according to OSI(Open System Interconnection (OSI) Reference Model, UDP and TCP All belong to the transport layer protocol. UDP The main function of the protocol is to compress the network data traffic into the form of data packets. A typical data packet is a transmission unit of binary data. The first eight bytes of each packet are used to contain header information, while the remaining bytes are used to contain specific transmission data.

public class UDPServer {
    public void run(int port) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new UDPServerHandler());
            b.bind(port).sync().channel().closeFuture().await();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new UDPServer().run(port);
    }
}
public class UDPServerHandler extends
        SimpleChannelInboundHandler<DatagramPacket> {

    private static final String[] DICTIONARY = {"Spring", "MyBatis", "Hibernate", "Struts", "Netty", "JMS"};

    private String nextQuote() {
        int quoteId = ThreadLocalRandom.current().nextInt(DICTIONARY.length);
        return DICTIONARY[quoteId];
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
        String req = packet.content().toString(CharsetUtil.UTF_8);
        if ("Java".equals(req)) {
            ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(
                    "-->" + nextQuote(), CharsetUtil.UTF_8), packet.sender()));
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        cause.printStackTrace();
    }
}
public class UDPClient {

    public void run(int port) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new UDPClientHandler());
            Channel ch = b.bind(0).sync().channel();
            // Broadcast UDP messages to all machines in the network segment
            ch.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("Java", CharsetUtil.UTF_8), 
                    new InetSocketAddress("127.0.0.1", port))).sync();
            if (!ch.closeFuture().await(15000)) {
                System.out.println("query timeout!");
            }
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new UDPClient().run(port);
    }
}
public class UDPClientHandler extends
        SimpleChannelInboundHandler<DatagramPacket> {

    @Override
    public void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
        String response = msg.content().toString(CharsetUtil.UTF_8);
        if (response.startsWith("-->")) {
            System.out.println(response);
            ctx.close();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

File

File The protocol is mainly used to access files on the local computer, just like in the Windows Open the file in the resource manager the same way.

public class FileServer {

    public void run(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(
                                new StringEncoder(CharsetUtil.UTF_8),
                                new LineBasedFrameDecoder(1024),
                                new StringDecoder(CharsetUtil.UTF_8),
                                new FileServerHandler());
                        }
                    });
            ChannelFuture f = b.bind(port).sync();
            System.out.println("Start file server at port : " + port);
            f.channel().closeFuture().sync();
        } finally {
            // Elegant shutdown
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new FileServer().run(port);
    }
}
public class FileServerHandler extends SimpleChannelInboundHandler<String> {

    private static final String CR = System.getProperty("line.separator");

    /*
     * (non-Javadoc)
     * 
     * @see
     * io.netty.channel.SimpleChannelInboundHandler#messageReceived(io.netty
     * .channel.ChannelHandlerContext, java.lang.Object)
     */
    public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
        File file = new File(msg);
        if (file.exists()) {
            if (!file.isFile()) {
                ctx.writeAndFlush("Not a file : " + file + CR);
                return;
            }
            saveFile(file);
            ctx.write(file + " " + file.length() + CR);
            RandomAccessFile randomAccessFile = new RandomAccessFile(msg, "r");
            FileRegion region = new DefaultFileRegion(randomAccessFile.getChannel(), 0, randomAccessFile.length());
            ctx.write(region);
            ctx.writeAndFlush(CR);
            randomAccessFile.close();
        } else {
            ctx.writeAndFlush("File not found: " + file + CR);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * io.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel
     * .ChannelHandlerContext, java.lang.Throwable)
     */
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    public void saveFile(File beforefile) {
        try {
            //This is the file you want to save. It's self-defined, it doesn't exist.
            File afterfile = new File("d://temp//"+beforefile.getName());
            //Define the file input stream to read the beforefile file file
            FileInputStream fis = new FileInputStream(beforefile);
            //Define the file output stream to write information to the afterfile file
            FileOutputStream fos = new FileOutputStream(afterfile);
            //File Cache
            byte[] b = new byte[1024];
            //Read the file stream information into the file buffer. If the reading result is not - 1, it means that the file has not been read, otherwise it has been read.
            while(fis.read(b)!=-1){
                //Write the contents of the buffer to the afterfile file
                fos.write(b);
                fos.flush();
            }
            fos.close();
            fis.close();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

Posted by phreek on Fri, 05 Jul 2019 11:36:22 -0700