socket通讯,有两种方式,一种是建立长连接(TCP),建立后,不停的发送,接收。另外一种是建立短连接(UDP),建立连接,发送报文,接收响应,关闭连接。两种方式 server的开销不同。
今天出现问题:不管是创建TCP还是UDP连接,发送数据后,接收到数据的长度均为空值。
我的代码如下:
wireshark抓包查看到的结果(有接收到服务发送过来的数据):
运行时查看Loadrunner中打印日志:
多次修改代码后,最后修改接收的buffer 的长度,终于接收到数据
总结:之前看过一篇文章,如果接收到的数据长度不固定,可以指定buffer的数据长度为“*”。 现在才明白,原来 “*” 表示不接收数据 。花费一天的时间,终于搞定。
对于recvbuffer数据长度不一致的问题,或许可以参考http://blog.sina.com.cn/s/blog_6bdd0e1d0101b13r.html 这篇文章。copy 内容如下:
使用LoadRunner录制socket协议的脚本,会发现每个请求都会发送和接受一定长度的数据流,即send buffer和recv buffer;这两个buffer后面都会有个数字,这个数字表示buffer的长度,是一个固定的值。当做性能测试时,执行每次请求响应的数据很多时候是不定长的,如果recv buffer的长度与响应的数据长度不一致,脚本会报错,有两种方法可以解决这个问题:
1、造数据,使响应的数据长度在每次不同请求中都一样。但实际上,方法1是有局限性的,也就是说有些请求通过造数据也不能使响应的数据长度一致,那么我们可以采用方法2。
2、自定义函数,动态解析并接受不定长响应数据流。
以下详细介绍下方法2,以举例讲解的方式来介绍:
【业务场景】:用户进行登录操作,每次登录的响应数据由于被加密压缩后才返回的缘故,导致长度不一致。
【协议简介】:用户登录操作采取的协议是自定义协议,协议头中第5,6个byte保存的是整个响应流的长度。
【自定义函数的思路】:先接受响应数据中的前6个bytes,然后取5,6位上的字节转换成int类型,得到整个响应流的长度,从而计算出剩下未被接受的数据长度,再接受剩下的数据。
【代码实现】:
第一部分,录制后未经修改的脚本如下:
Action() { lr_start_transaction("login"); lrs_create_socket("socket6","TCP","RemoteHost=172.16.4.16:1122", LrsLastArg); //登录请求 lrs_send("socket6", "buf1",LrsLastArg); lrs_receive("socket6","buf2",LrsLastArg); lrs_close_socket("socket6"); lr_end_transaction("login",LR_AUTO); return 0; } send buf1 49 "\x00\x00" recv buf2 291 //每次请求的响应数据长度不一定是291个字节
#include "lrs.h" int custom_lrs_receive(char *sock_desc, char *buf_desc,void *dummy) { int rc; int buf_len = 6; char szBytesLength[30], *buf = NULL, *pszError, *pszLastChar; rc = lrs_receive_ex(sock_desc, buf_desc, "NumberOfBytesToRecv=6", LrsLastArg); if (rc != 0) //正常情况下函数返回为0,非0表示函数有错误 { lr_error_message("Receive 6 bytes failed. The error code = %d", rc); return -1; } //判断前6个字节是否接受成功 lrs_get_last_received_buffer(sock_desc, &buf, &buf_len); if (buf == NULL || buf_len != 6) { lr_error_message("receive of %s failed", buf_desc); return -1; } sprintf (szBytesLength, "NumberOfBytesToRecv=%d", fiFromHexBinToInt(buf) - 6); //调用另一个自定义函数:计算总长度的函数 lr_debug_message(LR_MSG_CLASS_FULL_TRACE, "!!!! Bytes length = %s", szBytesLength); rc = lrs_receive_ex(sock_desc, buf_desc, szBytesLength, LrsLastArg); if (rc != 0) return -1; return 0; } int fiFromHexBinToInt(char *szBuffer) { int i, j, iIntValue = 0, iExp = 1; //一个字节一个字节的取值,循环2次,分别取第6位,第5位上的字节 for( i = 1; i >= 0; i--) { iExp = 1; //从16进制字节流转换成int类型:2个byte4个bit,每个字节的低位分别需要乘以16的0次方和16的2次方; for (j = 2; j > i*2; j--) iExp *= 16; iIntValue += (szBuffer[i+4] & 0x0000000f) * iExp + ((szBuffer[i+4] & 0x000000f0) >> 4) * iExp * 16; } lr_output_message("the length is %d", iIntValue); return iIntValue; } Action() { lr_start_transaction("login"); lrs_create_socket("socket6", "TCP", "RemoteHost=172.16.4.16:1122", LrsLastArg); // 登录请求 lr_think_time(5); lrs_send("socket6", "buf1", LrsLastArg); custom_lrs_receive("socket6", "buf2", LrsLastArg); //自定义函数接受不定长数据流