jmeter BeanShell tcp 性能测试, 发送和接受数据详细示例

本文详细介绍了如何使用JMeter的TcpSampler进行TCP协议接口的性能测试,包括配置TCP信息,使用Beanshell处理不同类型的TCP请求和响应,以及在遇到问题时如何调试和解决,如解析十六进制数据和断言响应。同时,文中提到了代码示例和关键函数,如Parser类和data_viewer类,用于打包和解析TCP报文。
摘要由CSDN通过智能技术生成

jmeter 使用 beanshell 发送tcp请求实例

基于可能预测到的很近的未来,会有项目对程序中tcp协议的接口进行性能测试,为了避免到时候两眼一抹黑,在领导的压力下 oh 不,应该是自身积极主动的推进下,进行未雨绸缪的测试活动
在这里插入图片描述

tcp sample了解

  1. 新建一个tcp sample
    在这里插入图片描述

  2. 配置 tcp 信息
    在这里插入图片描述

  • TCPClient classname: 填写TCP报文格式(有三类),默认前缀:org.apache.jmeter.protocol.tcp.sampler.

    TCPClientImpl:普通文本传输
    BinaryTCPClientImpl:十六进制报文(常用)
    LengthPrefixedBinaryTCPClientImpl:继承BinaryTCPClientImpl类,并在 BinaryTCPClientlmpl前面增加两个字节数据长度。

    尝试了TCPClientImpl ,发现无法正常结束tcp接受,所以放弃,所以使用 BinaryTCPClientImpl

  • End of line(EOL) byte value:响应数据的最后2位,转换为10进制的值。取值区间[-128,127], 因为TCP长连接是不会断开的,我们需要从响应数据来判断并告知TCP取样器这次请求已经获得了数据/响应成功

  • SO_LINGER:该配置项用于控制在关闭连接之前是否要等待缓冲区中的数据发送完成。如果SO_LINGER选项指定了
    值,则在得到关闭连接的请求之后还会等待指定的秒数以完成缓冲区中数据的发送,在指定的SO_LINGER秒数完成后,
    关闭连接。因此,如果你把该选项设置成0,那么所有连接在收到关闭连接的时候都会立即关闭,避免产生很多处于
    TIME_WAIT状态的套接字。

  • 其他字段可以自行百度下

beanshell编辑

发送tcp信息时,由于body长度可能不一样,所以导致业务上最后拼接出来的16进制数据和我预期的不一致,所以此处只能采用代码的方式来拼接 16进制数据。
在这里插入图片描述
具体代码

import com.alibaba.fastjson.JSONObject;
public  static String test(){

    	   // 拼装请求的结构体
        JSONObject object = new JSONObject();
        object.put("uname", "autopf");
        object.put("keytp", "cc");
        object.put("apd", "1.00");
        object.put("seq", "abc_"+"${__threadNum}"+"${counter}");

        // 转成字符串
        String dicstr = JSONObject.toJSONString(object);
        System.out.println("请求体转成字符串结果: "+dicstr);

        vars.put("tcp_body",dicstr);
		// 这个Parser类 是我引用了开发的代码, 所以各位大佬,需要根据公司的不同情况,来拼接
        Parser sss = new Parser();
        //tcp中 command字段,每隔接口command不一样
        int cmd = 2;
        // 将结构体转成 byte格式
        byte[] s = dicstr.getBytes();
        System.out.println(s);
        // 调用接口获取 拼接后的 tcp 头和 tcp内容
        byte[] msg =  sss.pack(s, cmd);

        System.out.println(new String(msg));
        String tmpmsg = new String(msg);

	   // 将拼接后的tcp数据转成16进制
	   char[] _16 = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < msg.length;++i){
            sb.append(_16[msg[i]>>4&0xf]).append(_16[msg[i]&0xf]);
        }
	
        System.out.println("sb: " + sb.toString());
	   return sb.toString();
    }

  • ${xxx} 是jmeter变量格式,这个格式可以直接在java代码中使用。这样的话,请求参数的 参数化工作 也就可以解决了。
 object.put("seq", "abc_"+"${__threadNum}"+"${counter}");
  • 引入文件的时候,注意路径
    原来我写成这样,本来不会报错,但是写了 beanshell的语句后,就会报错
    source(“D:\mytestforJMTER\src\main\java\myjmeter2.java”);
    所以改成以下方式(本机环境:windows):
    source(“D:/mytestforJMTER/src/main/java/myjmeter2.java”);

  • 代码中的Parser类 是我引用了开发的代码, 所以各位大佬,需要根据公司的不同情况,来拼接最后的16进制数据

  • 导入相应的jar包
    我是直接把jar 包放到了jmeter目录下
    然后重启 jmeter, 就不会报错了
    在这里插入图片描述
    准备好beanshell后,就是发送啦

发送请求

引用 beanshell 中定义的 jmeter变量
在这里插入图片描述

断言

添加一个 响应断言, 将需要断言的内容手工转成16进制。
这样我们就完成了一个简单的tcp 请求和响应判断
在这里插入图片描述
哎,这个时候你又会有疑惑了,难道我断言每次都要看16进制,自己去拼接吗,那不累死个人
在这里插入图片描述
好吧,在外部环境的重压之下, 我又踏上了寻找真理的艰苦之路

获取tcp响应结果,beanshell转成字符串然后断言

在这里插入图片描述

  • 说实话,我看来看去 感觉也没有合适的取样器获取tcp结果。无奈只能使用正则取样器
    -随便写了个正则。 疑惑的是 .* 这个规则 可以执行,但是 .*+ 这个规则就无法执行。我也是纳闷了很久在这里插入图片描述
    就这样,我天真的认为我可以获取到了结果。结果获取到的是 null 。。。。
    在这里插入图片描述
    没有办法了,我的技能点都在这里用完了,接着只能去度娘的怀抱中寻找安慰了。发现很多大佬都说 最好开一个 debug 取样器
    好的,安排
    其实 我并不清楚 debug sample 和 debug postprocessor 之间有多大的区别,但这并不影响我们去尝试。
    在这里插入图片描述

  • 可以看到下图结果,猜测这应该也是 jmeter的内置变量,虽然正则没有获取到值,但是可以根据 这个调式器来确定 我们需要的返回值。
    tcp_result=null
    tcp_result_g=0
    tcp_result_g0=2b02xxxxx

  • 如果有大佬可以告诉我这是个啥原因,那就感激涕零了
    在这里插入图片描述
    好了,上一步已经获取到tcp请求的响应结果,接下来就是转换16进制的问题

beanshell处理返回的结果

和前面一样,新增beanshell 取样器,写java代码,然后将最后的结果返回给 自定义的 jmeter变量
在这里插入图片描述
当然,这里的parser 也是开发给我的。

最后,这里就可以正常使用断言来操作了,不需要在搞16进制了

注意

  1. 添加 debug 取样器
  2. 需要添加 使用的 java 文件,或者导入 响应的jar包
  3. beanshell 写的代码,如果报错但是 信息不明显,也可以添加 try catch
	try {
	    JsonClientUtil jcu=new JsonClientUtil();
	    ObjectNode node = JsonUtil.createObjectNode();
	}catch (Throwable ex) {
	    log.error("Beanshell failure: ", ex);
	    throw ex;
	}

在这里插入图片描述

拼接和转换16进制代码

  • myjmeter2.java, 代码可以参考
class data_viewer {

	public static void write_short_le(byte[] buf, int offset, short value) {
		buf[offset + 1] = (byte) ((value >> 8) & 0xff);//说明一
		buf[offset + 0] = (byte) ((value) & 0xff);
	}

	public static void write_int_le(byte[] buf, int offset, int value) {
		buf[offset + 3] = (byte) ((value >> 24) & 0xff);//说明一
		buf[offset + 2] = (byte) ((value >> 16) & 0xff);
		buf[offset + 1] = (byte) ((value >> 8) & 0xff);
		buf[offset + 0] = (byte) (value & 0xff);
	}

	public static void write_bytes(byte[] src, int src_offset, byte[] dst, int dst_offset) {
		for(int i = 0 ; i < src.length - src_offset; ++i) {
			dst[dst_offset +i] = src[src_offset + i];
		}
		//System.out.println("bytes.length: " + (src.length - src_offset));
		//System.arraycopy(src, src_offset, dst, dst_offset, src.length - src_offset);
	}

	public static short read_short_le(byte[] data, int offset) {
		int ret = (data[offset] | (data[offset + 1] << 8));
		return (short) ret;
	}

	public static int read_int_le(byte[] data, int offset) {
		int ret = ((data[offset]&0xFF) | ((data[offset + 1]&0xFF) << 8) | ((data[offset + 2]&0xFF << 16)) | ((data[offset + 3]&0xFF << 24)));
		return ret;
	}

}


class Parser {

	static final int HEAD_SIZE = 10;
	static final int TOTAL_SIZE = 14;
	int my_cmd;
	String my_json;

	public String parse(byte[] bytes) {
		int offset = 0;
		int plen = data_viewer.read_int_le(bytes, offset);
		offset += 4;//pkgLen
		offset += 4;//checkSum
		my_cmd = data_viewer.read_short_le(bytes, offset);
		offset += 2;//cmd
		offset += 2;//target
		offset += 2;//retCode
		int content_size = (plen - HEAD_SIZE);
		byte[] content_buf = new byte[content_size];
		data_viewer.write_bytes(bytes, offset, content_buf, 0);
		my_json = new String(content_buf);
		return my_json;
	}

	public static byte[] pack(byte[] content,int cmd) {
		int total_size = content.length + TOTAL_SIZE;
		int pkgLen = total_size - 4;
		int offset = 0;
		byte[] msg = new byte[total_size];
		data_viewer.write_int_le(msg, offset, pkgLen);
		offset += 4;//pkgLen
		data_viewer.write_int_le(msg, offset, 0);
		offset += 4;//checkSum
		data_viewer.write_short_le(msg, offset, (short)cmd);
		offset += 2;//cmd
		data_viewer.write_short_le(msg, offset, (short) 0);
		offset += 2;//target
		data_viewer.write_short_le(msg, offset, (short) 0);
		offset += 2;//retCode
		data_viewer.write_bytes(content, 0, msg, offset);
		return msg;
	}
}


参考文档

  1. https://blog.51cto.com/u_6183574/2511391
  2. https://www.cnblogs.com/xiyozhang136/p/7687760.html
  3. https://blog.csdn.net/slsunxia/article/details/107710547
  4. https://www.thinbug.com/q/39677810
  5. https://www.cnblogs.com/bf-blackfish/p/10579867.html
  6. https://www.cnblogs.com/hetutu-5238/p/10648116.html
  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值