网络编程 简易网络服务器 BIO / NIO
Socket / Netty
BIO
基于Socket实现服务器
单线程,多线程,线程池实现handler。
单线程
package server;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import server.handler.Handler;
public class SingleThreadServer {
public void start(int port) {
try (ServerSocket serverSocket = new ServerSocket(port)) {
Socket socket;
while ((socket = serverSocket.accept()) != null) {
new Handler(socket).run();
}
} catch (IOException exception) {
System.out.println(exception.getMessage());
}
}
public static void main(String[] args) {
SingleThreadServer singleThreadServer = new SingleThreadServer();
singleThreadServer.start(4567);
}
}
多线程
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import server.handler.Handler;
public class MultiThreadServer {
public void start(int port) {
try (ServerSocket serverSocket = new ServerSocket(port)) {
Socket socket;
while ((socket = serverSocket.accept()) != null) {
new Thread(new Handler(socket)).start();
}
} catch (IOException exception) {
System.out.println(exception.getMessage());
}
}
public static void main(String[] args) {
MultiThreadServer server = new MultiThreadServer();
server.start(4568);
}
}
线程池
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.handler.Handler;
public class MultiThreadPoolServer {
private static final Logger logger = LoggerFactory.getLogger(MultiThreadPoolServer.class);
public void start(int port) {
logger.info("server start");
ExecutorService threadPool = Executors.newFixedThreadPool(20); // 创建包含10个线程的线程池
try (ServerSocket serverSocket = new ServerSocket(port)) {
Socket socket;
while ((socket = serverSocket.accept()) != null) {
threadPool.execute(new Handler(socket));
}
} catch (IOException exception) {
System.out.println(exception.getMessage());
}
}
public static void main(String[] args) {
MultiThreadPoolServer server = new MultiThreadPoolServer();
server.start(4569);
}
}
Handler
package server.handler;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Handler implements Runnable {
private final Socket socket;
private static final Logger logger = LoggerFactory.getLogger(Handler.class);
public Handler(Socket socket) {
this.socket = socket;
}
public void run(){
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream())
) {
// 接收来自客户端的数据
byte[] buffer = new byte[1024];
int bytesRead = bufferedInputStream.read(buffer);
String message = new String(buffer, 0, bytesRead);
logger.info("Received from "+ socket.getRemoteSocketAddress().toString() + ": " + message);
Thread.sleep(100); // 模拟操作
// 发送数据到客户端
bufferedOutputStream.write(message.getBytes());
bufferedOutputStream.flush();
} catch (InterruptedException | IOException e) {
throw new RuntimeException(e);
}
}
}
NIO
基于netty实现多线程(线程池)服务器
package server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import server.handler.NettyHandler;
public class NettyThreadPoolServer {
private int port;
public NettyThreadPoolServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
EventExecutorGroup group = new DefaultEventExecutorGroup(200); // 创建EventExecutorGroup
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(group, new NettyHandler()); // 使用EventExecutorGroup
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
group.shutdownGracefully(); // 关闭EventExecutorGroup
}
}
public static void main(String[] args) throws Exception {
int port = 4570;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new NettyThreadPoolServer(port).run();
}
}
NettyHandler
package server.handler;
import io.netty.buffer.*;
import io.netty.channel.*;
import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(NettyHandler.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
try {
String mess = in.toString(StandardCharsets.UTF_8);
logger.info("Received from " + ctx.channel().remoteAddress().toString() + ": " + mess);
Thread.sleep(100);
ctx.write(Unpooled.copiedBuffer(mess.getBytes()));
ctx.flush();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
in.release(); // 清空release
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
测试客户端
根据需求可以修改不同的port和ip地址
package client;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestClient {
private static final int NUM_REQUESTS = 500; // 请求数量
private static final int NUM_CONCURRENT_CONNECTIONS = 100; // 服务器并行连接数
private static final int TEST_DURATION = 1000; // 测试最长持续时间(秒)
private static final AtomicInteger completedRequests = new AtomicInteger(); // 请求成功数
private static final Logger logger = LoggerFactory.getLogger(TestClient.class);
private int port;
private String ip;
public TestClient(String ip, int port) {
this.ip = ip;
this.port = port;
}
public void test() throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(NUM_CONCURRENT_CONNECTIONS);
String message = "test";
long startTime = System.currentTimeMillis();
for (int i = 0; i < NUM_REQUESTS; i++) {
threadPool.execute(() -> {
// 重试异常请求
while(true){
try (Socket socket = new Socket(ip, port);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
BufferedInputStream in = new BufferedInputStream(socket.getInputStream())) {
out.write(message.getBytes(StandardCharsets.UTF_8));
out.flush();
byte[] buffer = new byte[1024];
int bytesRead = in.read(buffer);
String mess = new String(buffer, 0, bytesRead);
// logger.info("Received from " + socket.getRemoteSocketAddress().toString() + ": " + mess);
completedRequests.incrementAndGet();
break;
} catch (IOException e) {
// logger.error(e.getMessage());
}
}
});
}
threadPool.shutdown();
threadPool.awaitTermination(TEST_DURATION, TimeUnit.SECONDS);
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
int totalRequests = completedRequests.get();
completedRequests.set(0);
System.out.println("Num of Requests: " + totalRequests);
System.out.println("Duration: " + duration + " ms");
System.out.println("QPS: " + (totalRequests / (double) duration) * 1000);
}
public static void main(String[] args) throws InterruptedException {
TestClient client1 = new TestClient("127.0.0.1", 4567);
client1.test();
TestClient client2 = new TestClient("127.0.0.1", 4568);
client2.test();
TestClient client3 = new TestClient("127.0.0.1", 4569);
client3.test();
TestClient client4 = new TestClient("127.0.0.1", 4570);
client4.test();
}
}
Maven文件
仅包含依赖部分
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>1.7.25</slf4j.version>
</properties>
<!-- https://mvnrepository.com/artifact/com.guicedee.services/sl4j -->
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.100.Final</version>
</dependency>
</dependencies>