道理咱都懂,但是不会说,废话不多说,直接上代码。
Java
首先引入netty的包
<!-- Netty 引入netty,只需要这一个就足够了-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.25.Final</version>
</dependency>
服务端代码
package com.ruoyi.common.netty;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
* Netty
* 服务端
*/
@Component
@Configuration
public class NettyServer {
//handle已经添加
public void startServer() {
System.out.println("服务端启动成功");
//创建两个线程组,用于接收客户端的请求任务,创建两个线程组是因为netty采用的是反应器设计模式
//反应器设计模式中bossGroup线程组用于接收
EventLoopGroup bossGroup = new NioEventLoopGroup();
//workerGroup线程组用于处理任务
EventLoopGroup workerGroup = new NioEventLoopGroup();
//创建netty的启动类
ServerBootstrap bootstrap = new ServerBootstrap();
//创建一个通道
ChannelFuture f = null;
try {
bootstrap.group(bossGroup, workerGroup) //设置线程组
.channel(NioServerSocketChannel.class) //设置通道为非阻塞IO
.option(ChannelOption.SO_BACKLOG, 128) //设置日志
.option(ChannelOption.SO_RCVBUF, 32 * 1024) //接收缓存
.childOption(ChannelOption.SO_KEEPALIVE, true)//是否保持连接
.localAddress(new InetSocketAddress(61005))
.childHandler(new ChannelInitializer<SocketChannel>() {
//设置处理请求的逻辑处理类
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//ChannelPipeline是handler的任务组,里面有多个handler
ChannelPipeline pipeline = ch.pipeline();
LoggerFactory.getLogger(NettyServer.class).debug("新的链接进入...");
//心跳设置
pipeline.addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS));
pipeline.addLast("logging",new LoggingHandler(LogLevel.DEBUG));
pipeline.addLast("handler",new ServerInboundGetTimeHandler());
}
});
f = bootstrap.bind(61005).sync();//阻塞端口号,以及同步策略
f.channel().closeFuture().sync();//关闭通道
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//优雅退出
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
现在已经服务器开启了,下面开始设置handle处理类
package com.ruoyi.common.netty;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPatch;
import com.ruoyi.common.action.IAction;
import com.ruoyi.common.action.impl.LoginAction;
import com.ruoyi.common.action.impl.VersionAction;
import com.ruoyi.common.core.constant.NettyConstant;
import com.ruoyi.common.security.auth.AuthUtil;
import com.ruoyi.system.api.model.LoginUser;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Inbound处理类
* 给客户端返回一个时间戳
*/
@Configuration
public class ServerInboundGetTimeHandler extends ChannelInboundHandlerAdapter {
Logger logger = LoggerFactory.getLogger(ServerInboundGetTimeHandler.class);
IAction action;
/**
* 获取客户端的内容类
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//将传递过来的内容转换为ByteBuf对象
ByteBuf buf = (ByteBuf) msg;
//和文件IO一样,用一个字节数组读数据
byte[] reg = new byte[buf.readableBytes()];
buf.readBytes(reg);
//将读取的数据转换为字符串
String body = new String(reg, "UTF-8");
try{
处理body
}catch (Exception e){
logger.error("发送的消息错误");
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
NettyMap.remove(ctx);
ctx.close();
logger.error("链接异常断开");
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
//如果超时没有收到心跳会进入这里
if (evt instanceof IdleStateEvent) {//超时事件
IdleStateEvent idleEvent = (IdleStateEvent) evt;
if (idleEvent.state() == IdleState.READER_IDLE) {//读
NettyMap.remove(ctx);
ctx.close();
}
}
super.userEventTriggered(ctx, evt);
}
}
服务器代码已经写完了,下面开始客户端代码。
C#客户端代码
首先引入两个dll
1、DotNetty.dll
2、DotNetty.Extensions.dll
using BoardNetty.com.ruoyi.board;
using BoardNetty.com.ruoyi.entity;
using BoardNetty.com.ruoyi.response;
using DotNetty.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BoardNetty
{
public class NettyClient
{
private static NettyClient nettyClient = null;
private string serverIp;
private int port;
TcpSocketClient client = null;
Thread thread = null;
public static NettyClient GetNettyClient()
{
if (nettyClient == null)
{
nettyClient = new NettyClient();
}
return nettyClient;
}
#region 初始化服务器链接
public void init()
{
this.serverIp = "127.0.0.1";
this.port = 61005;
}
public void init(string serverIp)
{
this.serverIp = serverIp;
this.port = 61005;
}
public void init(int port)
{
this.serverIp = "127.0.0.1";
this.port = port;
}
public void init(string serverIp, int port)
{
this.serverIp = serverIp;
this.port = 61005;
}
#endregion
public void connection()
{
client = new TcpSocketClient(serverIp, port);
toevent();
}
public void heart()
{
try
{
thread = new Thread(() =>
{
while (true)
{
Thread.Sleep(5000);
var bytes = Encoding.UTF8.GetBytes("我是心跳");
if (client != null)
{
client.SendAsync(bytes);
}
}
});
thread.IsBackground = true;
thread.Start();
}
catch (Exception)
{
throw;
}
}
private void toevent()
{
client.OnPipeline(pipeLine =>
{
});
client.OnConnect(() =>
{
//链接成功之后默认进行登录
var bytes = Encoding.UTF8.GetBytes("hello i am C#");
client.SendAsync(bytes);
heart();
});
client.OnReceive(bytes =>
{
string msg = System.Text.UTF8Encoding.UTF8.GetString(bytes);
//处理逻辑(msg)
});
client.OnException(ex =>
{
Console.WriteLine("OnException:" + ex);
});
client.OnClose(ex =>
{
Console.WriteLine("OnClose:" + ex);
//restart
//client.ConnectAsync();
});
client.ConnectAsyncs();
}
}
}
好了现在就可以用了。
最后把dll放在下面