(转)JAVA socket 进行十六进制报文交互测试

Wesley13
• 阅读 708
 1 import java.io.IOException;  
 2 import java.io.InputStream;  
 3 import java.io.OutputStream;  
 4 import java.io.PrintWriter;  
 5 import java.net.ServerSocket;  
 6 import java.net.Socket;  
 7   
 8 public class ServerTest {  
 9   
10     private ServerSocket ss;  
11     private Socket socket;  
12     PrintWriter out;  
13     private int i = 0;  
14   
15     public ServerTest() {  
16         try {  
17             ss = new ServerSocket(7838);  
18             while (true) {  
19                 System.out.println(0);  
20                 socket = ss.accept();  
21                 ss.setSoTimeout(50000);  
22                 byte[] b = new byte[1024];  
23                 b[0] = (byte) 0x55;  
24                 b[1] = (byte) 0xAA;  
25                 b[2] = (byte) 0x01;  
26                 b[3] = (byte) 0x00;  
27                 b[4] = (byte) 0x00;  
28                 b[5] = (byte) 0x00;  
29                 b[6] = (byte) 0x00;  
30                 b[7] = (byte) 0x00;  
31                 b[8] = (byte) 0x00;  
32                 b[9] = (byte) 0x00;  
33                 b[10] = (byte) 0x00;  
34                 b[11] = (byte) 0x00;  
35                 b[12] = (byte) 0x00;  
36                 b[13] = (byte) 0x00;  
37                 b[14] = (byte) 0x00;  
38                 b[15] = (byte) 0x00;  
39                 b[16] = (byte) 0x00;  
40   
41                 InputStream socketReader = socket.getInputStream();  
42                 OutputStream socketWriter = socket.getOutputStream();  
43                 socketWriter.write(b);  
44                 System.out.println("OK");  
45                 socketWriter.flush();  
46                 i = i + 1;  
47                 out.flush();  
48             }  
49         } catch (IOException e) {  
50             e.printStackTrace();  
51         }  
52     }  
53   
54     public static void main(String[] args) {  
55         new ServerTest();  
56     }  
57 }

上面是我百度的JAVA socket 发送十六进制数据的代码,通过这个思路,我进行了一些抽象:

因为我需要模拟多个报文,每个报文都用字节流拆分比较费时间,从网上查询到把一个字符串直接拆分成字节流的方法:

 1 public class socketWriter {  
 2     public static byte[] hexStringToBytes(String hexString) {  
 3         if (hexString == null || hexString.equals("")) {  
 4             return null;  
 5         }  
 6         // toUpperCase将字符串中的所有字符转换为大写  
 7         hexString = hexString.toUpperCase();  
 8         int length = hexString.length() / 2;  
 9         // toCharArray将此字符串转换为一个新的字符数组。  
10         char[] hexChars = hexString.toCharArray();  
11         byte[] d = new byte[length];  
12         for (int i = 0; i < length; i++) {  
13             int pos = i * 2;  
14             d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));  
15         }  
16         return d;  
17     }  
18     //charToByte返回在指定字符的第一个发生的字符串中的索引,即返回匹配字符  
19     private static byte charToByte(char c) {  
20         return (byte) "0123456789ABCDEF".indexOf(c);  
21     }  
22 }

因为我需要对输入流的数据进行解析,所以写了一个公共方法,把字节流转换成字符串:

// 字节的转换  
public class ByteUtil {  
    //将字节数组转换为short类型,即统计字符串长度  
    public static short bytes2Short2(byte[] b) {  
        short i = (short) (((b[1] & 0xff) << 8) | b[0] & 0xff);  
        return i;  
    }  
    //将字节数组转换为16进制字符串  
    public static String BinaryToHexString(byte[] bytes) {  
        String hexStr = "0123456789ABCDEF";  
        String result = "";  
        String hex = "";  
        for (byte b : bytes) {  
            hex = String.valueOf(hexStr.charAt((b & 0xF0) >> 4));  
            hex += String.valueOf(hexStr.charAt(b & 0x0F));  
            result += hex + " ";  
        }  
        return result;  
    }  
}  

在模拟客户端发送报文中去调用该方法即可,则可以实现只需要传入字符串,完成客户端与服务器的交互,并完成输入流的字符串转换,

代码如下:

import java.net.Socket;  
import Base.ByteUtil;  
import Base.socketWriter;  
import java.io.InputStream;  
import java.io.OutputStream;  
  
public class ClientLaunch {  
    // 这个客户端连接到地址为xxx.xxx.xxx.xxx的服务器,端口为10020,并发送16进制到服务器,然后接受服务器的返回信息,最后结束会话。  
    // 客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。  
    public String Client(Socket socket, socketWriter s, InputStream socketReader, String strInput) {  
        String strOutput = "";  
        try {  
            //客户端输出流作为服务器的输入  
            OutputStream socketWriter = socket.getOutputStream();  
            socketWriter.write(s.hexStringToBytes(strInput));  
            socketWriter.flush();  
            Thread.sleep(1000);  
  
            //服务器的输出即为客户端的输入,这里主要是为了把服务器输出的字节流报文转化成字符串,方便进行解析,最终测试报文的正确性  
            socketReader = socket.getInputStream();  
            //因为我测试的报文包含报文头和报文体,这里的字节数组长度37为报文头长度  
            byte[] temp = new byte[37];  
            int bytes = 0;  
            /* read从输入流socketReader中读取temp(37)数量的字节数,并将它们存储到缓冲区数组temp。实际读取的字节数作为一个整数37返回。 
             * 此方法块,直到输入数据可用,检测到文件结束,或抛出异常。如果B的长度为零,则没有读取字节数和返回0; 
             * 否则,将有一个至少一个字节的尝试。如果没有可用的字节,因为流是在文件的结尾,值- 1返回;否则,至少一个字节被读取和存储到temp。 
            */  
            bytes = socketReader.read(temp);  
            if (bytes != 37) {  
                return null;  
            }  
            strOutput += ByteUtil.BinaryToHexString(temp);  
              
            //读取报文体的内容  
            byte[] array = new byte[2];  
            for (int i = 1; i < 3; i++) {  
                array[i - 1] = temp[i];  
            }  
            int let = ByteUtil.bytes2Short2(array);  
            array = new byte[let];  
            bytes = socketReader.read(array);  
            if (bytes != let) {  
                return null;  
            }  
            strOutput += ByteUtil.BinaryToHexString(array);  
            //把字符串中“ ”去掉  
            strOutput = strOutput.replaceAll(" ", "");  
  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return strOutput;  
    }  
}  

这样就完成了报文数据传送,以及读取的基础代码,调用它们即可完成交互,如测试报文数据的处理:

public class CardFlow {  
    ClientLaunch cl = new ClientLaunch();  
    Socket socket;  
    socketWriter socketWriter = new socketWriter();  
    InputStream socketReader;  
  @Test  
  public void cardFlow() throws Exception {  
        // 建立 socket链接,连接服务器192.168.1.1:8080  
        socket = new Socket("192.168.1.1", "8080");  
          
        String charger="00000000136";  
        //登陆,通过验证输出报文中的响应报文是否为 "000000",判断是否登陆成功,其中loginInput为输出报文字符串  
        String loginInput="283400120001"+charger+"10002016112214151800000000321321233212FFFFFFFFFFFFFFFF00010201";  
        String outlogin = cl.Client(socket, socketWriter, socketReader, loginInput);  
        Assert.assertEquals(outlogin.substring(30,36), "000000");  
          
        //心跳,通过验证输出报文中的响应报文是否为 "000000",判断是否交互成功  
        String heartbeatInput="283400130001"+charger+"1000201611221415180000";  
        String outheartbeat = cl.Client(socket, socketWriter, socketReader, heartbeatInput);  
        Assert.assertEquals(outheartbeat.substring(30,36), "000000");  
          
        socket.close();  
  }  
}  

则完成socket报文交互测试的实现流程:

1. 读取协议配置

2. 替换可变长度和值

3. 计算包体长度

4. 包头+包长度+包体+包尾

5. 建立到192.168.1.1:8080的 socket连接

6. 发送数据包

7. 等待接收返回数据包(超时)

8. 解析返回数据包

9. 得到定位值1和定位值2(便于验证)

该方法完成后,可以进行基于协议的压力测试:

针对频繁请求和处理的协议(不频繁默认无问题),自动生成多测试数据,多客户端多线程长时间进行协议测试,不断加压,统计请求响应时间变化、失败和成功的数据及比例,并收集服务器性能参数和资源消耗等数据(可自动出图),手动检查和数据结果分析。

附:通用自动化测试工具效果

1. 主体框架实现特定功能,高级语言完成,并开放大量实用API,且不断增加和完善

2. 嵌套或封装一种或多种脚本语言解析器,能够动态执行测试用例脚本,对Windows窗体、Web、代码、接口、协议或性能等若干方面进行测试(扩展:录制功能)

3. 流程完善,能够准确高效的解放人力和深入测试,手工尽可能少的参与或专注于测试用例脚本工作

4. 可扩展性强,能够远程操控多个多种平台(分布集群,通过网络通信、协议通信等),能够并行调度执行,可配置可存储,资源共享方便

5. 自动化框架工作:检测新的版本-->下载、编译、批量部署-->调用指定测试脚本执行测试-->邮件或消息通知QA测试结果报告路径和发现的bug。

6. 手工工作:编写修改测试脚本并上传、收邮件校验bug

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这