static class Server{ private int port; public Server(int port) { this.port = port; Arrays.fill(buffer, (byte) 0);//初始化设置为0 } public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { //ch.pipeline().addLast(new FixedLengthFrameDecoder(12) ); ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,4,2)); ch.pipeline().addLast(new MyInHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); // 绑定端口,开始接收进来的链接 //bind(b,9000); ChannelFuture f = b.bind(port).sync(); System.out.println("Server start listen at " + port ); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } }
其中的MyInHandler类是咱们实现Modbus协议的核心,咱们继续看。
二、MyInHandler类的实现
MyInHandler类是咱们处理ModbusTCP协议的基础,下面咱们来看看怎么实现这个类的。app
static class MyInHandler extends ChannelInboundHandlerAdapter{ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("---------------start process msg--------------------"); System.out.println("readable bytes is:"+byteBuf.readableBytes()); short TransActionId = byteBuf.readShort(); short protocal = byteBuf.readShort(); short msg_len = byteBuf.readShort(); byte slave_id = byteBuf.readByte(); byte funcotion_code = byteBuf.readByte(); if(funcotion_code ==4 )//若是功能码是4,也就是读请求,咱们要返回结果 { //输出 short start_address = byteBuf.readShort(); short ncount = byteBuf.readShort(); System.out.println("TransactionID is:"+ TransActionId); System.out.println("protocal id is:"+protocal); System.out.println("msg len is:"+msg_len); System.out.println("slave id is:"+slave_id); System.out.println("function code is:"+funcotion_code); System.out.println("start address is:"+start_address); System.out.println("count is:"+ncount); //返回响应消息报文 ByteBuf out = ctx.alloc().directBuffer(110); out.writeShort(0);//Transaction ID 2 out.writeShort(0);//protocal id 2 out.writeShort(95);//msg len 2 out.writeByte(1);//slave id 1 out.writeByte(4);//function code 1 //out.writeShort(0);//start address 2 out.writeByte(46);//46个寄存器 46*2 for(int i=0;i<92;i++) out.writeByte(buffer[i]); ctx.channel().writeAndFlush(out); } else if(funcotion_code == 0x10) { short start_address = byteBuf.readShort(); short nWords = byteBuf.readShort(); byte ncount = byteBuf.readByte(); //更新本地buffer for(int i=0;i<ncount;i++) buffer[start_address*2+i] = byteBuf.readByte(); //printMsg(); //返回响应消息 ByteBuf out = ctx.alloc().directBuffer(93); out.writeShort(0);//Transaction ID 2 out.writeShort(0);//protocal id 2 out.writeShort(0);//msg len 2 out.writeByte(1);//slave id 1 out.writeByte(0x10);//function code 1 out.writeShort(start_address);//46个寄存器 46*2 out.writeShort(ncount);//ncuont 2 ctx.channel().writeAndFlush(out); //System.out.println("response write success,write words is:"+out.readableBytes()); //out.release(); } else{ System.out.println("error function"); } //System.out.println("---------------end process msg--------------------"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 当出现异常就关闭链接 //cause.printStackTrace(); ctx.close(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("客户端已经链接!"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("客户端退出!"); ctx.close(); } private void printMsg(){ for(int i=0;i<200;i++){ if(i%20==0) System.out.println(); System.out.print( buffer[i]+" "); } } }
三、界面实现
为了方便调试,咱们这边实现了一个简单的界面。框架
static class NewFrame{ NewFrame(){} private void start(){ JFrame frame = new JFrame(); // 4.设置窗体对象的属性值:标题、大小、显示位置、关闭操做、布局、禁止调整大小、可见、... frame.setTitle("PSD-Test-Tool");// 设置窗体的标题 frame.setSize(400, 450);// 设置窗体的大小,单位是像素 frame.setDefaultCloseOperation(3);// 设置窗体的关闭操做;3表示关闭窗体退出程序;二、一、0 frame.setLocationRelativeTo(null);// 设置窗体相对于另外一个组件的居中位置,参数null表示窗体相对于屏幕的中央位置 frame.setResizable(false);// 设置禁止调整窗体大小 // 实例化FlowLayout流式布局类的对象,指定对齐方式为居中对齐,组件之间的间隔为5个像素 FlowLayout fl = new FlowLayout(FlowLayout.LEFT, 10, 10); // 实例化流式布局类的对象 frame.setLayout(fl); // 5.实例化元素组件对象,将元素组件对象添加到窗体上(组件添加要在窗体可见以前完成)。 // 实例化ImageIcon图标类的对象,该对象加载磁盘上的图片文件到内存中,这里的路径要用两个\ ImageIcon icon = new ImageIcon(""); // 用标签来接收图片,实例化JLabel标签对象,该对象显示icon图标 JLabel labIcon = new JLabel(icon); //设置标签大小 //labIcon.setSize(30,20);setSize方法只对窗体有效,若是想设置组件的大小只能用 Dimension dim = new Dimension(400,30); labIcon.setPreferredSize(dim); // 将labIcon标签添加到窗体上 frame.add(labIcon); //显示寄存器界面 final JTextArea registView = new JTextArea(); Dimension d = new Dimension(400,200); registView.setPreferredSize(d); frame.add(registView); // 实例化JLabel标签对象,该对象显示"帐号:" JLabel labName = new JLabel("地址:"); // 将labName标签添加到窗体上 frame.add(labName); // 实例化JTextField标签对象 final JTextField textName = new JTextField(); Dimension dim1 = new Dimension(350,30); //textName.setSize(dim);//setSize这方法只对顶级容器有效,其余组件使用无效。 textName.setPreferredSize(dim1);//设置除顶级容器组件其余组件的大小 // 将textName标签添加到窗体上 frame.add(textName); //实例化JLabel标签对象,该对象显示"密码:" JLabel labpass= new JLabel("值 :"); //将labpass标签添加到窗体上 frame.add(labpass); //实例化JPasswordField final JTextField textword=new JTextField(); //设置大小 textword.setPreferredSize(dim1);//设置组件大小 //添加textword到窗体上 frame.add(textword); //实例化JButton组件 JButton button=new JButton(); //设置按钮的显示内容 Dimension dim2 = new Dimension(150,30); button.setText("发送"); //设置按钮的大小 button.setSize(dim2); frame.add(button); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //textword.getAccessibleContext(); int register_index = Integer.parseInt(textName.getText()); int value = Integer.parseInt(textword.getText()); System.out.println("register_index="+register_index+";value="+value); if(register_index>200 || register_index<0) return; if(value>255 || value<0) return; buffer[register_index] =(byte)(value&0xff); //registView.setText(); //printMsg(); } }); frame.setVisible(true);// 设置窗体为可视化 new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { registView.setText(""); StringBuilder sb = new StringBuilder(); int time = 0; for (int i = 0; i < 200; i++) { if (i % 20 == 0) { int start = time * 20; int end = time * 20 + 19; sb.append("\r\n reg[" + start + "-" + end + "]"); if (time == 0) sb.append(" "); if (start < 100 && time > 0) sb.append(" "); time++; } if (i % 10 == 0) sb.append(" "); sb.append(Integer.toHexString(buffer[i]&0xff) + " ");//转换成16进制显示 } registView.setText(sb.toString()); } }).start(); } }
五、主模块ide
public static void main(String[] args) throws Exception{ NewFrame newFrame = new NewFrame(); newFrame.start(); Server server = new Server(9000); server.run(); }
四、运行结果oop
五、小结
这是初版代码,其中在MyHandler类能够继续提取代码。布局