目录
🥞以下是使用 CompletableFuture 实现异步处理客户端请求的示例代码:
🥞前言:
⛺摸鱼一下, 你路上捡到一百块,很开心,然后你发现后面还有一百块,还有一叠一百块,还有一箱一百块.....
🥞基础实现代码:
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<SocketChannel>() {
@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中配置证书和密钥,并在程序中正确加载和使用
🥞优化方案梳理:
- 使用异步IO来实现非阻塞IO,提高性能和并发能力。
- 使用Netty等高性能网络框架来简化开发和提高性能。
- 使用ZooKeeper等分布式协调服务来实现高可用和负载均衡。
- 使用Redis等缓存服务来提高性能和减轻数据库负载。
- 使用线程池来处理客户端连接和消息接收,提高并发性能和资源利用率。
- 使用SSL/TLS来加密通信,提高安全性。
- 使用WebSocket等协议来实现双向通信,提高实时性和交互性。
- 使用消息队列等技术来实现异步处理和解耦,提高可靠性和扩展性。