RTU

最近项目分给我一个任务模拟写RTU的客户端程序,和一个产品的用户手册,所以没怎么更新。现在对这

段时间接触到的东西做个总结。

1. 打开页面时显示为下载页面。看到一个同事出这个错误。结果是头文件没写好
<%@page contentType="text/html" pageEncoding="GB2312"%>少了分号。

2. 设置JTable的列数。
DefaultTableModel tableModel = (DefaultTableModel) Table.getModel();
        tableModel.setRowCount(clientArray.size());
3. 在使用SocketChannel时容易犯的错误:
   1. 发送数据时:
     public void sendRequest(SelectionKey key) {
        SocketChannel channel = (SocketChannel) key.channel();
        Map<Integer, List<ByteBuffer>> writeCache = ServiceCache.getWriteCache();
        List<ByteBuffer> list = writeCache.get(this.getSiteAddr());
        if (list != null && !list.isEmpty()) {
            try {
//                System.out.println("写缓存有数据,发送数据");
//刚开始写时没写下面这条代码,然后再服务器端收不到数据。开始以为是发送不过去
                //调试了一段时间才发现是没有把指针置为0,在使用byteBuffer时尤为需要注意的点
//使用时要把指针置为0,或者flip();
                list.get(0).position(0);
                channel.write(list.get(0));
                list.remove(0);
            } catch (IOException ex) {
                Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
   2. 对byte[]做转换。
   ① 将整型转换成byte[],并制定其站几位。在协议里面确定好位数,比如功能码占两个字节!
public static byte[] intToByteArray(long res, int length) {

byte[] byteArray = new byte[length];
for (int i = 0; i < byteArray.length; i++) {
byteArray[i] = (byte) ((res >> ((byteArray.length - i - 1) * ) &

0xff);
}
return byteArray;
}
  ②   将byte[]转换成整型
public static byte[] intToByteArray(long res, int length) {

byte[] byteArray = new byte[length];
for (int i = 0; i < byteArray.length; i++) {
byteArray[i] = (byte) ((res >> ((byteArray.length - i - 1) * ) &

0xff);
}
return byteArray;
}
  等等;
4. 在做通讯协议是,有很多操作,读数据,写数据,处理数据,将传过来的包解包,将数组组包发送。

都需要一些列的处理。
①这做这些处理时可以引入缓存的概念。

  e.g.
private static Map<Integer, List<ByteBuffer>> readcache = new HashMap<Integer,

List<ByteBuffer>>();

该处声明了一个Map用来存放ByteBuffer以及接受该Bytebuffer的通讯端的标志号。这么做的好处在于比

如一个client端接收到服务器端发送过来的包时。对该包的处理不必在client中处理。只需要把收到的厨

具存到缓存中。当服务器端连续发送大量数据过来也不会等待。然后再起一个线程专门用来对readcache

中的数据进行解包处理。这样就把这两个步骤分离开来。

②对于数据包,的解包及组包的操作。把包声明为一个类,讲包头,功能码,校验码,包尾等等都变成类

的字段。字段的类型根据具体的协议来定。在转换时的难题在于位数要确定。比如协议规定包头占2为。

所以在做byte[]前两位转换成包头。组包时如果把String类型的包头转换成byte[],如何让byte[]也占两
位。

③ 在做缓存时,通常把缓存都声明在一个类中,然后为缓存添加getter和setter方法。要注意两点,其

一是对缓存修改时必须记得synchronized。防止读缓存和写缓存时有冲突。 其二是当map<>中封装的为基

本类型时,当你用set方法,是不会set成功的。虽然也知道传引用这么回事。这次还是调试了很久才发现


e.g.    
   public static void setStatus(Integer siteNum, Boolean state)
    {
        Boolean sta = status.get(siteNum);
        if (sta != null)
        {
            synchronized(sta)
            {
                sta = state;
            }
        }
        else
        {
            status.put(siteNum, state);
        }
    }
//该缓存为记录客户端的状态,在线或不在线。但是当调用set时,是改不掉其状态的。对于这种,直

接//用put操作将其放入map中即可

④ 一般协议的各个部分的字节都是定义好了的,所以我们将其字节在一个文件中定义出来。
    e.g.
public class FieldInfo {
    //帧头   
    public static final int HEAD_LEN = 1;
    //功能码
    public static final int FUNCTION_CODE = 1;
    //帧长度
    public static final int FRAME_LEN = 2;
    //流水号
    public static final int MSG_ID = 4;
    //站址
    public static final int STATION_ADDR = 2;
    //保留
    public static final int LEAVE = 2;
    //校验位
    public static final int CHECK_SUM = 1;
    //帧尾
    public static final int TAIL_LEN = 1;
}

⑤ 判断客户端是否在线的方法。客户端启动一个线程,不停的往写缓存中写入心跳包,然后服务器端收

到心跳包时给个心跳回应包。

5.Timer 类可以实现定时刷新。Timer time = new Timer(1000,new class(implements actionLisener))
然后再实现了ActionListener的方法中调用repaint方法。

你可能感兴趣的:(缓存,协议,socketChanne)