4. tcp客户端在协议栈源码工程下,存在一个用vs2015建立的TcpServerForStackTesting工程 。其运行在windows平台下,模拟实际应用场景下的tcp服务器 。当tcp客户端连接到服务器后,服务器会立即下发一个1100多字节长度的控制报文到客户端 。之后在整个tcp链路存续期间,服务器会每隔一段随机的时间(90秒到120秒之间)下发控制报文到客户端 , 模拟实际应用场景下服务器主动下发指令、数据到客户端的情形 。客户端则连续上发数据报文到服务器 , 服务器回馈一个应答报文给客户端 。客户端如果收不到该应答报文则会立即重发,直至收到应答报文或超过重试次数后重连服务器 。总之,整个测试场景的设计目标就是完全契合常见的商业应用需求,以此来验证协议栈的核心功能指标是否完全达标 。用vs2015打开这个工程,配置管理器指定目标平台为x64 。main.cpp文件的头部定义了服务器的端口号以及报文长度等信息:
#define SRV_PORT6410 //* 服务器端口#define LISTEN_NUM10//* 最大监听数#define RCV_BUF_SIZE2048 //* 接收缓冲区容量#define PKT_DATA_LEN_MAX 1200 //* 报文携带的数据最大长度,凡是超过这个长度的报文都将被丢弃
我们可以依据实际情形调整上述配置并利用这个模拟服务器测试tcp客户端的通讯功能 。
……#include "onps.h"#define PKT_FLAG 0xEE //* 通讯报文的头部和尾部标志typedef struct _ST_COMMUPKT_HDR_ { //* 数据及控制指令报文头部结构CHAR bFlag;//* 报文头部标志,其值参看PKT_FLAG宏CHAR bCmd;//* 指令,0为数据报文,1为控制指令报文CHAR bLinkIdx;//* tcp链路标识,当存在多个tcp链路时,该字段用于标识这是哪一个链路UINT unSeqNum;//* 报文序号UINT unTimestamp;//* 报文被发送时刻的unix时间戳USHORT usDataLen;//* 携带的数据长度USHORT usChechsum;//* 校验和(crc16) , 覆盖除头部和尾部标志字符串之外的所有字段} PACKED ST_COMMUPKT_HDR, *PST_COMMUPKT_HDR; typedef struct _ST_COMMUPKT_ACK_ { //* 数据即控制指令应答报文结构ST_COMMUPKT_HDR stHdr; //* 报文头UINT unTimestamp;//* unix时间戳,其值为被应答报文携带的时间戳CHAR bLinkIdx;//* tcp链路标识,其值为被应答报文携带的链路标识CHAR bTail;//* 报文尾部标志,其值参看PKT_FLAG宏} PACKED ST_COMMUPKT_ACK, *PST_COMMUPKT_ACK;//* 提前申请一块静态存储时期的缓冲区用于tcp客户端的接收和发送,因为接收和发送的报文都比较大,所以不使用动态申请的方式#define RCV_BUF_SIZE1300//* 接收缓冲区容量#define PKT_DATA_LEN_MAX 1200//* 报文携带的数据最大长度,凡是超过这个长度的报文都将被丢弃static UCHAR l_ubaRcvBuf[RCV_BUF_SIZE]; //* 接收缓冲区static UCHAR l_ubaSndBuf[sizeof(ST_COMMUPKT_HDR) + PKT_DATA_LEN_MAX]; //* 发送缓冲区,ST_COMMUPKT_HDR为通讯报文头部结构体int main(void){EN_ONPSERR enErr;SOCKET hSocket = INVALID_SOCKET;if(open_npstack_load(&enErr)){printf("The open source network protocol stack (ver %s) is loaded successfully. \r\n", ONPS_VER);//* 协议栈加载成功,在这里初始化ethernet网卡或等待ppp链路就绪#if 0emac_init(); //* ethernet网卡初始化函数,并注册网卡到协议栈#elsewhile(!netif_is_ready("ppp0")) //* 等待ppp链路建立成功os_sleep_secs(1);#endif}else{printf("The open source network protocol stack failed to load, %s\r\n", onps_error(enErr));return -1;}//* 分配一个socketif(INVALID_SOCKET == (hSocket = socket(AF_INET, SOCK_STREAM, 0, &enErr))){//* 返回了一个无效的socket,打印错误日志printf("<1>socket() failed, %s\r\n", onps_error(enErr));return -1;}//* 连接成功则connect()函数返回0,非0值则连接失败if(connect(hSocket, "192.168.0.2", 6410, 10)){printf("connect 192.168.0.2:6410 failed, %s\r\n", onps_get_last_error(hSocket, NULL));close(hSocket);return -1;}//* 等待接收服务器应答或控制报文的时长(即recv()函数的等待时长),单位:秒 。0不等待;大于0等待指定秒数;-1一直//* 等待直至数据到达或报错 。设置成功返回TRUE,否则返回FALSE 。这里我们设置recv()函数不等待//* 注意 , 只有连接成功后才可设置这个接收等待时长 , 在这里我们设置接收不等待,recv()函数立即返回,非阻塞型if(!socket_set_rcv_timeout(hSocket, 0, &enErr))printf("socket_set_rcv_timeout() failed, %s\r\n", onps_error(enErr));INT nThIdx = 0;while(TRUE && nThIdx < 1000){//* 接收,前面已经设置recv()函数不等待,有数据则读取数据后立即返回,无数据则立即返回INT nRcvBytes = recv(hSocket, ubaRcvBuf, sizeof(ubaRcvBuf));if(nRcvBytes > 0){//* 收到报文 , 处理之,报文有两种:一种是应答报文;另一种是服务器主动下发的控制报文//* 在这里添加你的自定义代码……}//* 发送数据报文到服务器,首先封装要发送的数据报文,PST_COMMUPKT_HDR其类型为指向ST_COMMUPKT_HDR结构体的指//* 针,这个结构体是与TcpServerForStackTesting服务器通讯用的报文头部结构PST_COMMUPKT_HDR pstHdr = (PST_COMMUPKT_HDR)l_ubaSndBuf;pstHdr->bFlag = (CHAR)PKT_FLAG;pstHdr->bCmd = 0x00;pstHdr->bLinkIdx = (CHAR)nThIdx++;pstHdr->unSeqNum = unSeqNum;pstHdr->unTimestamp = time(NULL);pstHdr->usDataLen = 900; //* 填充随机数据,随机数据长度加ST_COMMUPKT_HDR结构体长度不超过l_ubaSndBuf的长度即可pstHdr->usChechsum = 0;pstHdr->usChechsum = crc16(l_ubaSndBuf + sizeof(CHAR), sizeof(ST_COMMUPKT_HDR) - sizeof(CHAR) + 900, 0xFFFF);l_ubaSndBuf[sizeof(ST_COMMUPKT_HDR) + 900] = PKT_FLAG;//* 发送上面已经封装好的数据报文INT nPacketLen = sizeof(ST_COMMUPKT_HDR) + pstHdr->usDataLen + 1;INT nSndBytes = send(hSocket, l_ubaSndBuf, nPacketLen, 3);if(nSndBytes != nPacketLen) //* 与实际要发送的数据不相等的话就意味着发送失败了{printf("<err>sent %d bytes failed, %s\r\n", nPacketLen, onps_get_last_error(hSocket, &enErr));//* 关闭socket,断开当前tcp连接 , 释放占用的协议栈资源close(hSocket);return -1;}}//* 关闭socket , 断开当前tcp连接,释放占用的协议栈资源close(hSocket);return 0;}
推荐阅读
- 2 onps栈使用说明——ping、域名解析等网络工具测试
- Go实现栈与队列基本操作
- 定位java程序中占用cpu最高的线程堆栈信息
- 1 onps栈使用说明——API接口手册
- 索尼wf1000xm4怎么连接_索尼wf1000xm4使用说明
- 3 onps栈移植说明——添加网卡
- 2 onps栈移植说明——编译器及os适配层移植
- 1 onps栈移植说明——onps栈的配置及裁剪
- 开源网络协议栈onps诞生记
- <一>从指令角度了解函数堆栈调用过程