Socket communication using network programming

If you think the content of this blog is helpful or inspiring to you, please follow my blog to get the latest technical articles and tutorials as soon as possible. At the same time, you are also welcome to leave a message in the comment area to share your thoughts and suggestions. Thank you for your support!

1. Network programming and socket communication

1.1 What is network programming

Network programming refers to the process of writing application programs to exchange data between computer networks. Network programming can help us build distributed systems, realize data communication between clients and servers, and realize peer-to-peer communication, etc.

1.2 What is socket communication

Socket communication is the most common communication method in network programming. It is based on the TCP/IP protocol stack, establishes a connection between two computers through the network, and transmits data between the connections. Socket communication can exchange data between different programs running on different computers, and it is an important part of building network applications.

1.3 Advantages of socket communication

The advantages of Socket communication include:

  1. Reliability: Socket communication uses TCP protocol to ensure the reliability of data transmission and ensure that data will not be lost or damaged.
  2. Flexibility: Socket communication supports multiple network protocols and transmission methods, and can be used in different application scenarios.
  3. Efficiency: Socket communication has efficient data transmission speed and low latency, which can meet the needs of large amounts of data transmission.
  4. Versatility: Socket communication can be used not only to transmit text data, but also to transmit different types of data such as multimedia data and binary data.
  5. Programmability: Socket communication is a programming interface, and developers can perform custom programming according to their own needs to achieve more complex functions.

Two, socket communication protocol

2.1 TCP protocol

TCP is the abbreviation of Transmission Control Protocol, which is a connection-oriented and reliable protocol. The TCP protocol establishes a connection through a three-way handshake to ensure reliable data transmission. In the TCP connection, the data is divided into multiple data packets, and each data packet is marked with a serial number and sorted. After receiving the data packet, the receiving end confirms and reassembles according to the serial number to ensure the accuracy and integrity of the data. sex.

The TCP protocol has the following characteristics:

  1. Reliability: The TCP protocol ensures reliable data transmission through confirmation and retransmission mechanisms.
  2. Orderly: The TCP protocol divides the data into multiple data packets and sorts them according to the sequence number to ensure the orderly transmission of data.
  3. Connection-oriented: The TCP protocol needs to establish a connection before communication, and disconnect after the communication ends.
  4. Slow start and congestion control: The TCP protocol avoids network congestion and data loss through slow start and congestion control mechanisms.

The TCP protocol is often used in scenarios that require reliable transmission, such as web browsing and file transfer.

2.2 UDP protocol

UDP is the abbreviation of User Datagram Protocol, which is a connectionless-oriented protocol. The UDP protocol packages data into datagrams, does not guarantee reliable transmission and order of data, does not perform confirmation and retransmission, and only sends datagrams to the destination. Therefore, the UDP protocol has lower latency and network overhead.

The UDP protocol has the following characteristics:

  1. No connection: The UDP protocol does not need to establish a connection, and directly sends the datagram to the destination.
  2. Unreliability: The UDP protocol does not guarantee the reliable transmission and order of data, and does not perform confirmation and retransmission.
  3. Rapidity: The UDP protocol has low latency and network overhead, and can transmit data quickly.

The UDP protocol is often used in real-time data transmission scenarios, such as voice, video, games, etc. Since the UDP protocol has low delay and network overhead, it can meet the real-time requirements.

2.3 How to choose a protocol

Whether to choose the TCP protocol or the UDP protocol depends on the requirements and scenarios of the application.

If the application requires reliable data transmission and ordering, then the TCP protocol is a better choice. For example, application scenarios such as file transfer, web browsing, and e-mail need to ensure the accuracy and integrity of data. At this time, the confirmation and retransmission mechanism of the TCP protocol can ensure the reliable transmission and order of data.

If the application requires fast data transmission and real-time performance, then the UDP protocol is a better choice. For example, application scenarios such as real-time voice, video, and games need to meet low latency and network overhead. At this time, the connectionless and low network overhead of the UDP protocol can meet the real-time requirements.

To sum up, choosing a protocol needs to be made according to the needs and scenarios of the application. If the application requires reliable data transmission and orderliness, you should choose the TCP protocol; if the application requires fast data transmission and real-time performance, you should choose the UDP protocol.

3. Socket programming in Java

3.1 socket class and ServerSocket class

Socket programming in Java uses the Socket and ServerSocket classes in the java.net package.

The Socket class is used to establish a connection between the client and the server. It provides two constructors:

Socket(String host, int port) throws UnknownHostException, IOException
Socket(InetAddress address, int port) throws IOException

Among them, the first construction method is used to specify the host name and port number of the server, and the second construction method is used to specify the IP address and port number of the server.

The ServerSocket class is used to monitor the client's connection request on the server side. It provides a constructor and an accept() method:

ServerSocket(int port) throws IOException
Socket accept() throws IOException

Among them, the construction method is used to specify the port number of the server, and the accept() method is used to wait for the connection request from the client. Once a client connects successfully, the accept() method will return a new Socket object for communicating with the client. communication.

3.2 Communication between client and server

The communication between the client and the server needs to establish a connection first, and then use the input stream and output stream of the Socket object for data transmission. The following is an example of communication between a simple client and server:

Server side code:

import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器已启动,等待客户端连接...");

        Socket socket = serverSocket.accept();
        System.out.println("客户端已连接...");

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        String line = in.readLine();
        System.out.println("客户端发送的消息:" + line);

        out.println("你好,客户端!");
        out.flush();

        socket.close();
        serverSocket.close();
    }
}

Client code:

import java.io.*;
import java.net.*;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 8888);

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        out.println("你好,服务器!");
        out.flush();

        String line = in.readLine();
        System.out.println("服务器返回的消息:" + line);

        socket.close();
    }
}

In this example, the server first uses the ServerSocket class to create a server socket, and then uses the accept() method to wait for the client's connection request. Once a client is successfully connected, the accept() method will return a new Socket object for communicating with the client.

The server uses input streams and output streams to communicate with the client. The client creates a connection through the Socket class, and then uses the input stream and output stream to transmit data with the server.

4. Example demonstration

Socket programming in Java supports communication using TCP and UDP. Using TCP for communication can ensure the reliable transmission and order of data, but it may affect the real-time performance of transmission; using UDP for communication can improve the real-time performance of transmission, but it may affect the reliable transmission and order of data.

4.1 Use TCP protocol for socket communication

Sample code for socket communication using TCP:

Server side code:

import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器已启动,等待客户端连接...");

        Socket socket = serverSocket.accept();
        System.out.println("客户端已连接...");

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        String line = in.readLine();
        System.out.println("客户端发送的消息:" + line);

        out.println("你好,客户端!");
        out.flush();

        socket.close();
        serverSocket.close();
    }
}

Client code:

import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 8888);

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        out.println("你好,服务器!");
        out.flush();

        String line = in.readLine();
        System.out.println("服务器返回的消息:" + line);

        socket.close();
    }
}

4.2 Use UDP protocol for socket communication

Sample code for socket communication using UDP:

Server side code:

import java.net.*;

public class UDPServer {
    public static void main(String[] args) throws Exception {
        DatagramSocket serverSocket = new DatagramSocket(8888);
        System.out.println("服务器已启动,等待客户端连接...");

        byte[] receiveData = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);

        serverSocket.receive(receivePacket);
        System.out.println("客户端已连接...");

        String line = new String(receivePacket.getData(), 0, receivePacket.getLength());
        System.out.println("客户端发送的消息:" + line);

        InetAddress address = receivePacket.getAddress();
        int port = receivePacket.getPort();

        byte[] sendData = "你好,客户端!".getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port);
        serverSocket.send(sendPacket);

        serverSocket.close();
    }
}

Client code:

import java.net.*;

public class UDPClient {
    public static void main(String[] args) throws Exception {
        DatagramSocket clientSocket = new DatagramSocket();

        InetAddress address = InetAddress.getByName("localhost");
        int port = 8888;

        byte[] sendData = "你好,服务器!".getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port);
        clientSocket.send(sendPacket);

        byte[] receiveData = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);

        clientSocket.receive(receivePacket);
        String line = new String(receivePacket.getData(), 0, receivePacket.getLength());
        System.out.println("服务器返回的消息:" + line);

        clientSocket.close();
    }
}

When using UDP for socket communication, you need to use the DatagramSocket and DatagramPacket classes to send and receive data. The server first creates a DatagramSocket object, and then uses the receive() method to wait for the connection request from the client. Once a client is successfully connected, the server uses the DatagramPacket object to receive the data sent by the client.

The client uses the DatagramSocket class to create a DatagramPacket object, puts the data to be sent into the DatagramPacket object, and uses the send() method to send the data. After receiving the data, the server can use the DatagramPacket object to obtain the address and port of the client, and use the DatagramPacket object to send data to the client.

4.3 Summary

In Java, you can use the Socket class and ServerSocket class to implement the TCP protocol for socket communication, and you can also use the DatagramSocket class and DatagramPacket class to implement the UDP protocol for socket communication. The TCP protocol is suitable for scenarios that require high data reliability and order, and the UDP protocol is suitable for scenarios that require high real-time data.

Both TCP and UDP communications in the sample code are modeled as a single request and response. In practical applications, it is usually necessary to implement multiple requests and responses, which can be processed using multithreading or a thread pool. In addition, issues such as TCP connection reconnection, timeout, and disconnection processing, as well as UDP data loss and repeated transmission issues, etc. need to be considered.

5. Some common network programming problems and their solutions

5.1 Network delay and timeout issues

Network latency and timeout issues are frequently encountered problems in network programming. When a delay or timeout occurs during network communication, the client may not be able to receive the server's response, or the server may not be able to receive the client's request. In order to solve this problem, the following solutions can be adopted:

  • Set the timeout period: Set the timeout period in the Socket object. If no response or request is received within the specified time, a SocketTimeoutException will be thrown.

Socket socket = new Socket();
socket.setSoTimeout(5000); // 设置超时时间为5秒
  • Use the heartbeat mechanism: send heartbeat messages to the other party regularly to ensure the validity of the connection. If no response from the other party is received within a period of time, it can be determined that the connection has been disconnected.

5.2 Network congestion problem

Network congestion means that the amount of data sent in the network is greater than the capacity of the network, resulting in a bottleneck in the network. Network congestion issues can cause delays, loss, and duplication of data transmissions. In order to solve this problem, the following solutions can be adopted:

  • Reduce the amount of data transmission: Minimize unnecessary data transmission and reduce the occurrence of network congestion.
  • Use flow control: Use flow control technology to limit the rate of sending data to avoid network congestion.
  • Use data compression: Compress data to reduce the amount of data transmission, thereby reducing the occurrence of network congestion.

5.3 Packet Loss and Duplication Problems

The problem of data packet loss and duplication refers to the loss or repeated transmission of some data packets during data transmission. This problem may result in inaccurate and incomplete data transfers. In order to solve this problem, the following solutions can be adopted:

  • Use a reliable transmission protocol: use a reliable transmission protocol, such as the TCP protocol, to ensure the reliability of data transmission.
  • Use Sequence Numbers: Number packets with sequence numbers to identify lost or duplicated packets.
  • Use checksum: Use checksum to check the data packet to ensure the correctness of data transmission.

5.4 Security issues

Security issues mean that during network transmission, data may be eavesdropped by hackers, leading to data leakage and security issues. In order to solve this problem, the following solutions can be adopted:

  • Use encryption technology: encrypt data to ensure data security.
  • Using identity authentication technology: Authenticating user identities to prevent illegal users from accessing.
  • Use firewall technology: Use firewall technology to protect the network and prevent hackers.

Regarding network programming problems and their solutions, we just gave an overview here, and we will write articles to explain them later.

Six, the best practice of network programming

6.1 Avoid using blocking IO

Blocking IO will block the execution of the current thread until the IO operation is completed or an exception occurs. In a high-concurrency scenario, blocking IO will cause a large number of threads to block, thereby affecting the performance and throughput of the program. Therefore, try to avoid using blocking IO, and you can choose to use non-blocking IO or asynchronous IO.

The implementation of non-blocking IO is to set the Socket channel to non-blocking mode, and then use the Selector to poll the Socket channel. When the Socket channel is ready to read or write, the corresponding IO operation is performed. Here is an example of non-blocking IO:

import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
///

SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
while (true) {
    int readyChannels = selector.select();
    if (readyChannels == 0) continue;
    Set<SelectionKey> selectedKeys = selector.selectedKeys();
    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
    while (keyIterator.hasNext()) {
        SelectionKey key = keyIterator.next();
        if (key.isReadable()) {
            // 处理读操作
        } else if (key.isWritable()) {
            // 处理写操作
        }
        keyIterator.remove();
    }
}

6.2 Using the thread pool to manage threads

In a high-concurrency scenario, creating a large number of threads will lead to waste of system resources and overhead of thread switching, thus affecting the performance and throughput of the program. Therefore, you can use the thread pool to manage threads, reduce the cost of creating and destroying threads, and increase the reuse rate of threads.

Java provides the thread pool framework Executor and ExecutorService, which can easily create and manage thread pools. Here is an example using a thread pool:

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//

ExecutorService executorService = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
    Socket socket = serverSocket.accept();
    executorService.submit(() -> handleRequest(socket));
}

6.3 Using NIO (New IO) API

NIO (New IO) is a set of non-blocking IO APIs provided in Java. Compared with traditional IO APIs, it has better performance and scalability when dealing with high-concurrency and high-throughput scenarios. The core of NIO API is Channel and Buffer, where Channel is responsible for reading and writing data, and Buffer is responsible for storing data.

import java.net.InetSocketAddress;
      import java.nio.ByteBuffer;
      import java.nio.channels.SelectionKey;
      import java.nio.channels.Selector;
      import java.nio.channels.ServerSocketChannel;
      import java.nio.channels.SocketChannel;
      import java.util.Iterator;
      import java.util.Set;

      ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
      serverSocketChannel.socket().bind(new InetSocketAddress(8080));
      serverSocketChannel.configureBlocking(false);
      Selector selector = Selector.open();
      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
      while (true) {
          int readyChannels = selector.select();
          if (readyChannels == 0) continue;
          Set<SelectionKey> selectedKeys = selector.selectedKeys();
          Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
          while (keyIterator.hasNext()) {
              SelectionKey key = keyIterator.next();
              if (key.isAcceptable()) {
                  ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                  SocketChannel socketChannel = serverChannel.accept();
                  socketChannel.configureBlocking(false);
                  socketChannel.register(selector, SelectionKey.OP_READ);
              } else if (key.isReadable()) {
                  SocketChannel socketChannel = (SocketChannel) key.channel();
                  ByteBuffer buffer = ByteBuffer.allocate(1024);
                  socketChannel.read(buffer);
                  buffer.flip();
                  byte[] bytes = new byte[buffer.remaining()];
                  buffer.get(bytes);
                  String message = new String(bytes);
                  // 处理消息
              }
              keyIterator.remove();
          }
      }

The above is the best practice of network programming. By avoiding blocking IO, using thread pool to manage threads, and using NIO (New IO) API, the performance and scalability of the program can be improved.

Seven, Netty and MINA framework

7.1 What are Netty and MINA

Both Netty and MINA are network programming frameworks developed based on the Java language, which provide a high degree of reusability, scalability and performance. Netty is a NIO (New IO) client/server framework that makes it possible to quickly develop maintainable and high-performance protocol servers and clients. MINA is an event-driven asynchronous network framework, and its goal is to provide a high-performance and flexible server/client framework for network applications.

7.2 Advantages of Netty and MINA

Both Netty and MINA have the following advantages:

  • High reusability: Both Netty and MINA provide a set of easy-to-use and highly reusable APIs, enabling developers to quickly build efficient and easy-to-maintain network applications.
  • Scalability: Both Netty and MINA have good scalability, and can be customized and developed according to the needs of actual applications, so as to achieve higher performance and better reliability.
  • High performance: Both Netty and MINA use an asynchronous, non-blocking I/O model, which avoids the I/O thread blocking problem in the traditional blocking I/O model, thereby improving the performance of the application.
  • Multiple protocol support: Both Netty and MINA support multiple protocols, such as TCP, UDP, HTTP, etc., which can meet the needs of various applications.
  • Multi-platform support: Both Netty and MINA are cross-platform and can run on a variety of operating systems.

7.3 How to choose Netty or MINA

When choosing Netty or MINA, you need to choose according to the needs of specific applications. If you need to develop a high-performance protocol server or client and need to support multiple protocols, you can choose Netty. If you need to develop an event-driven asynchronous network application, you can choose MINA. At the same time, you can also consider the specific application scenarios and the development experience of the team to make a choice. No matter which framework you choose, you need to deeply understand its characteristics and usage methods, and optimize and debug it in combination with actual application scenarios to achieve the best performance and reliability.

Eight, the basic architecture of Netty and MINA

8.1 The basic architecture of Netty

The core of Netty is a set of highly reusable and easily extensible asynchronous event processing mechanisms. Its architecture includes the following components:

  • Channel: A channel for network transmission, through which data can be read and written, connection status and events can be monitored, and event handlers can be bound/unbound.
  • EventLoop: An event loop mechanism that processes all events and dispatches them to corresponding event handlers for processing.
  • ChannelPipeline: The event processor chain, which combines different event processors into a complete processing chain in an orderly manner.
  • ChannelHandler: An event handler that handles input/output events and executes specific business logic.

8.2 Basic Architecture of MINA

The core of MINA is an event-driven asynchronous I/O framework, and its architecture includes the following components:

  • IoSession: Represents a TCP/IP connection, which encapsulates the underlying network I/O operations and corresponding status information.
  • IoFilterChain: An event handler chain consisting of a series of IoFilters, each of which is responsible for processing specific types of events or modifying the properties of IoSession.
  • IoFilter: event processor, responsible for processing input/output events and executing specific business logic.
  • IoProcessor: Responsible for managing I/O operations, including reading, writing, accepting, and connecting.
  • IoAcceptor and IoConnector: Used on the server side and client side respectively, responsible for accepting connections and creating connections.

Nine, example demonstration

9.1 Using Netty to implement HTTP server

Using Netty to implement an HTTP server can greatly simplify the development and deployment of Web applications, because Netty provides a lightweight, high-performance HTTP programming framework. Below we will demonstrate how to use Netty to implement an HTTP server through a simple sample code.

We need to add Netty dependency in pom.xml file:

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.65.Final</version>
        </dependency>

HTTP server code:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class HTTPServer {

    private final int port;

    public HTTPServer(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new HTTPServerInitializer());

            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("Server started and listening on " + future.channel().localAddress());

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

    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: " + HTTPServer.class.getSimpleName() + " <port>");
            return;
        }

        int port = Integer.parseInt(args[0]);

        new HTTPServer(port).start();
    }
}

HTTP server initialization code:

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;

public class HTTPServerInitializer extends ChannelInitializer<SocketChannel> {

    private static final HttpServerCodec CODEC = new HttpServerCodec();
    private static final HttpObjectAggregator AGGREGATOR = new HttpObjectAggregator(1024 * 1024);
    private static final ChunkedWriteHandler CHUNKED_WRITE_HANDLER = new ChunkedWriteHandler();
    private static final HTTPServerHandler SERVER_HANDLER = new HTTPServerHandler();

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(CODEC);
        pipeline.addLast(AGGREGATOR);
        pipeline.addLast(CHUNKED_WRITE_HANDLER);
        pipeline.addLast(SERVER_HANDLER);
    }
}

HTTP server handler code:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;

import java.io.File;
import java.nio.file.Files;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class HTTPServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        String uri = request.uri();

        if (uri.equals("/")) {
            uri = "/index.html";
        }

        String path = "." + uri;

        File file = new File(path);

        if (file.exists() && file.isFile()) {
            byte[] bytes = Files.readAllBytes(file.toPath());
            ByteBuf buffer = Unpooled.wrappedBuffer(bytes);
          
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buffer);
            response.headers().set(CONTENT_TYPE, "text/html");
            response.headers().set(CONTENT_LENGTH, buffer.readableBytes());

            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    }

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

In the above sample code, we have used the ​HttpServerCodec​processors to​HttpObjectAggregator​ simplify the processing of HTTP requests and responses. In​ChunkedWriteHandler​ , we can process the received object, judge whether to read the file content according to the requested URI, and return the.​HTTPServerHandler​​FullHttpRequest​​ByteBuf​

It should be noted that the above sample code is just a simple HTTP server implementation, which is only suitable for learning and demonstration. In the production environment, we need to consider more issues such as security, performance and scalability, and optimize and configure accordingly.

In general, Java provides a rich network programming API that allows developers to easily implement various network applications. When implementing network applications, we need to pay attention to the specification of network protocols, deal with abnormal conditions of network connections, and ensure the security and reliability of network transmission.

If you think the content of this blog is helpful or inspiring to you, please follow my blog to get the latest technical articles and tutorials as soon as possible. At the same time, you are also welcome to leave a message in the comment area to share your thoughts and suggestions. Thank you for your support!

Guess you like

Origin blog.csdn.net/bairo007/article/details/132595896