pom引入
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.48.Final</version> </dependency>
创建四个文件
server、serverHandler
client、clientHandler
package com.kaige.admin.netty1;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import java.net.SocketAddress;
import java.util.Scanner;
/**
* <p>
* client
*
* @author wdz
* @since 2021-06-01
*/
@Slf4j
public class ChatClient {
public void run() {
// 创建线程组
NioEventLoopGroup group = new NioEventLoopGroup();
// 创建启动助手
Bootstrap b = new Bootstrap();
// 设置线程组
b.group(group);
// 设置客户端通道实现
b.channel(NioSocketChannel.class);
// 创建通道初始化对象
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
// 自定义handler 配置加载
ChannelPipeline pipeline = channel.pipeline();
// 编解码器要与handler中继承的父类泛型保持一致:SimpleChannelInboundHandler
// 添加解码器
pipeline.addLast("decoder",new StringDecoder());
// 添加编码器
pipeline.addLast("encoder",new StringEncoder());
// 自定义handler
channel.pipeline().addLast(new ChatClientHandler());
}
});
log.info("---------客户端已就绪-----------");
ChannelFuture localhost = null;
try {
localhost = b.connect("localhost", 8888).sync();
Channel channel = localhost.channel();
SocketAddress socketAddress = channel.localAddress();
log.info("---------IP:{}",socketAddress.toString());
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()){
String s = sc.nextLine();
channel.writeAndFlush(s);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 关闭链接
if (localhost != null) {
try {
localhost.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
group.shutdownGracefully();
log.info("------客户端-----关闭-------------");
}
}
public static void main(String[] args) {
new ChatClient().run();
}
}
package com.kaige.admin.netty1;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
/**
* <p>
* client h
*继承类发生变化,便于将编解码使用
* @author wdz
* @since 2021-06-01
*/
@Slf4j
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
/**
* 读取服务端广播信息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
log.info("----接收到服务器信息--:{}",msg);
}
}
package com.kaige.admin.netty1;
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 lombok.extern.slf4j.Slf4j;
/**
* <p>
* server
*
* @author wdz
* @since 2021-06-01
*/
@Slf4j
public class ChatServer {
private int port;
public ChatServer(int port) {
this.port = port;
}
public void run() {
// 创建线程组,接收客户端链接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 处理网络操作:IO
EventLoopGroup workGroup = new NioEventLoopGroup();
// 创建服务器端启动助手,配置服务端信息
ServerBootstrap sb = new ServerBootstrap();
// 设置线程组
sb.group(bossGroup, workGroup);
// 使用NioServerSocketChannel作为服务器端的通道实现
sb.channel(NioServerSocketChannel.class);
// 设置线程对列中等待链接数量
sb.option(ChannelOption.SO_BACKLOG, 128);
// 设置活动保持链接状态
sb.childOption(ChannelOption.SO_KEEPALIVE, true);
// 初始化通道
sb.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
// 自定义handler 配置加载
ChannelPipeline pipeline = channel.pipeline();
// 编解码器要与handler中继承的父类泛型保持一致:SimpleChannelInboundHandler
// 添加解码器
pipeline.addLast("decoder", new StringDecoder());
// 添加编码器
pipeline.addLast("encoder", new StringEncoder());
// 自定义handler
pipeline.addLast(new ChatServerHandler());
}
});
log.info("---------服务器端已就绪--------");
// 绑定端口,非阻塞
ChannelFuture sync = null;
try {
// 异步绑定,sync同步阻塞
sync = sb.bind(port).sync();
log.info("---------服务启动成功--------");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 关闭通道、线程组
if (sync != null) {
try {
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
log.info("------服务端-----关闭-------------");
}
}
public static void main(String[] args) {
new ChatServer(8888).run();
}
}
package com.kaige.admin.netty1;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* server h
* 服务器端业务处理类,处理服务器接收到的数据
* @author wdz
* @since 2021-06-01
*
*/
@Slf4j
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
public static List<Channel> channels = new ArrayList<>();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
// 将客户端通道存储
channels.add(channel);
log.info("----------客户端就绪----------:{}上线", channel.remoteAddress().toString());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channels.remove(channel);
log.info("----------客户端未就绪----------:{}离线", channel.remoteAddress().toString());
}
/**
* 重写读取数据方法
* @param ctx
* @param msg
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
log.info("服务端读取到信息:{}",msg);
Channel channel = ctx.channel();
// 给非当前用户通道进行广播(发送信息)
for (int i = 0; i < channels.size(); i++) {
Channel channel1 = channels.get(i);
if (channel1 != channel){
channel1.writeAndFlush(channel.remoteAddress()+"发送消息:"+msg);
}
}
}
/**
* 重写数据读取完毕方法
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
JSONObject object = new JSONObject();
object.put("msg","服务器数据读取完成返回信息");
object.put("code","200");
log.info("--------读取消息完成返回信息:{}",object.toJSONString());
ctx.writeAndFlush(Unpooled.copiedBuffer(object.toString(),CharsetUtil.UTF_8));
}
/**
* 重写异常发生
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 发生异常,关闭通道
log.info("----exceptionCaught----发生异常,关闭通道");
ctx.close();
}
}
先启动ChatServer 然后再启动ChatClient
idea 启动多个main方法配置:
客户端启动成功(多个)后,即可在控制台中输入聊天信息,实现简单聊天