前言,很垃圾的。
一:服务端
1.springboot导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
</dependency>
2.单例管理类,持有连接的ChannelHandlerContext,发消息就靠他。
public class NettyManager {
private NettyManager(){}
private static volatile NettyManager nettyManager = null;
private ArrayList<ChannelHandlerContext> list = new ArrayList<>();
public static NettyManager getInstance(){
if(nettyManager==null){
synchronized (NettyManager.class){
if(nettyManager==null){
nettyManager = new NettyManager();
}
}
}
return nettyManager;
}
public void addContext(ChannelHandlerContext context){
list.add(context);
}
public ArrayList<ChannelHandlerContext> getList(){
return list;
}
}
3.连接处理类
@Slf4j
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 客户端连接会触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("Channel active......");
NettyManager.getInstance().addContext(ctx);
log.info("size of list:","ll"+String.valueOf(NettyManager.getInstance().getList().size()));
}
/**
* 客户端发消息会触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("服务器收到消息: {}", msg.toString()+":"+String.valueOf(NettyManager.getInstance().getList().size()));
var list = NettyManager.getInstance().getList();
for(int i =0;i<list.size();i++){
ChannelHandlerContext item = list.get(i);
item.write(msg.toString());
item.flush();
}
}
/**
* 发生异常触发
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
4.服务初始化器
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//添加编解码
socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
socketChannel.pipeline().addLast(new NettyServerHandler());
}
}
5.启动引导类
@Slf4j
public class NettyServer {
public void start(InetSocketAddress socketAddress) {
//new 一个主线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//new 一个工作线程组
EventLoopGroup workGroup = new NioEventLoopGroup(200);
ServerBootstrap bootstrap = new ServerBootstrap()
.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer())
.localAddress(socketAddress)
//设置队列大小
.option(ChannelOption.SO_BACKLOG, 1024)
// 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定端口,开始接收进来的连接
try {
ChannelFuture future = bootstrap.bind(socketAddress).sync();
log.info("服务器启动开始监听端口: {}", socketAddress.getPort());
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//关闭主线程组
bossGroup.shutdownGracefully();
//关闭工作线程组
workGroup.shutdownGracefully();
}
}
}
6.在程序入口启动服务,ip根据服务器填。
NettyServer nettyServer = new NettyServer();
nettyServer.start(new InetSocketAddress("ip", 8090));
二:客户端
1.导入依赖:implementation 'io.netty:netty-all:4.1.9.Final'
2.连接处理类,这里收到消息后通过EventBus发送消息。
class ChannelHandle: SimpleChannelInboundHandler<String>() {
override fun channelRead0(ctx: ChannelHandlerContext?, msg: String?) {
super.channelInactive(ctx)
Log.d("NettyClient","get the message "+msg);
EventBus.getDefault().post(Message(1,msg!!))
}
override fun channelActive(ctx: ChannelHandlerContext?) {
super.channelActive(ctx)
Log.d("NettyClient","NettyClient Active...");
}
override fun channelInactive(ctx: ChannelHandlerContext?) {
super.channelInactive(ctx)
Log.d("NettyClient","NettyClient Inactive...");
}
override fun userEventTriggered(ctx: ChannelHandlerContext?, evt: Any?) {
super.userEventTriggered(ctx, evt)
}
override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
super.exceptionCaught(ctx, cause)
cause!!.printStackTrace()
ctx!!.close()
}
override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
super.channelRead(ctx, msg)
Log.d("NettyClient","NettyClient get the message..."+msg.toString());
}
}
3.客户端初始化器
class NettyClientInitializer : ChannelInitializer<SocketChannel>() {
override fun initChannel(socketChannel: SocketChannel?) {
socketChannel!!.pipeline().addLast("decoder", StringDecoder())
socketChannel!!.pipeline().addLast("encoder", StringEncoder())
socketChannel!!.pipeline().addLast(ChannelHandle())
}
}
4.启动引导类,通过此类发送消息给服务器,ip对应好,本地服务器,可以ipconfig查看ipv4,不能用localhost或127.0.0.1,服务端也是。
class NettyClient {
private var channel: Channel?=null
fun sendMessage(string:String){
channel!!.writeAndFlush(string)
}
fun start() {
val group: EventLoopGroup = NioEventLoopGroup()
val bootstrap: Bootstrap = Bootstrap()
.group(group) //该参数的作用就是禁止使用Nagle算法,使用于小数据即时传输
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,3000)
.channel(NioSocketChannel::class.java)
.handler(NettyClientInitializer())
try {
val future: ChannelFuture = bootstrap.connect("ip", 8090).sync()
Log.d("nettyClient","客户端成功....")
//发送消息
channel = future.channel()
future.channel().writeAndFlush("你好啊")
// 等待连接被关闭
future.channel().closeFuture().sync()
}catch (e:ChannelException){
e.printStackTrace()
}
catch (e: InterruptedException) {
e.printStackTrace()
} finally {
group.shutdownGracefully()
Log.d("nettyClient","finally....")
}
}
}
5.启动客户端
var ss = object : Thread() {
override fun run() {
client = NettyClient()
client!!.start()
}
}
ss.start()
6.发送:
sendBtn.setOnClickListener(object:View.OnClickListener{
override fun onClick(v: View?) {
client!!.sendMessage(uuid.text.toString()+":"+name.text.toString())
}
})
7.接受显示:
@Subscribe(threadMode = ThreadMode.MAIN)
fun getMessageFromService(message:Message){
var df = SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss")
var time = df.format(Date(System.currentTimeMillis()))
chatText.append(time+message.content+"\n")
name.setText("")
}
到此结束,如果想点对点,可以加个uuid唯一标识,在消息里加就好了,服务端找到uuid对应的ChannelHandlerContext,只用它发消息就好了。不过这里没实现,有空在弄吧,不过这里已经实现了群聊,十分简陋,刚学,多见谅(狗头保命)。