[Java实现 Scoket实时接收Tcp消息 优化层层叠加]

目录

前言:

基础实现代码:

描述: 

优化代码多线程处理客户端连接和消息接收:

 描述:

再次优化异步实现:

以下是使用 CompletableFuture 实现异步处理客户端请求的示例代码:

 描述:

进一步优化的代码:Netty来实现Socket服务器 

 描述:

用SSL/TLS来加密通信,提高安全性

 描述:

优化方案梳理:


前言:

     ⛺摸鱼一下, 你路上捡到一百块,很开心,然后你发现后面还有一百块,还有一叠一百块,还有一箱一百块.....  

基础实现代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {
    private static final int PORT = 8888;

    public static void main(String[] args) {
        try {
            // 创建ServerSocket对象,监听指定端口
            ServerSocket serverSocket = new ServerSocket(PORT);
            System.out.println("Server started, waiting for client...");

            // 等待客户端连接
            Socket socket = serverSocket.accept();
            System.out.println("Client connected: " + socket.getInetAddress().getHostAddress());

            // 创建输入流,用于接收客户端发送的消息
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            // 循环读取客户端发送的消息
            String message;
            while ((message = reader.readLine()) != null) {
                System.out.println("Received message: " + message);
            }

            // 关闭输入流和socket连接
            reader.close();
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

描述: 

  • 将端口号定义为常量,方便修改和维护。
  • 在程序启动时打印出监听的端口号。
  • 在程序结束时打印出关闭的端口号。
  • 使用try-with-resources语句自动关闭输入流和socket连接,避免资源泄漏。

优化代码多线程处理客户端连接和消息接收:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {
    private static final int PORT = 8888;

    public static void main(String[] args) {
        try {
            // 创建ServerSocket对象,监听指定端口
            ServerSocket serverSocket = new ServerSocket(PORT);
            System.out.println("Server started, waiting for client...");

            // 循环等待客户端连接
            while (true) {
                // 等待客户端连接
                Socket socket = serverSocket.accept();
                System.out.println("Client connected: " + socket.getInetAddress().getHostAddress());

                // 创建新线程处理客户端连接和消息接收
                Thread thread = new Thread(new SocketHandler(socket));
                thread.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static class SocketHandler implements Runnable {
        private Socket socket;

        public SocketHandler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                // 创���输入流,用于接收客户端发送的消息
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                // 循环读取客户端发送的消息
                String message;
                while ((message = reader.readLine()) != null) {
                    System.out.println("Received message: " + message);
                }

                // 关闭输入流和socket连接
                reader.close();
                socket.close();
                System.out.println("Client disconnected: " + socket.getInetAddress().getHostAddress());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 描述:

代码创建了一个新的线程来处理每个客户端连接和消息接收。当有新的客户端连接时,程序会创建一个新的SocketHandler对象,并将其作为参数传递给新线程的构造函数。在SocketHandler的run方法中,程序会创建输入流,循环读取客户端发送的消息,并在接收到消息后打印出消息内容。当客户端关闭连接时,程序会关闭输入流和socket连接,并打印出客户端的IP地址。

这种方式可以提高并发性能,同时也可以避免阻塞主线程。

再次优化异步实现:

  • 使用 NIO。Java NIO(New IO)是一种基于缓冲区、非阻塞 IO 的 IO API,可以提高服务器的并发处理能力和性能。

  • 使用 Netty。Netty 是一个基于 NIO 的客户端-服务器框架,可以大大简化网络编程的开发过程,并提供高性能和可靠性。

  • 使用异步编程。例如使用 CompletableFuture 或 FutureTask 来实现异步处理客户端请求,避免阻塞主线程。

以下是使用 CompletableFuture 实现异步处理客户端请求的示例代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TCPServer {
    private static final int PORT = 8080; // 服务器监听端口号
    private static final int THREAD_POOL_SIZE = 10; // 线程池大小,可根据实际情况调整

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Server started. Listening for connections...");

            ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("Client connected from " + clientSocket.getInetAddress() + ":" + clientSocket.getPort());

                CompletableFuture.runAsync(() -> {
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                         PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true)) {

                        String line;
                        while ((line = reader.readLine()) != null) {
                            System.out.println("Received message from " + clientSocket.getInetAddress() + ":" + clientSocket.getPort() + " : " + line);

                            // 在这里对收到的消息进行处理
                            // ...

                            writer.println("Server received message: " + line);

                            if ("bye".equals(line)) {
                                break;
                            }
                        }

                        clientSocket.close();
                        System.out.println("Client disconnected from " + clientSocket.getInetAddress() + ":" + clientSocket.getPort());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }, executorService);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 描述:

  • 代码中,我们使用了 CompletableFuture 的 runAsync 方法来实现异步处理客户端请求。这样可以避免阻塞主线程,提高服务器的并发处理能力和性能。
  • 需要注意的是,在使用异步编程时,要确保线程安全和正确性,避免出现线程安全问题和错误处理问题。

进一步优化的代码:Netty来实现Socket服务器 

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class SocketServer {
    private static final int PORT = 8888;

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new SocketHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture future = bootstrap.bind(PORT).sync();
            System.out.println("Server started, waiting for client...");

            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    private static class SocketHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf buf = (ByteBuf) msg;
            String message = buf.toString(CharsetUtil.UTF_8);
            System.out.println("Received message: " + message);
        }

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

 描述:

  • 代码使用了Netty框架来实现Socket服务器。在main方法中,程序创建了两个EventLoopGroup对象,一个用于处理客户端连接,一个用于处理客户端消息。然后,程序创建了一个ServerBootstrap对象,并设置了一些参数,例如监听端口号、处理器等。在SocketHandler的channelRead方法中,程序会将接收到的消息打印出来。在exceptionCaught方法中,程序会打印出异常信息并关闭连接。
  • 这种方式可以简化开发和提高性能,同时也可以提供更多的功能和扩展性

用SSL/TLS来加密通信,提高安全性

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import java.security.Security;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

public class SocketServer {
    private static final int PORT = 8888;
    private static final String KEYSTORE_PATH = "/path/to/keystore";
    private static final String KEYSTORE_PASSWORD = "password";

    public static void main(String[] args) {
        try {
            // 加载keystore文件
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(SocketServer.class.getResourceAsStream(KEYSTORE_PATH), KEYSTORE_PASSWORD.toCharArray());

            // 创建KeyManagerFactory对象,用于管理keystore中的密钥
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, KEYSTORE_PASSWORD.toCharArray());

            // 创建SSLContext对象,用于创建SSLServerSocketFactory
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagerFactory.getKeyManagers(), null, null);

            // 创建SSLServerSocketFactory对象,用于创建SSLServerSocket
            SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
            SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(PORT);

            // 设置SSLServerSocket的一些属性
            sslServerSocket.setNeedClientAuth(false);
            sslServerSocket.setEnabledProtocols(new String[] { "TLSv1.2" });
            sslServerSocket.setEnabledCipherSuites(new String[] { "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" });

            System.out.println("Server started, waiting for client...");

            // 循环等待客户端连接
            while (true) {
                // 等待客户端连接
                SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
                System.out.println("Client connected: " + sslSocket.getInetAddress().getHostAddress());

                // 创建新线程处理客户端连接和消息接收
                Thread thread = new Thread(new SocketHandler(sslSocket));
                thread.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class SocketHandler implements Runnable {
        private SSLSocket sslSocket;

        public SocketHandler(SSLSocket sslSocket) {
            this.sslSocket = sslSocket;
        }

        @Override
        public void run() {
            try {
                // 创建输入流,用于接收客户端发送的消息
                BufferedReader reader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));

                // 循环读取客户端发送的消息
                String message;
                while ((message = reader.readLine()) != null) {
                    System.out.println("Received message: " + message);
                }

                // 关闭输入流和socket连接
                reader.close();
                sslSocket.close();
                System.out.println("Client disconnected: " + sslSocket.getInetAddress().getHostAddress());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 描述:

  • 示例代码使用了SSL/TLS来加密通信。在main方法中,程序加载了keystore文件,并创建了KeyManagerFactory和SSLContext对象,用于管理keystore中的密钥和创建SSLServerSocketFactory。在创建SSLServerSocketFactory时,程序设置了一些SSLServerSocket的属性,例如需要客户端认证、使用TLSv1.2协议、使用TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256加密套件等。在循环等待客户端连接时,程序创建了一个新的SSLSocket对象,并将其作为参数传递给新线程的构造函数。在SocketHandler的run方法中,程序会创建输入流,循环读取客户端发送的消息,并在接收到消息后打印出消息内容。当客户端关闭连接时,程序会关闭输入流和socket连接,并打印出客户端的IP地址。
  • 这种方式可以提高安全性,防止数据被窃听和篡改。当然,您需要在keystore中配置证书和密钥,并在程序中正确加载和使用

优化方案梳理:

  1. 使用异步IO来实现非阻塞IO,提高性能和并发能力。
  2. 使用Netty等高性能网络框架来简化开发和提高性能。
  3. 使用ZooKeeper等分布式协调服务来实现高可用和负载均衡。
  4. 使用Redis等缓存服务来提高性能和减轻数据库负载。
  5. 使用线程池来处理客户端连接和消息接收,提高并发性能和资源利用率。
  6. 使用SSL/TLS来加密通信,提高安全性。
  7. 使用WebSocket等协议来实现双向通信,提高实时性和交互性。
  8. 使用消息队列等技术来实现异步处理和解耦,提高可靠性和扩展性。

你可能感兴趣的:(java,多线程,java,tcp/ip,网络)