在工作中HTTP协议是使用的最多的,但除了这种协议外还有一些其它的协议也会被使用,特别是Windows Sockets协议,它是一个底层协议,银行业务经常会使用到这种协议进行录制脚本。还有一种被常用到的协议就是邮件服务协议,对于性能测试邮件系统即需要使用到这种协议。本章节主要讲解“Windows Sockets(WinSock)协议”。
一、Windows Sockets(WinSock)协议
WinSock协议是一个底层协议。所有的高级协议(如FTP、HTTP协议等),以及所有基于Windows的应用(如IE、FTP),其底层通信都是使用WinSock协议,因此任何高级协议的底层都使用WinSock通信。那么什么时候才选择WinSock协议呢?如果在录制脚本前,找不到更合适的协议时,都可以选择WinSock协议进行录制脚本。WinSock的另一个特点就是非常适合应用程序代码级,所以需要查看缓冲区发送和接收的实际数据时,也可以选择WinSock协议。
使用WinSock协议开发脚本的过程如下,如图12-1所示。
图12-1 WinSock协议脚本开发过程
1、Windows Sockets录制选项设置
选择Tools→Recording Options或在Start Recording对话框中选择Options,都弹出如图12-2所示的Recording Options对话框。
图12-2 WinSock选项对话框
01、配置转换表
需要以EBCDIC格式显示数据,请在录制选项中指定转换表。通过转换表可以指定录制会话的格式。这适用于大型机或AS/400服务器上运行的用户。服务器和客户端计算机都从系统上所安装的转换表中确定数据的格式。可以在Translation tables下拉列表框中选择转换选项。其中前四位表示服务器的格式,后四位表示客户端的格式。例如,002504e4表示服务器格式为0025,客户端格式为04e4。
注意:如果数据是ASCII格式,则不需要转换。此时必须选择默认的None。如果确实选择了转换表,则VuGen将转换该ASCII数据。
02、排除套接字
VuGen支持“排除套接字”功能,可以从录制会话中排除特定的套接字。要从脚本中排除某个套接字的所有操作,请在“排除套接字”列表中指定该套接字的地址。要向列表中添加套接字,点击该框右上角的加号,然后以下列格式之一输入套接字地址,如表12-1所示。
表12-1 排除套接字表
这样可以将多个主机或端口添加到排除套接字列表中。要从排除列表中删除套接字,请选择该套接字地址,然后点击该框右上角的减号。
默认情况下,VuGen不记录在“已排除套接字列表”中的套接字的操作,如果需要指示VuGen记录已排除的套接字操作,请清除Do not include excluded socket in log复选框。如果已排除的套接字启用了日志记录,那么在日志文件中,这些套接字的操作之前会加上Exclude字样。
03、设置思考时间阈值
在录制期间,VuGen会自动插入操作者的思考时间。可以设置阈值级别,录制的思考时间如果低于此阈值,则被忽略。如果录制的思考时间超出了阈值级别,VuGen将在LRS函数之前放置lr_think_time语句。如果录制的思考时间低于阈值级别,则不会生成lr_think_time语句。
2、Windows Sockets录制
前面已经讲述了使用Windows Sockets协议开发的过程,下面结合实例对这个开发过程进行详细讲解。
01、录制脚本
首先录制一段脚本,被录制的程序主要功能是通过网络将数据从一台机器传输到另一台机器。下面是录制好的代码,后面所有关于这一章节的讲解都是围绕这段脚本来进行。
Action()
{
lrs_create_socket("socket0", "TCP", "LocalHost=0", "RemoteHost=58.222.18.90:80", LrsLastArg);
lrs_send("socket0", "buf0", LrsLastArg);
lrs_receive("socket0", "buf1", LrsLastArg);
lr_think_time(8);
lrs_create_socket("socket1", "UDP", "LocalHost=8080", LrsLastArg);
lrs_send("socket1", "buf2", "TargetSocket=edan-3a89b690e0:8080", LrsLastArg);
lrs_receive("socket1", "buf3", LrsLastArg);
return 0;
}
data.ws文件中的内容如下:
;WSRData 2 1
send buf0 201
"GET /download/cmsoft/UpgradeInfo/NetAssist.inf HTTP/1.1\r\n"
"If-Modified-Since: Fri, 03 Jul 2009 03:57:53 GMT\r\n"
"If-None-Match: \"e8ffa77092fbc91:633a6\"\r\n"
"User-Agent: Internet+Explorer\r\n"
"Host: www.cmsoft.cn\r\n"
"\r\n"
recv buf1 297
"HTTP/1.1 304 Not Modified\r\n"
"Date: Tue, 14 Jul 2009 12:25:16 GMT\r\n"
"Content-Location: http://www.cmsoft.cn/download/cmsoft/UpgradeInfo/NetAssi"
"st.inf\r\n"
"Last-Modified: Fri, 03 Jul 2009 03:57:53 GMT\r\n"
"Accept-Ranges: bytes\r\n"
"ETag: \"e8ffa77092fbc91:
"Server: Microsoft-IIS/6.0\r\n"
"X-Powered-By: ASP.NET\r\n"
"\r\n"
send buf2 5
"hello"
recv buf3 5
"hello"
-1
02、增强脚本
增强脚本部分主要是对脚本进行插入开始事务、结束事务和集合点的操作,也包括对脚本进行单机调试,保证脚本能正确运行。
03、脚本参数化
增强脚本之后,需要对脚本进行参数化。首先要确定脚本中哪些部分需要进行参数化。上一实例的脚本中buf2是手动输入的要发送的内容,buf3是接收buf2的内容,两者内容一致,这里可以对发送的buf2中的数据进行参数化。但需要注意的问题是,由于在Windows Sockets协议录制时所有的传输数据均保存在数据文件data.ws中,因此在进行参数化时主要是对该文件中的相关数据进行参数化。在data.ws中选择buf2中内容“hello”,点击右键→Replace with a Parameter,如图12-3所示。接下来的参数化过程就和普通的参数化过程一致。
那么为什么不需要对buf3进行参数化呢?可以分析一下buf2和buf3的数据,这两个缓存中的数据内容是一样的,buf2的关键字是send,buf3的关键字是rev,也就是说buf2的内容是要传送给远程服务器的内容,而buf3的内容是录制时从远程服务器返回的数据,当然这个数据也是希望返回的数据。因此,一般情况下只需要对send中的相关内容进行参数化。
这里只是需要参数化data.ws数据文件中的内容,那么如果希望传输一个文件中所有的内容应该怎么办呢?这是在编写脚本时经常会遇到的一个问题,而LoadRunner本身并未提供直接读取文件的方法,不过LoadRunner可以兼容C语言,这样就可以使用C语言来增强这段代码。如下面的代码,让传输buf2的内容变成传输一个文件的内容。
图12-3 参数化
int count;
char userbuffer[500];
long filestream;
char * filename = "E:\\1.txt";
if ((filestream = fopen(filename, "r")) == NULL ) {
lr_error_message ("Cannot open %s", filename);
return -1;
}
// Read until end of file
while (!feof(filestream)) {
// Read 500 bytes while maintaining a running count
count = fread(userbuffer, sizeof(char), 500, filestream);
lr_output_message ("%3d bytes read", count);
if (ferror(filestream)) { /* Check for file I/O errors */
lr_output_message ("Error reading file %s", filename);
break;
}
}
if (fclose(filestream)) // Close the file stream
lr_error_message ("Error closing file %s", filename);
lrs_set_send_buffer("socket1",userbuffer,count);
上面这两种参数化的方法是最常用的方法。
04、关联脚本
参数化完成之后,进行脚本回放,发现回放的日志文件中提示下面的信息:
Action.c(25): Mismatch in buffer's length (expected 299 bytes, 986 bytes actually received, difference in 687 bytes)。
这说明录制与回放的内容没有匹配,回放日志文件中详细记载了Expected Buffer和Received Buffer的值,通过比较这两种值可以发现录制和回放过程中不一致的地方。回放日志文件中提示缓存长度不匹配。
为什么脚本回放会提示缓存长度不匹配?首先来了解Mismatch匹配的机制。Mismatch有两种匹配方式:长度匹配和内容匹配。所谓长度匹配是当lrs_receive在接收到数据之后就会和期望的缓冲区数据进行长度(字符数)对比,如果实际接收到的数据长度不等于期望值,就会提示Mismatch警告信息。长度匹配是Mismatch缺省的匹配方式。
可以使用lrs_set_receive_option来设置匹配方式,将缺省值设置为MISMATCH_CONTENT来指定匹配方式为内容匹配,那么lrs_receive在接收到返回数据后将与期望的数据内容进行匹配对比,这时即使长度相同,如果内容不一样(比如实际接收到的是“A”,而期望数据为“a”),同样会提示Mismatch警告信息。
既然提示Mismatch警告信息,那么就需要找到这个录制与回放不同的信息,然后对脚本进行关联。进入树模式,这里需要注意一个细节,关联的信息一定是在Receive Buf中,因为关联的信息一定是服务器发送给客户端的信息。这里需要关联的信息在Receive Buf1中,在Action中选择Receive Buf1,在Text View(文本视图)中选择要关联的信息后右击,选择Create Parameter,如图12-4所示。
图12-4 选择要关联的内容
此时,弹出Create Parameter对话框,如图12-5所示。
“创建参数”对话框中各选项的含义如下:
Parameter Name(参数名称):设置定义的参数名称。
Data Range(数据范围):设置需要关联信息的偏移量,点击Select Range按钮,可以手动更改待关联内容的偏移量。
图12-5 创建参数
Boundaries(边界):可以手动设置参数左边界和右边界的值,这样可以固定参数的值。
Script Statement(脚本声明):显示生成的关联脚本,即脚本关联完成后脚本中添加的关联函数。当设置了左边界、右边界后发现脚本关联的函数发生了变化。
点击“确定”键,切换回脚本模式,可以看到脚本中多了一行关联的代码。
lrs_save_searched_string("socket0",LRS_LAST_RECEIVED,"port1","LB/BIN=e8ffa77092fbc91:", "RB/BIN=\"\r\\nServer:", 1, 0, -1);
到这里关联工作也已经完成了。
05、设置运行参数
上面的工作完成之后,脚本编辑已经完成。接下来设置运行时的参数,进入Vuser→Run-time Settings对话框,设置运行时的参数即可。
06、运行脚本
上面的所有工作完成后即运行脚本。
3、 Windows Sockets数据操作
在使用Windows Sockets协议录制后,可以查看并操纵数据。下面介绍几种常用的操纵数据的方法。
01、查看快照中的数据
在树视图中查看脚本时,VuGen提供缓冲区快照窗口,可以以文本视图或二进制视图方式查看快照,并且可以对窗口中显示的数据进行编辑。
文本视图缓冲区快照,以文本形式表示其内容,如图12-6所示。
图12-6 文本视图
二进制视图显示以十六进制表示的数据。左列显示该行中第一个字符的偏移量,中间的列显示数据的十六进制值,右列以ASCII格式显示数据,如图12-7所示。
图12-7 二进制视图
02、缓冲区导航器
默认情况下,VuGen左窗格中显示所有的步骤和缓冲区。缓冲区导航器是一个浮动窗口,通过它仅可以显示接收和发送缓冲区(lrs_send、lrs_receive、lrs_receive_ex和lrs_length_receive)。此外,还可以应用筛选器,以查看发送或接收缓冲区。
选择View→Buffer Navigator,打开“缓冲区导航器”对话框,如图12-8所示。
当在缓冲导航区选择缓冲区时,其内容会显示在缓冲区快照中。
如果在录制之后更改缓冲区的名称,其内容将不会显示在快照窗口中。要查看已重命名的缓冲区数据,可以使用缓冲区导航器,并选择新缓冲区的名称。VuGen将发出警告消息,指明选定的缓冲区将禁止创建参数。
03、转至偏移量
通过指定偏移量,可以在数据缓冲区中移动。可以指定数据的绝对位置,也可以指示与缓冲区中光标当前位置相对的位置。在快照窗口中点击右键,选择Go to Offset,将打开Go to Offset对话框,如图12-9所示。
图12-8 缓冲区导航器
图12-9 转至偏移量
通过此对话框,可以指定开始和结束偏移量选择数据的范围,并且还可以为关联函数提供要关联数据的偏移量的值。
Go to offset(转至偏移量):转至缓冲区中特定的偏移量(绝对偏移量)。
Advance by(前进):要跳至与光标相对的位置。输入正值,表示要前进,输入负值,表示在缓冲区内后退。
Select range from(选择范围):选择缓冲区中数据的范围,指定开始和结束偏移量。
04、书签
通过VuGen,可以将缓冲中的位置标记为书签,并且可以为每个书签赋予一个描述性名称。点击该名称直接跳至该书签的位置。书签列在缓冲区快照下方“输出”窗口的“书签”选项卡中。
在缓冲区快照(文本或二进制视图)中选择一个或多个字节点击右键,选择New Bookmark,并为书签输入一个描述性名称,如图12-10所示。
图12-10 新建书签
书签可以标记单个字节或多个字节。在列表中点击书签时,它在缓冲区快照窗口中将作为选定内容显示。书签的数据内容在文本视图中最初以蓝色突出显示,在二进制视图中,书签则以红色标记。将光标置于缓冲区中的书签上时,将弹出一个文本框,显示书签的名称。
05、修改缓冲区数据
在树视图中,VuGen提供了几个工具,使用这些工具,可以通过删除、更改或向现有数据中添加数据对数据进行修改。
数据缓冲区中可以插入数值,可以是单字节、双字节或4字节值。
点击右键,选择Advanced→Insert Number→Specify,弹出Specify Value对话框,如图12-11所示。
图12-11 “指定值”对话框
输入想要插入到Value框中的ASCII值,Size下拉框中可以选择数据的大小,包括单字节、双字节或4字节值,确定后,VuGen将以十六进制的形式将数据插入缓冲区。
可以对缓冲区数据执行下列所有标准编辑操作:复制、粘贴、剪切、删除和撤消。
执行以上修改缓冲区数据的操作时,一定要保证缓冲区的数据是处于可修改的状态,即Read only复选框是未被选中的状态。
4、关于LRS函数
上面对Windows Sockets协议录制和使用进行了阐述,下面介绍一些常用的函数。
01、Lrs_accept_connection接收侦听套接字连接
格式:
int lrs_accept_connection(char *old_socket,char *new_socket);
参数说明:
old_socket:被侦听的套接字标识符,如socket0。
new_socket:侦听到请求时建立的新套接字标识符,如socket1。
返回值:
成功返回0。
例:lrs_accept_connection(socket0,socket1);
函数从old_socket上的挂起连接队列中取出第一个连接,使用相同属性创建新的套接字。原来的套接字对于其他连接仍然是可用的。
侦听机制:当accept函数监听的old_socket收到连接请求时,old_socket执行体将建立一个新的socket连接(标识符为new_socket),收到服务请求的初始socket后(即old_socket)仍可以继续在以前的socket上监听,同时可以在新的socket描述符上进行数据传输操作。
02、Lrs_set_receive_option 设置套接字接收选项
格式:
int lrs_set_receive_option ( int option,int value,[char *terminator] );
参数说明:
option:接收选项,表示何时停止接收数据。可用选项有Mismatch和EndMarker。
value:选项的值详见表12-2。
terminator:用于value中接收的结束标记。该选项仅在EndMarker选项被指定为StringTerminator时需要。
表12-2 option选项值
返回值:成功返回0。
例1:该例指定对接收到的数据进行内容匹配性验证。
lrs_set_receive_option(Mismatch, MISMATCH_CONTENT);
lrs_send("socket1", "buf0", 1, 0);
lrs_receive("socket1", "buf1", LrsLastArg);
lrs_close_socket("socket1");
假设预期接收数据中包含一个“%”字符,而回放时该位置接收到的是“#”字符,则该例会报出Mismatch信息。如果选项值为MISMATCH_SIZE则不会报出Mismatch信息。
例2:该例中lrs_set_receive_option函数指定接收的套接字长度等于预期长度。
lrs_create_socket("socket0", "TCP", "RemoteHost=199.203.77.12:80", LrsLastArg);
lrs_set_receive_option(EndMarker, RecordingSize);
lrs_receive("socket0", "buf0", LrsLastArg);
假如录制时得到的buf0的长度为20个字节,而回放时从服务器接收到的长度为30个字节,以上脚本将只接收前20个字节,剩余的10个字节将被下一个lrs_receive接收。
如果该例的option值为EndMarker_None,那么lrs_receive接收完整的返回值并报一个Mismatch的消息。
例3:该例中lrs_set_receive_option函数指定接收中断时的二进制标识串
lrs_create_socket("socket0", "TCP", "RemoteHost=199.203.77.12:80", LrsLastArg);
lrs_set_receive_option(EndMarker, BinaryStringTerminator, "\\x00\\x07Mercury");
lrs_receive("socket0", "buf0", LrsLastArg);
假设服务器发送的内容如下:
"\x00\x01\x85\x80\x00\x01\x00\x01\x00\x00\x00\x00\x07"
"Mercury"
"\x02"
"co"
"\x02"
"il"
"\x00\x00\x01\x00\x01\xc0"
接收到的内容是:
"\x00\x01\x85\x80\x00\x01\x00\x01\x00\x00\x00\x00\x07"
"Mercury"
03、lrs_set_send_buffer 指定在套接字上发送的缓冲区
格式:
int lrs_set_send_buffer ( char *s_desc,char *buffer,int size );
参数说明:
s_desc:套接字标识符。
buffer:指定要发送的缓冲区。
size:发送的字节数。
返回值:发送成功返回0,失败返回错误码。
char *Buffer;
int Size;
lrs_receive("socket2", "buf20", LrsLastArg);
lrs_get_last_received_buffer("socket2", &Buffer, &Size);
/* 将缓冲区buf20中的值保存到用户缓冲区Buffer中*/
. . .
lrs_set_send_buffer("socket2", Buffer, 10 );
lrs_send("socket2","buf21", LrsLastArg );
lrs_free_buffer(Buffer);
函数lrs_set_send_buffer用来指定下一次调用lrs_send时要发送的缓冲区内容。该缓冲区在lrs_set_send_buffer中被指定后,其后的一个lrs_send中指定的缓冲区内容将不会被发送。
04、lrs_save_param 将静态数据或从缓冲区得到的数据保存到参数中
格式:
int lrs_save_param(char *s_desc,char *buf_desc,char *param_name,int offset,int param_len);
参数说明:
s_desc:套接字标识符。
buf_desc:缓冲区标识符。
param_name:存放缓存数据的参数名称。
offset:被保存到参数中的缓冲区偏移量。
param_len:要保存到参数中的字节数。
返回值:成功返回0,失败返回错误码。
例1:保存动态缓冲区数据到参数中。
lrs_save_param("socket0","buf0","param1",20,10):
lr_output_message("The content of param1
is%s",lr_eval_string("
脚本执行结果:
Vuser_init.c(28):lrs_save_param(socket0, buf0, param1,20,3):
Vuser_init.c(29):
The content of param1 is PID
例2:将最后接收到的缓冲区数据保存到参数中。
lrs_receive("socket0","buf0",LrsLastArg);
lrs_save_param("socket0",NULL,"param1",20,3):
lr_output_message("the content of param1
is%s:",lr_eval_string("
利用该函数将指定的缓冲区数据成功保存到参数中,指定的参数和普通参数一样,可以在脚本的其他地方自由引用。
05、lrs_save_param_ex 将用户、静态或接收到的缓冲区(或缓冲区部分)保存到参数中
格式:
int lrs_save_param_ex(char *s_desc,char *type,char *buff,int offset,int length,char *encoding,char *param);
参数说明:
s_desc:套接字标识符。
type:要将数据保存到参数中的缓冲区类型,有user(用户缓冲区)、static(data.ws中的静态缓冲区)和received(最后接收的缓冲区数据)三种。
buff:和type的值有关,如果type的值为user(用户缓冲区),则buff的值为指定用户缓冲区;如果type的值为static(data.ws中的静态缓冲区),则buff的值为指定的动态缓冲区;如果type的值为received(最后接收的缓冲区数据),则buff的值可设为NULL。
offset:缓冲区偏移量。
length:保存到参数中的字节数。
encoding:编码方式可以指定为ASCII或EBCDIC,如果是用户缓冲区,则NULL默认为ASCII,如果type为static或received,则NULL默认为客户端编码方式。
param:参数名称。
返回值:成功返回0,失败返回错误码。
例1:保存用户缓冲区数据到参数中。
char *userbuffer="chuanshi";
lrs_save_param_ex("socket0","user",userbuffer,0,5,"ascii","param1");
lr_output_message("The content of param1 is
%s",lr_eval_string("
脚本执行结果:
Action.c(9): lrs_save_param_ex(socket0, user, buf_p, 0, 8, ascii, param1)
Action.c(11): The content of param1 is chuanshi
例2:保存静态缓冲区数据到参数中。
lrs_save_param_ex("socket0","static","buf0",20,3,"ascii","param1");
lr_output_message("The content of param1 is
%s",lr_eval_string("
脚本执行结果:
Action.c(9): lrs_save_param_ex(socket0, static, buf_p, 20, 3, ascii, param1)
Action.c(11): The content of param1 is PID
例3:将最后接收到的缓冲区数据保存到参数中。
lrs_receive("socket0","buf3",LrsLastArg);
lrs_save_param_ex("socket0","received",NULL,20,3,"param1");
lr_output_message("The content of param1 is
%s",lr_eval_string("
脚本执行结果:
Action.c(9): lrs_save_param_ex(socket0, received, buf_p, 20, 3, null, param1)
Action.c(11): The content of param1 is PID
函数lrs_save_param_ex不识别用户缓冲区的参数化,如例1中如果userbuffer的内容为“chuan
Action.c(11): The content of param1 is chuan
06、lrs_save_searched_string 在静态或接收到的缓冲区中搜索出现的字符串,将出现的字符串的缓冲区部分保存到参数中
格式:
int lrs_save_searched_string (char* s_desc,char* buf_desc,char* param_name,char* left_boundary, char* right_boundary,int ordinal, int offset,int param_len );
参数说明:
s_desc:套接字标识符。
buf_desc:缓冲区标识符。
param_name:参数名称。
left_boundary:标识搜索缓冲区部分左边界的字符串,格式为“LB=xxx”。
right_boundary:标识搜索缓冲区部分右边界的字符串,格式为“RB=xxx”。
ordinal:表示从第几次出现的左边界字符串开始搜索,如果指定了左边界则ordinal的值一定大于0,如果没有指定左边界则将ordinal设为-1。
offset:要开始搜索的偏移量。如果指定了左边界则此偏移量相对于左边界计算,否则就从缓冲区的开始处计算偏移量。
param_len:要保存到参数中的缓冲区数据字节数,适用于没有指定右边界的情况。如果指定了右边界则将该参数设置为-1。
返回值:成功返回0,失败返回错误码。
例1:将左边界和右边界的值设为NULL,指定偏移量和字节数。
lrs_save_searched_string("socket0","buf0","param1",NULL,NULL,-1,139,5);
lr_output_message("The content is %s",lr_eval_string("
数据文件data.ws中的内容如下:
send buf0 201
"GET /download/cmsoft/UpgradeInfo/NetAssist.inf HTTP/1.1\r\n"
"If-Modified-Since: Fri, 03 Jul 2009 03:57:53 GMT\r\n"
"If-None-Match: \"e8ffa77092fbc91:633a6\"\r\n"
"User-Agent: Internet+Explorer\r\n"
"Host: www.cmsoft.cn\r\n"
"\r\n"
脚本执行结果:
Action.c(27): lrs_save_searched_string(socket0, buf0, param1, null, null, -1, 139, 5)
Action.c(27): Notify: Saving Parameter "param1 = 633a6"
Action.c(29): Notify: Parameter Substitution: parameter "param1" = "633a6"
例2:指定左边界值为e8ffa77092fbc91:,右边界值为\"\r\\nServer,偏移量为0。
lrs_save_searched_string("socket0",LRS_LAST_RECEIVED,"Param1","LB/BIN= e8ffa77092fbc91:", "RB/BIN=\"\r\\nServer", 1, 0, -1);
lr_output_message("The text is:%s", lr_eval_string("
脚本执行结果:
Action.c(27): lrs_save_searched_string(socket0, LRS_LAST_RECEIVED, param1, e8ffa77092fbc91:, \"\r\\nServer, 1, 0, -1)
Action.c(27): Notify: Saving Parameter "param1 = 633a6"
Action.c(29): Notify: Parameter Substitution: parameter "param1" = "633a6"
函数lrs_save_searched_string将缓冲区中的一部分数据保存到参数中。如果左右边界指定的是二进制字符串,则使用“LB/BIN”或“RB/BIN”来指定,如“LB/BIN=\\x56”。缓冲区标识符表示结构从那个缓冲区中获取数据,并保存到参数中,如果是从数据文件data.ws中读取则指定为“bufxxx”,如果设置为NULL则表示从最后一个接收到的缓冲区(LRS_LAST_RECEIVED)中读取数据。
该函数的参数列表有以下几种常见的组合方式:
左右边界均为NULL,并指定偏移量和长度(等同于lrs_sava_param)。
指定左边界和右边界出现的次数。
指定左边界、左边界出现的次数及读取的数据长度(字节数)。
指定左边界、左边界出现的次数、从左边界算起的偏移量及右边界。
指定左边界、左边界出现的次数、从左边界算起的偏移量及读取的字节数。
只限定偏移量和右边界。
本章节主要讲解了关于“Windows Sockets(WinSock)协议”的内容,大家觉得有用的话记得每天来这里和小编一起学习涨薪技能哦。