一,基本格式:
1,主要jar包:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.30.Final</version>
</dependency>
2,主要程序
客户端:
package com.xl.test01.com.xl.nettytest01;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class nettyClient {
public static void main(String[] args) throws InterruptedException {
//客户端需要一个事件循环组
EventLoopGroup eventExecutors = new NioEventLoopGroup();
try {
//创建客户端启动对象,客户端使用的不是ServerBootstrap,而是BootStrap
Bootstrap bootstrap = new Bootstrap();
//设置相关参数
bootstrap.group(eventExecutors)//设置线程组
.channel(NioSocketChannel.class)//设置客户端通道的实现类
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new nettyClientHandle());
}
});
System.out.println("客户端 ok....");
//启动客户端连接服务器端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
//给关闭通道增加一个监听
channelFuture.channel().closeFuture().sync();
} finally {
eventExecutors.shutdownGracefully();
}
}
}
客户端处理器:
package com.xl.test01.com.xl.nettytest01;
import com.xl.test01.com.xl.myCar.NoBodyCar;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class nettyClientHandle extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client:" + ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("haha", CharsetUtil.UTF_8));
// ctx.writeAndFlush(setNoBodyCar());
}
//当通道有读取事件时会触发
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务器回复的消息" + buf.toString(CharsetUtil.UTF_8));
System.out.println("服务器的地址:" + ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
服务器端:
package com.xl.test01.com.xl.nettytest01;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class nettyServer {
public static void main(String[] args) throws InterruptedException {
//创建BossGroup和WorkGroup
//BossGroup负责连接,WorkerGroup负责客户端的业务处理
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup worherGroup = new NioEventLoopGroup();
try {
//创建服务器启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();
//使用链式编程进行设置
bootstrap.group(bossGroup, worherGroup) //设置两个线程组
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)//设置线程队列等待连接个数
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持请求连接状态
.childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象(匿名对象)
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new nettyServerHandle());
}
});//给我们的workgroup的EventLoop对应的管道设置处理器
System.out.println("服务器 is ready");
//绑定一个端口并且同步,生成一个ChannelFuture对象
//启动服务器,并绑定端口
ChannelFuture cf = bootstrap.bind(6668).sync();
//对关闭通道进行监听
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
worherGroup.shutdownGracefully();
}
}
}
服务器端处理器:
package com.xl.test01.com.xl.nettytest01;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/*
* 自定义一个Hander,需要继承netty,规定好的某个HandlerAdapter
*
* */
public class nettyServerHandle extends ChannelInboundHandlerAdapter {
//读取数据实际(这里我们可以读取客户端发送的消息)
/*
*1,ChannelHandlerContext ctx:上下文对象,含有管道pipeline,通道,地址
* 2,Object msg:客户端发送过来的数据
*
* */
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server ctx=" + ctx);
//将msg转成ByteBu f
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送的消息是:" + buf.toString(CharsetUtil.UTF_8));
//System.out.println("客户端发送的消息是:"+msg);
System.out.println("客户端地址是:" + ctx.channel().remoteAddress());
//数据读取完毕
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//writeAndFlush,代表写和刷新,及将数据写入到缓存并刷新
//对发送的数据进行编码
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端", CharsetUtil.UTF_8));
}
//处理异常,一般需要关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
二,我的项目:
说明:在该项目中,客户端为一个通过以太网的传送数据信息的数据采集器,我的电脑作为服务器端负责接收数据:
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.30.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling</artifactId>
<version>2.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jboss.marshalling/jboss-marshalling-serial -->
<!-- https://mvnrepository.com/artifact/org.jboss.marshalling/jboss-marshalling-serial -->
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling-serial</artifactId>
<version>2.0.9.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jboss.marshalling/jboss-marshalling -->
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling</artifactId>
<version>2.0.9.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/bcel/bcel -->
<dependency>
<groupId>bcel</groupId>
<artifactId>bcel</artifactId>
<version>5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/nlog4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>nlog4j</artifactId>
<version>1.2.25</version>
<scope>provided</scope>
</dependency>
</dependencies>
package com.xl.test01.com.xl3.nettyTest02;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Serve {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup worherGroup = new NioEventLoopGroup();
try{
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, worherGroup) //设置两个线程组
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)//设置线程队列等待连接个数
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持请求连接状态
.childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象(匿名对象)
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ServeHandle());
}
});//给我们的workgroup的EventLoop对应的管道设置处理器
System.out.println("服务器 is OK");
//绑定一个端口并且同步,生成一个ChannelFuture对象
//启动服务器,并绑定端口
ChannelFuture cf = bootstrap.bind(8020).sync();
//对关闭通道进行监听
cf.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
bossGroup.shutdownGracefully();
worherGroup.shutdownGracefully();
}
}
}
package com.xl.test01.com.xl3.nettyTest02;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.unix.Buffer;
import io.netty.util.CharsetUtil;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class ServeHandle extends ChannelInboundHandlerAdapter {
List<Byte> mylist = new LinkedList<Byte>();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
myFunction myFunction = new myFunction();
RespLoginCommand respLoginCommand = new RespLoginCommand();
ByteBuf buf = (ByteBuf) msg;
int bufLength = buf.readableBytes();
byte[] bytes = new byte[bufLength];
buf.getBytes(0, bytes);
for (int i = 0; i < bytes.length; i++) {
mylist.add(bytes[i]);
}
System.out.println("数据收到,下面处理数据,链表长度是: " + mylist.size());
if (mylist.size() > 100) {
List<Byte> newList = myFunction.getFullXDate(mylist);
System.out.println("newList从list中分析得到的长度为: " + newList.size() + " 值为: " + newList);
System.out.println("转换为16进制为:");
for(int ii=0;ii<newList.size();ii++){
System.out.println(Integer.toHexString(newList.get(ii)));
}
int i = myFunction.JudgeDate(newList);
System.out.println(i);
if (i == 1) {//发送登录请求命令
System.out.println("登录命令匹配成功,同意登录");
ctx.writeAndFlush(respLoginCommand.createLogRequestCode(ctx));
}
if (i == 2 || i == 3) {
System.out.println("登录命令匹配失败,");
ctx.writeAndFlush(Unpooled.copiedBuffer("登录命令匹配失败,请重新登录,", CharsetUtil.UTF_8));
}
if (i == 4) {//发送内容成功
System.out.println("发送内容接收成功");
for (int j = 13; j < newList.size(); j = j + 5) {
String combSS = new String();
for (int k = j + 3; k >= j; k--) {
String s = new String();
if (newList.get(k) >= 0) {
s = Integer.toHexString(newList.get(k) +51);
} else {//在不大于0的条件下,newlist的值转换为16进制为ffffff01,所以取后两位。
s = Integer.toHexString(newList.get(k) + 51).substring(Integer.toHexString(newList.get(k) + 51).length()-2);
}
// System.out.println(s);
if (s.length() == 1) {
s = "0".concat(s);
}
combSS = combSS.concat(s);
}
// System.out.println(combSS);
double temperature = myFunction.parseHex4(combSS.substring(0, 4));
double humidity = myFunction.parseHex4(combSS.substring(4, 8));
int n = (j - 13) / 5 + 1;
System.out.println("通道" + n + "的温度是" + humidity / 10 + " C");
System.out.println("通道" + n + "的湿度是" + temperature / 10 + "%");
System.out.println("**********************");
}
ctx.writeAndFlush(respLoginCommand.createContRequestCode(ctx));
}
if (i == 5 || i == 6) {
System.out.println("发送数据内容匹配失败,");
ctx.writeAndFlush(Unpooled.copiedBuffer("发送数据内容匹配失败,请重新登录,", CharsetUtil.UTF_8));
}
} else {
System.out.println("链表长度尚未满足,继续接收数据");
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
}
}
package com.xl.test01.com.xl3.nettyTest02;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
public class RespLoginCommand {
public ByteBuf createLogRequestCode(ChannelHandlerContext ctx) {
ByteBuf byteBuf = ctx.alloc().buffer();
byteBuf.writeByte(0x68);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x68);
byteBuf.writeByte(0x20);
byteBuf.writeByte(6);//长度
byteBuf.writeByte(0x33);
byteBuf.writeByte(0x1d);
byteBuf.writeByte(0x64);
byteBuf.writeByte(0xc3);
byteBuf.writeByte(0x37);
byteBuf.writeByte(0x73);
byteBuf.writeByte(0x647 & 0xff);
byteBuf.writeByte(0x16);
return byteBuf;
}
public ByteBuf createContRequestCode(ChannelHandlerContext ctx) {
ByteBuf byteBuf = ctx.alloc().buffer();
byteBuf.writeByte(0x68);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x88);
byteBuf.writeByte(0x68);
byteBuf.writeByte(0x20);
byteBuf.writeByte(6);//长度
byteBuf.writeByte(0x36);
byteBuf.writeByte(0x1d);
byteBuf.writeByte(0x64);
byteBuf.writeByte(0xc3);
byteBuf.writeByte(0x37);
byteBuf.writeByte(0x73);
byteBuf.writeByte(0x64A & 0xff);
byteBuf.writeByte(0x16);
return byteBuf;
}
public byte[] str2Bcd(String asc) {
int len = asc.length();
int mod = len % 2;
if (mod != 0) {
asc = "0" + asc;
len = asc.length();
}
byte abt[] = new byte[len];
if (len >= 2) {
len = len / 2;
}
byte bbt[] = new byte[len];
abt = asc.getBytes();
int j, k;
for (int p = 0; p < asc.length() / 2; p++) {
if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
j = abt[2 * p] - '0';
} else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
j = abt[2 * p] - 'a' + 0x0a;
} else {
j = abt[2 * p] - 'A' + 0x0a;
}
if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
k = abt[2 * p + 1] - '0';
} else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
k = abt[2 * p + 1] - 'a' + 0x0a;
} else {
k = abt[2 * p + 1] - 'A' + 0x0a;
}
int a = (j << 4) + k;
byte b = (byte) a;
bbt[p] = b;
}
return bbt;
}
}
package com.xl.test01.com.xl3.nettyTest02;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class myFunction {
/**
* @param list
* @return 返回一条完整的数据
*/
public List<Byte> getFullXDate(List<Byte> list) {
List<Byte> newList = new LinkedList<Byte>();
System.out.println("list长度为" + list.size());
System.out.println("list内容为" + list);
if (list.contains((byte) 0x16)) {
int i = list.indexOf((byte) 0x16);//有多个16返回值问题
while (i <= list.size()) {
if (list.get(i) == (byte) 0x16 && list.get(i + 1) == (byte) 0x68 && list.get(i + 2) == (byte) 0x88) { ;
newList.addAll(list.subList(0, i+1));//取前i条数据
for (int k = 0; k <newList.size();k++) {
list.remove(0);
}
//list.removeAll(newList);//删除的为list中newlist中所含的所有元素。
// Iterator<Byte> it = list.iterator();
// while(it.hasNext()){
// Byte x = it.next();
// if(x.equals("del")){
// it.remove();
// }
// }
break;
} else {
i++;
continue;
}
}
} else {
System.out.println("所发数据中不包含结束符16");
}
// for (int k = 0; k <= newList.size(); k++) {
// list.remove(k);
}
// System.out.println(newList);
// System.out.println(list);
// List<Byte> subList = new ArrayList<Byte>(list.subList(0,i));
// list.removeAll(subList);
// list.removeAll(newList);
// list=list.subList(i+2,list.size());
// System.out.println(list);
System.out.println("修改过的list的长度为: " + list.size() + "值为: " + list);
return newList;
}
/**
* @param
* @return 1, 登录命令匹配成功,同意登陆 2,登录命令内容匹配失败3,登录命令头部匹配失败
* 4,发送数据匹配寄接收成功 5,发送内容匹配失败 6,发送内容匹配失败
*/
public Integer JudgeDate(List<Byte> list) {
Byte[] bytes = new Byte[list.size()];
list.toArray(bytes);
int returnId = 0;
if (bytes[10] == (byte) 0x33 && bytes[11] == (byte) 0x1d) {
//判断帧头
if (bytes[0] == (byte) 0x68 && bytes[7] == (byte) 0x68) {
int j = 0;
for (int i = 1; i < 7; i++) {
if (bytes[i] == (byte) 0x88) {
j++;
continue;
} else {
break;
}
}
if (j == 6) {
//System.out.println("头部匹配成功");
// returnId = 1;
if (bytes[8] == (byte) 0xA0 && bytes[21] == (byte) 0x16) {
returnId = 1;
// System.out.println("经检验全部数据合法,同意登陆");
// ctx.writeAndFlush(respLoginCommand.createLogRequestCode(ctx));
// System.out.println("客户端发送成功");
} else {
returnId = 2;
// System.out.println("内容匹配失败");
}
} else {
returnId = 3;
// System.out.println("头部匹配失败");
}
} else {
returnId = 3;
//System.out.println("头部匹配失败");
}
} else
//判断标识码,是否为运行数据帧
if (bytes[10] == (byte) 0x36 && bytes[11] == (byte) 0x1d) {
if (bytes[0] == (byte) 0x68 && bytes[7] == (byte) 0x68) {
int j = 0;
for (int i = 1; i < 7; i++) {
if (bytes[i] == (byte) 0x88) {
j++;
continue;
} else {
break;
}
}
if (j == 6) {
// returnId = 1;
// System.out.println("头部匹配成功");
if (bytes[8] == (byte) 0xA0 && bytes[bytes.length - 1] == (byte) 0x16) {
returnId = 4;
// System.out.println("内容匹配成功");
// for (int i = 0; i < bufLength; i++) {
// System.out.println("接收到的数据为" + Integer.toHexString(bytes[i] - 0x33));
// }
// System.out.println("客户端发送运行数据成功,已收到");
} else {
returnId = 5;
// System.out.println("内容匹配失败");
}
} else {
returnId = 6;
//System.out.println("头部匹配失败");
}
} else {
returnId = 6;
// System.out.println("头部匹配失败");
}
} else {
returnId = 6;
}
return returnId;
}
//16进制转换为10进制
public double parseHex4(String num) {
List<String> list = new ArrayList<String>();
if (num.length() != 4) {
throw new NumberFormatException("Wrong length: " + num.length() + ", must be 4.");
}
int ret = Integer.parseInt(num, 16);
ret = ((ret & 0x8000) > 0) ? (ret - 0x10000) : (ret);
return (double) ret;
}
}