官网:
Netty 是一个 NIO 客户端服务器框架,可以快速轻松地开发协议服务器和客户端等网络应用程序。它极大地简化和精简了 TCP 和 UDP 套接字服务器等网络编程。
“快速简便”并不意味着最终的应用程序会存在可维护性或性能问题。Netty 的设计充分考虑了从实现许多协议(如 FTP、SMTP、HTTP 以及各种二进制和基于文本的旧协议)中获得的经验。因此,Netty 成功地找到了一种无需妥协即可实现易于开发、性能、稳定性和灵活性的方法。
需求:此案例为模仿C2C聊天案例,类似与IOS和Android进行聊天,通过服务器进行转发给客户端
实在不懂看下图
创建mave工程,引入坐标依赖
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class NettyC2CServer {
// 存储已连接的客户端 <客户端类型, Channel>
private static Map<String, Channel> clients = new ConcurrentHashMap<>();
public static void main(String[] args) throws Exception {
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
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new C2CServerHandler());
}
});
System.out.println("Netty C2C Server is starting...");
ChannelFuture channelFuture = bootstrap.bind(9008).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
// 处理客户端连接与消息
static class C2CServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// 假设消息格式为 "IDENTITY:客户端类型" 或 "MESSAGE:发送方:接收方:消息内容"
if (msg.startsWith("IDENTITY")) {
// 客户端发送标识消息,格式:IDENTITY:android 或 IDENTITY:ios
String[] parts = msg.split(":", 2);
if (parts.length == 2) {
String clientType = parts[1];
// 将客户端连接存储
clients.put(clientType, ctx.channel());
System.out.println(clientType + " connected.");
ctx.channel().writeAndFlush("Server acknowledged: " + clientType);
} else {
ctx.channel().writeAndFlush("Invalid IDENTITY format.");
}
} else if (msg.startsWith("MESSAGE")) {
// 消息格式:MESSAGE:sender:target:message
String[] parts = msg.split(":", 4);
if (parts.length == 4) {
String sender = parts[1];
String target = parts[2];
String message = parts[3];
System.out.println("Received message from " + sender + ": " + message);
// 如果目标客户端在线,发送消息
if (clients.containsKey(target)) {
clients.get(target).writeAndFlush("Message from " + sender + ": " + message);
} else {
ctx.channel().writeAndFlush("Target " + target + " is not online.");
}
} else {
ctx.channel().writeAndFlush("Invalid MESSAGE format. Use 'MESSAGE:sender:target:message'");
}
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client connected: " + ctx.channel().id().asShortText());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// 在客户端断开连接时,移除连接
clients.values().remove(ctx.channel());
System.out.println("Client disconnected: " + ctx.channel().id().asShortText());
}
}
}
bossGroup:负责接受客户端连接
workerGroup:负责处理连接后的读写操作
ChannelInitializer:在每个新的客户端连接时设置管道,添加StringDecoder、StringEncoder用于处理字符串数据,以及自定义的C2CServerHandler来处理消息
handlerAdded:
handlerRemoved:
clients
中移除该客户端的 Channel
。import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Scanner;
public class NettyAndroidClient {
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
//初始化渠道信息
@Override
protected void initChannel(Channel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println("Received: " + msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 客户端连接成功后,发送标识消息
ctx.writeAndFlush("IDENTITY:android");
System.out.println("Android client connected and sent identity.");
}
});
}
});
Channel channel = bootstrap.connect("localhost", 9008).sync().channel();
Scanner scanner = new Scanner(System.in);
// 模拟 Android 发送消息给 iOS
System.out.println("Type your message to send to iOS:");
while (scanner.hasNextLine()) {
String message = scanner.nextLine();
channel.writeAndFlush("MESSAGE:android:ios:" + message);
}
} finally {
group.shutdownGracefully();
}
}
}
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Scanner;
public class NettyIOSClient {
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println("Received: " + msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 客户端连接成功后,发送标识消息
ctx.writeAndFlush("IDENTITY:ios");
System.out.println("iOS client connected and sent identity.");
}
});
}
});
Channel channel = bootstrap.connect("localhost", 9008).sync().channel();
Scanner scanner = new Scanner(System.in);
System.out.println("Type your message to send to Android:");
while (scanner.hasNextLine()) {
String message = scanner.nextLine();
channel.writeAndFlush("MESSAGE:ios:android:" + message);
}
} finally {
group.shutdownGracefully();
}
}
}
客户端相同的特点如下解释:
group:用于管理客户端的 I/O 线程。
Bootstrap:
NioSocketChannel
),以及通道初始化逻辑。ChannelInitializer:
pipeline
),添加 StringDecoder
和 StringEncoder
处理字符串消息。SimpleChannelInboundHandler<String>
处理从服务器接收到的消息。channelRead0:
channelActive:
"IDENTITY:ios/
android"
给服务器,告知服务器这是一个 iOS /android客户端。案例运行结果:
喜欢的话点个关注,接下来会分享更多知识以及工作感受
需要项目工程私信(这么详细了应该可以写出来的)