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類能夠繼續提取代碼。佈局