由协议栈底层提供的api,用于涉及底层操作的一些功能实现,这些api接口函数的原型定义分布于不同的文件,它们被统一include进了onps.h中:
功能
协议栈的入口函数,目标系统调用该函数启动协议栈。换言之,在使用协议栈之前必须首先成功先调用该函数。
原型
BOOL open_npstack_load(EN_ONPSERR *penErr);
入口参数
返回值
加载成功,返回TRUE;反之返回FALSE,具体的错误信息参见参数penErr返回的错误码。
示例
EN_ONPSERR enErr;
if(open_npstack_load(&enErr)) //* 加载协议栈
{
//* 协议栈加载成功,在这里添加你的自定义代码
……
}
else
printf("协议栈加载失败, %s\r\n", onps_error(enErr)); //* 打印错误信息
功能
协议栈的退出函数。一旦调用该函数,协议栈会结束运行并释放占用的相关系统资源。
原型
void open_npstack_unload(void);
入口参数
无
返回值
无
示例
略
功能
添加一条静态路由到路由表。
原型
BOOL route_add(PST_NETIF pstNetif, UINT unDestination, UINT unGateway, UINT unGenmask, EN_ONPSERR *penErr);
入口参数
返回值
添加成功则返回TRUE,否则返回FALSE,具体的错误信息参见参数penErr返回的错误码。
示例
EN_ONPSERR enErr;
PST_NETIF pstNetif = netif_get_by_name("eth0");
if(pstNetif && route_add(pstNetif, inet_addr_small("47.92.239.0"), inet_addr_small("192.168.0.1"), inet_addr_small("255.255.255.0"), &enErr))
{
//* 添加成功,在这里添加你的自定义代码
……
}
else
printf("静态路由添加失败, %s\r\n", onps_error(enErr)); //* 打印错误信息
功能
删除一条静态路由。
原型
void route_del(UINT unDestination);
入口参数
返回值
无
示例
略
功能
删除指定网卡在路由表中的所有路由条目,禁止网卡跨网段通讯。
原型
void route_del_ext(PST_NETIF pstNetif);
入口参数
返回值
无
示例
略
功能
获取缺省路由信息。
原型
PST_NETIF route_get_default(void);
入口参数
无
返回值
返回缺省路由绑定的网卡控制块首地址。
示例
略
功能
启动dhcp客户端,向dhcp服务器请求租用一个动态地址。
原型
BOOL dhcp_req_addr(PST_NETIF pstNetif, EN_ONPSERR *penErr);
入口参数
返回值
请求成功则返回TRUE,否则返回FALSE,具体的错误信息参见参数penErr返回的错误码。
示例
参见上一篇博文《onps栈移植说明(3)——添加网卡》。
功能
增加新的ethernet网卡到协议栈。
原型
PST_NETIF ethernet_add(const CHAR *pszIfName,
const UCHAR ubaMacAddr[ETH_MAC_ADDR_LEN],
PST_IPV4 pstIPv4,
PFUN_EMAC_SEND pfunEmacSend,
void (*pfunStartTHEmacRecv)(void *pvParam),
PST_NETIF *ppstNetif,
EN_ONPSERR *penErr);
入口参数
返回值
注册成功,返回一个 PST_NETIF 类型的指针,该指针指向新添加的网卡控制块,唯一的标识新添加的这块网卡;注册失败则返回 NULL,具体错误信息参见 penErr参数携带的错误码。
示例
参见上一篇博文《onps栈移植说明(3)——添加网卡》。
功能
删除ethernet网卡。
原型
void ethernet_del(PST_NETIF *ppstNetif);
入口参数
返回值
无
示例
略
功能
将收到的ethernet报文推送给协议栈。
原型
void ethernet_put_packet(PST_NETIF pstNetif, PST_SLINKEDLIST_NODE pstNode);
入口参数
返回值
无
示例
参见上一篇博文《onps栈移植说明(3)——添加网卡》。
功能
检查网卡是否已进入或正处于正常工作状态。
原型
BOOL netif_is_ready(const CHAR *pszIfName);
入口参数
返回值
返回TRUE,网卡已就绪;反之则返回FALSE。
示例
略
功能
通过网卡名称查找网卡。
原型
PST_NETIF netif_get_by_name(const CHAR *pszIfName);
入口参数
返回值
存在则返回指向网卡控制块ST_NETIF的指针;否则返回NULL。
示例
略
功能
向内存管理单元(mmu)申请一块指定大小的内存。
原型
void *buddy_alloc(UINT unSize, EN_ONPSERR *penErr);
入口参数
返回值
申请成功返回内存首地址;反之则返回NULL,具体错误信息参见 penErr参数携带的错误码。
示例
参见buf_list_merge_packet函数示例或上一篇博文《onps栈移植说明(3)——添加网卡》。
功能
归还(释放)先前通过buddy_alloc()函数申请的内存。
原型
BOOL buddy_free(void *pvStart);
入口参数
返回值
成功归还内存返回TRUE;当pvStart指向的并不是buddy_alloc()函数返回的内存首地址时则返回FALSE。
示例
参见buf_list_merge_packet函数示例。
功能
按链接顺序从链表首部逐个取出链表节点。
原型
void *buf_list_get_next_node(SHORT *psNextNode, USHORT *pusDataLen);
入口参数
返回值
如果尚未到达链表尾部,则返回当前链表节点携带的数据的首地址;反之则返回NULL。
示例
参见上一篇博文《onps栈移植说明(3)——添加网卡》。
功能
统计链表所有节点携带的数据长度之和。
原型
UINT buf_list_get_len(SHORT sBufListHead);
入口参数
返回值
返回链表所有节点携带的数据长度之和。
示例
参见buf_list_merge_packet函数示例或上一篇博文《onps栈移植说明(3)——添加网卡》。
功能
从链表首节点开始顺序取出数据将其放入用户指定的缓冲区,把零散的链表数据合并成一块连续数据。
原型
void buf_list_merge_packet(SHORT sBufListHead, UCHAR *pubPacket);
入口参数
返回值
无
示例
EN_ONPSERR enErr;
UINT unDataLen = buf_list_get_len(sBufListHead /* 链表首地址 */);
UCHAR *pubBuf = (UCHAR *)buddy_alloc(unDataLen, &enErr); //* 申请一块缓冲区用于保存合并后的数据
if(pubBuf)
{
//* 合并,合并后的数据保存在了pubBuf中,数据长度为unDataLen
buf_list_merge_packet(sBufListHead, pubBuf);
//* 在这里增加你自己的代码处理合并合并后的数据
……
//* 处理完毕,释放刚才申请的内存
buddy_free(pubBuf);
}
协议栈提供的伯克利套接字(Berkeley sockets)并不是严格按照传统socket标准设计实现的,而是我根据以往socket编程经验,以方便用户使用、简化用户编码为设计目标,重新声明并定义的一组常见socket接口函数。协议栈简化了传统BSD socket编程需要的一些繁琐操作,将一些不必要的操作细节改为底层实现,比如select/poll模型、阻塞及非阻塞读写操作等。简化并不意味着推翻,socket接口函数的基本定义、主要参数、使用方法并没有改变,你完全可以根据以往经验快速上手并熟练使用onps栈sockets。这一点相信你已经从前面的测试代码中得到了佐证。
socket层所有接口函数的实现源码被封装在了单独的一个文件中,参见bsd/socket.c。其对应的头文件socket.h有这些接口函数的定义和说明。目前协议栈提供的socket接口函数有十几个,能够满足绝大部分应用场景的需求。这些接口函数如下:
功能
创建一个socket,支持udp和tcp两种类型,即:SOCK_DGRAM和SOCK_STREAM。注意,不使用时一定要调用close()函数关闭,以释放其占用的协议栈相关资源。
原型
SOCKET socket(INT family, INT type, INT protocol, EN_ONPSERR *penErr);
入口参数
返回值
执行成功返回socket句柄,失败返回INVALID_SOCKET,具体的错误信息参见penErr参数返回的错误码。
示例
EN_ONPSERR enErr;
//* tcp
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0, &enErr);
if(INVALID_SOCKET == hSocket) //* 返回一个无效的socket
printf("%s\r\n", onps_error(enErr)); //*打印错误信息
//* udp
SOCKET hSocket = socket(AF_INET, SOCK_DGRAM, 0, &enErr);
……
功能
关闭socket,释放占用的协议栈资源。
原型
void close(SOCKET socket);
入口参数
返回值
无
示例
EN_ONPSERR enErr;
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0, &enErr);
……
if(INVALID_SOCKET != hSocket)
close(hSocket);
功能
用于tcp类型的socket时,其功能为与目标服务器建立tcp连接,阻塞型;用于udp类型的socket时,其功能为绑定一个固定的目标通讯地址,udp通讯均与这个固定地址进行。
原型
INT connect(SOCKET socket, const CHAR *srv_ip, USHORT srv_port, INT nConnTimeout);
入口参数
返回值
0:连接成功
-1:连接失败,具体的错误信息通过onps_get_last_error()获得
示例
EN_ONPSERR enErr;
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0, &enErr);
if(INVALID_SOCKET == hSocket)
{
printf("%s\r\n", onps_error(enErr));
return;
}
if(!connect(hSocket, "47.92.239.107", 6410, 10))
{
//* 连接成功,在这里添加你的自定义代码
……
}
else
{
//* 连接失败,打印错误信息
printf("%s\r\n", onps_get_last_error(hSocket, NULL));
}
……
close(hSocket);
功能
仅用于tcp类型的socket,非阻塞型,与目标tcp服务器建立连接。
原型
INT connect_nb(SOCKET socket, const CHAR *srv_ip, USHORT srv_port);
入口参数
返回值
0:连接成功
1:连接中
-1:连接失败,具体的错误信息通过onps_get_last_error()获得
示例
EN_ONPSERR enErr;
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0, &enErr);
if(INVALID_SOCKET == hSocket)
{
printf("%s\r\n", onps_error(enErr));
return;
}
//* 循环等待tcp连接成功
while(1)
{
INT nRtnVal = connect_nb(hSocket, "47.92.239.107", 6410);
if(!nRtnVal)
{
//* 连接成功,在这里增加你的自定义代码
……
break; //* 退出循环,不再轮询检查tcp连接进程
}
else if(nRtnVal < 0)
{
//* 连接失败,打印错误信息并退出循环不再轮询检查tcp连接进程
printf("%s\r\n", onps_get_last_error(hSocket, NULL));
break;
}
else;
//* 连接中,tcp三次握手操作尚未完成,此时你可以干点别的事情,或者延时一小段时间后继续检查当前连接状态
……
os_sleep_secs(1);
}
……
close(hSocket);
功能
检查tcp链路是否处于连接状态。
原型
INT is_tcp_connected(SOCKET socket, EN_ONPSERR *penErr);
入口参数
返回值
0:未连接
1:已连接
-1:函数执行失败,具体的错误信息通过参数penErr获得
示例
EN_ONPSERR enErr;
……
INT nRtnVal = is_tcp_connected(hSocket, &enErr);
if(nRtnVal > 0)
printf("已连接\r\n");
else if(!nRtnVal)
printf("未连接\r\n");
else
printf("检查失败,%s\r\n", onps_error(enErr));
……
功能
发送数据到目标地址。注意tcp链路下为阻塞型,直至收到对端的tcp层ack报文或超时才会返回。udp链路下为非阻塞型,且只有在调用connect()函数后才能使用这个函数。
原型
INT send(SOCKET socket, UCHAR *pubData, INT nDataLen, INT nWaitAckTimeout);
入口参数
返回值
发送成功,则返回值等于nDataLen;发送失败,返回值不等于nDataLen,具体的错误信息通过onps_get_last_error()获得。
示例
/* tcp链路下send()函数使用示例 */
EN_ONPSERR enErr;
……
UCHAR ubUserData[128];
INT nSndBytes, nSndNum = 0;
__lblSend:
if(nSndNum > 2)
{
//* 超出重传次数,不再重传,可以关闭当前tcp链路重连tcp服务器或者你自己的其它处理方式
……
return;
}
nSndBytes = send(hSocket, ubUserData, sizeof(ubUserData), 3);
if(sizeof(ubUserData) == nSndBytes)
{
//* 发送成功,在这里添加你自己的处理代码
……
}
else
{
const CHAR *pszErr = onps_get_last_error(hSocket, &enErr);
if(enErr == ERRTCPACKTIMEOUT) //* 等待tcp层的ack报文超时
{
//* 数据重传,用户层实现tcp层重传机制
nSndNum++;
goto __lblSend;
}
else //* 其它错误,意味着底层协议栈捕捉到了内存不够用、网卡故障等类似的严重问题
{
//* 没必要触发重传机制了,根据你自己的具体情形增加容错处理代码并打印错误信息
……
printf("发送失败,%s\r\n", pszErr);
}
}
……
功能
发送数据到目标地址,非阻塞型,其它与send函数完全相同。
原型
INT send_nb(SOCKET socket, UCHAR *pubData, INT nDataLen);
入口参数
返回值
发送成功,则返回值等于nDataLen;返回值为0,上一组数正处于发送中(尚未收到对端的tcp ack报文),需要等待其发送成功后再发送当前数据;发送失败,返回值小于等于0,具体的错误信息通过onps_get_last_error()获得。
示例
/* tcp链路下send_nb()函数使用示例 */
EN_ONPSERR enErr;
……
UCHAR ubUserData[128];
INT nRtnVal;
__lblSend:
nRtnVal = send_nb(hSocket, ubUserData, sizeof(ubUserData));
if(sizeof(ubUserData) == nRtnVal)
{
//* 调用is_tcp_send_ok()函数等待是否已成功送达对端,或者(同时)做点别的事情
……
}
else if(0 == nRtnVal)
{
//* 上一组数据尚未发送完毕,需要等待发送完毕后再发送当前数据,等待期间你可以在这里做点别的事情
……
goto __lblSend;
}
else
{
//* 发送失败,协议栈底层捕捉到了一个严重的系统错误,这里增加你的容错代码并打印错误信息,不再继续发送
……
printf("发送失败,%s\r\n", onps_get_last_error(hSocket, NULL));
}
……
功能
非阻塞型,数据是否已成功送达tcp链路的对端(已收到对端回馈的tcp ack报文)。
原型
INT is_tcp_send_ok(SOCKET socket);
入口参数
返回值
0:发送中
1:发送成功
-1:发送失败,具体错误信息通过onps_get_last_error()函数获得
示例
EN_ONPSERR enErr;
……
UCHAR ubUserData[128];
INT nRtnVal;
__lblSend:
nRtnVal = send_nb(hSocket, ubUserData, sizeof(ubUserData));
if(sizeof(ubUserData) == nRtnVal)
{
//* 数据已通过网卡成功送出,接下来轮询等待对端回馈的tcp ack报文,确保数据成功送达对端
while(1)
{
INT nResult = is_tcp_send_ok(hSocket);
if(nResult == 1)
{
//* 发送成功了,退出轮询等待
break;
}
else if(nResult < 0)
{
//* 协议栈底层捕捉到了一个严重的系统错误,不再轮询等待,并打印错误信息
……
printf("%s\r\n", onps_get_last_error(hSocket, NULL));
break;
}
//* 发送中,在这里你可以做点别的事情
……
}
……
}
else if(0 == nRtnVal)
{
//* 上一组数据尚未发送完毕,需要等待发送完毕后再发送当前数据,等待期间你可以在这里做点别的事情
……
goto __lblSend;
}
else
{
//* 发送失败,协议栈底层捕捉到了一个严重的系统错误,这里打印错误信息,不再继续发送
printf("发送失败,%s\r\n", onps_get_last_error(hSocket, NULL));
}
……
功能
非阻塞型,仅用于udp通讯,发送数据到指定的目标地址。
原型
INT sendto(SOCKET socket, const CHAR *dest_ip, USHORT dest_port, UCHAR *pubData, INT nDataLen);
入口参数
返回值
发送成功,返回值等于nDataLen,反之则发送失败,具体的错误信息通过onps_get_last_error()获得。
示例
EN_ONPSERR enErr;
……
UCHAR ubUserData[128];
INT nSndBytes = sendto(hSocket, "47.92.239.107", 6411, ubUserData, sizeof(ubUserData));
if(sizeof(ubUserData) == nSndBytes)
{
//* 发送成功,在这里增加你自己的业务代码
……
}
else
{
//* 发送失败,在这里增加你自己的容错代码并打印错误信息
……
printf("发送失败,%s\r\n", onps_get_last_error(hSocket, NULL));
}
……
功能
读取链路对端发送的数据。其阻塞类型取决于socket_set_rcv_timeout()函数设定的接收等待时长。缺省为阻塞型,一直等待直至收到数据或报错。
原型
INT recv(SOCKET socket, UCHAR *pubDataBuf, INT nDataBufSize);
入口参数
返回值
大于等于0为实际到达的数据长度,单位:字节;小于0,接收失败,具体的的错误信息通过onps_get_last_error()获得。
示例
EN_ONPSERR enErr;
……
UCHAR ubRcvBuf[1500];
INT nRcvBytes = recv(hSocket, ubRcvBuf, sizeof(ubRcvBuf));
if(nRcvBytes > 0) //* 收到数据
{
……
}
else
{
if(nRcvBytes < 0) //* 协议栈底层捕捉到了一个严重错误,在这里增加你的容错代码并打印错误信息
{
……
printf("%s\r\n", onps_get_last_error(hSocket, NULL));
}
}
……
功能
设定recv()函数接收等待的时长。其设定的接收等待时长决定了recv()函数的阻塞类型。
等于0:非阻塞,recv()不等待立即返回
大于0:阻塞,recv()等待指定时长直至数据到达或超时
小于0:阻塞,recv()一直等待直至数据到达或出错
原型
BOOL socket_set_rcv_timeout(SOCKET socket, CHAR bRcvTimeout, EN_ONPSERR *penErr);
入口参数
返回值
设置成功返回TRUE,否则返回FALSE,具体的错误信息通过参数penErr获得。
示例
EN_ONPSERR enErr;
……
if(!socket_set_rcv_timeout(hSocket, 1, &enErr))
{
//* 设置失败,打印错误信息,此时系统采用缺省值,即recv()函数一直等待直至收到数据或协议栈报错
printf("%s\r\n", onps_error(enErr));
}
……
功能
接收数据并返回数据源的地址信息,仅用于udp通讯。
原型
INT recvfrom(SOCKET socket, UCHAR *pubDataBuf, INT nDataBufSize, in_addr_t *punFromIP, USHORT *pusFromPort);
入口参数
返回值
实际收到的数据的长度,单位:字节;小于0则接收失败,具体的的错误信息通过onps_get_last_error()获得。
示例
EN_ONPSERR enErr;
……
UCHAR ubRcvBuf[512];
in_addr_t unFromIp;
USHORT usFromPort;
INT nRcvBytes = recvfrom(hSocket, ubRcvBuf, sizeof(ubRcvBuf), &unFromIp, &usFromPort);
if(nRcvBytes > 0) //* 收到数据
{
CHAR szAddr[20];
printf("收到来自地址%s:%d的%d字节的数据\r\n", inet_ntoa_safe_ext(unFromIp, szAddr), usFromPort, nRcvBytes);
……
}
else
{
if(nRcvBytes < 0) //* 协议栈底层捕捉到了一个严重错误,在这里增加你的容错代码并打印错误信息
{
……
printf("发送失败,%s\r\n", onps_get_last_error(hSocket, NULL));
}
}
……
功能
绑定一个ip地址和端口。
原型
INT bind(SOCKET socket, const CHAR *pszNetifIp, USHORT usPort);
入口参数
返回值
0:成功
-1:失败,具体的的错误信息通过onps_get_last_error()获得
示例
EN_ONPSERR enErr;
……
SOCKET hSockSrv = socket(AF_INET, SOCK_STREAM, 0, &enErr);
if(INVALID_SOCKET != hSockSrv)
{
if(!bind(hSockSrv, NULL, 6411)) //* 绑定成功
……
else //* 绑定失败
printf("%s\r\n", onps_get_last_error(hSocket, NULL)); //* 打印错误信息
}
else
printf("%s\r\n", onps_error(enErr)); //*打印错误信息
……
功能
tcp服务器进入监听状态,等待tcp客户端连接请求的到达。
原型
INT listen(SOCKET socket, USHORT backlog);
入口参数
返回值
0:成功
-1:失败,具体的的错误信息通过onps_get_last_error()获得
示例
EN_ONPSERR enErr;
……
SOCKET hSockSrv = socket(AF_INET, SOCK_STREAM, 0, &enErr);
if(INVALID_SOCKET != hSockSrv)
{
if(!bind(hSockSrv, NULL, 6411)) //* 绑定成功
{
if(!listen(hSockSrv, usBacklog)) //* 进入监听状态
{
……
}
else //* 失败
printf("%s\r\n", onps_get_last_error(hSocket, NULL)); //* 打印错误信息
}
else //* 绑定失败
printf("%s\r\n", onps_get_last_error(hSocket, NULL)); //* 打印错误信息
}
else
printf("%s\r\n", onps_error(enErr)); //*打印错误信息
……
功能
阻塞/非阻塞型,接受一个到达的tcp连接请求。
原型
SOCKET accept(SOCKET socket, in_addr_t *punCltIP, USHORT *pusCltPort, INT nWaitSecs, EN_ONPSERR *penErr);
入口参数
返回值
返回请求连接的tcp客户端的socket句柄;当没有新的客户端连接请求到达或协议栈报错时返回INVALID_SOCKET,具体的错误码通过penErr参数获得。
示例
EN_ONPSERR enErr;
……
SOCKET hSockSrv = socket(AF_INET, SOCK_STREAM, 0, &enErr);
if(INVALID_SOCKET != hSockSrv)
{
if(!bind(hSockSrv, NULL, 6411)) //* 绑定成功
{
if(!listen(hSockSrv, usBacklog)) //* 进入监听状态
{
//* 循环等待并处理到达的tcp连接请求
while(1)
{
in_addr_t unCltIP;
USHORT usCltPort;
SOCKET hSockClt = accept(hSockSrv, &unCltIP, &usCltPort, 1, &enErr);
if(INVALID_SOCKET != hSockClt) //* 返回了一个有效的客户端socket句柄
{
//* 新的客户端到达,在这里增加你的自定义代码
……
}
else
{
//* 错误码为ERRNO代表无错误发生,意味着没有新的客户端连接请求到达,回到循环开始处继续等待即可
if(ERRNO == enErr)
continue;
else //* 不等于ERRNO意味着协议栈报错,需要处理
{
……
printf("%s\r\n", onps_error(enErr)); //* 打印错误信息
}
}
}
}
else //* 失败
printf("%s\r\n", onps_get_last_error(hSocket, NULL)); //* 打印错误信息
}
else //* 绑定失败
printf("%s\r\n", onps_get_last_error(hSocket, NULL)); //* 打印错误信息
}
else
printf("%s\r\n", onps_error(enErr)); //*打印错误信息
……
功能
阻塞/非阻塞型,tcp服务器数据接收专用函数,等待任意一个或多个tcp客户端数据到达信号。协议栈利用rtos提供的信号量实现了一个poll模型,当有一个及以上的tcp客户端数据到达,均会触发一个信号到用户层,我们通过tcpsrv_recv_poll()函数等待这个信号。这个函数的第二个参数值表示这个函数最长等待多少秒,等待期间有任意一个或多个客户端数据到达则立即返回最先到达的这个客户端的socket,继续调用这个函数则继续返回下一个客户端socket,直至返回一个无效的socket才意味着当前所有已送达的数据均已读取完毕,已经没有任何客户端有新数据到达了。
原型
SOCKET tcpsrv_recv_poll(SOCKET hSocketSrv, INT nWaitSecs, EN_ONPSERR *penErr);
入口参数
返回值
返回已经收到数据的tcp客户端的socket句柄;当没有任何tcp客户端收到数据或协议栈报错时返回INVALID_SOCKET,具体的错误码通过penErr参数获得。
示例
//* 完成tcp服务器的数据读取工作
void tcp_server_recv(void *pvData)
{
SOCKET hSockClt;
EN_ONPSERR enErr;
INT nRcvBytes;
UCHAR ubaRcvBuf[100];
while(TRUE)
{
hSockClt = tcpsrv_recv_poll(l_hSockSrv, 1, &enErr);
if(INVALID_SOCKET != hSockClt) //* 有效的socket
{
//* 注意这里一定要尽量读取完毕该客户端的所有已到达的数据,因为每个客户端只有新数据到达时才会触发一个信号到用户层
//* ,如果你没有读取完毕就只能等到该客户端送达下一组数据时再读取了,这可能会导致数据处理延迟问题
while(TRUE)
{
//* 读取数据
nRcvBytes = recv(hSockClt, ubaRcvBuf, 256);
if(nRcvBytes > 0)
{
//* 原封不动的回送给客户端,利用回显来模拟服务器回馈应答报文的场景
send(hSockClt, ubaRcvBuf, nRcvBytes, 1);
}
else //* 已经读取完毕
{
if(nRcvBytes < 0)
{
//* 协议栈底层报错,这里需要增加你的容错代码处理这个错误并打印错误信息
printf("%s\r\n", onps_get_last_error(hSocket, NULL));
}
break;
}
}
}
else //* 无效的socket
{
//* 返回一个无效的socket时需要判断是否存在错误,如果不存在则意味着1秒内没有任何数据到达,否则打印这个错误
if(ERRNO != enErr)
printf("tcpsrv_recv_poll() failed, %s\r\n", onps_error(enErr));
}
}
}
功能
获取socket最近一次发生的错误,包括描述信息及错误编码。该函数其实是前面示例代码中出现的onps_get_last_error()函数的二次封装,功能及使用方式与之完全相同。
原型
const CHAR *socket_get_last_error(SOCKET socket, EN_ONPSERR *penErr);
入口参数
返回值
返回值为字符串指针,指向socket最近一次发生的错误描述字符串。
示例
略。
功能
获取socket最近一次发生的错误编码。
原型
EN_ONPSERR socket_get_last_error_code(SOCKET socket);
入口参数
返回值
返回值为socket最近一次发生的错误编码。
示例
略。
协议栈还提供了一组网络编程常见的工具函数以供用户使用,同时还提供了一些常用的比如字符串操作、16进制格式化转换输出等函数:
功能
实现64位长整型数的网络字节序转换。
原型
LONGLONG htonll(LONGLONG llVal);
入口参数
返回值
返回值为字节序转换后的64位长整型数。
示例
略。
功能
实现32位整型数的网络字节序转换。
原型
LONG htonl(LONG lVal);
入口参数
返回值
返回值为字节序转换后的32位整型数。
示例
略。
功能
实现16位短整型数的网络字节序转换。
原型
SHORT htonl(SHORT sVal);
入口参数
返回值
返回值为字节序转换后的16位短整型数。
示例
略。
功能
实现点分十进制1Pv4地址到4字节无符号整型地址的转换,即10.0.1.2转换为0x0A000102。
原型
in_addr_t inet_addr(const char *pszIP);
入口参数
返回值
返回值为无符号32位整型地址。
示例
略。
功能
实现点分十进制1Pv4地址到4字节无符号整型地址的转换,即10.0.1.2转换为0x0201000A。
原型
in_addr_t inet_addr_small(const char *pszIP);
入口参数
返回值
返回值为无符号32位整型地址。
示例
略。
功能
注意,这是一个线程不安全的函数,实现in_addr类型的地址到点分十进制1Pv4地址的转换。
原型
char *inet_ntoa(struct in_addr stInAddr);
入口参数
返回值
返回字符串指针,指向转换后的点分十进制格式的IPv4地址字符串。
示例
struct in_addr stAddr;
stSrcAddr.s_addr = inet_addr_small("192.168.0.9");
printf("%s\r\n", inet_ntoa(stAddr));
功能
注意,这是一个线程不安全的函数,实现4字节无符号整型地址到点分十进制1Pv4地址的转换。
原型
char *inet_ntoa_ext(in_addr_t unAddr);
入口参数
返回值
返回字符串指针,指向转换后的点分十进制格式的IPv4地址字符串。
示例
in_addr_t unAddr = inet_addr_small("192.168.0.9");
printf("%s\r\n", inet_ntoa_ext(unAddr));
功能
注意,这是一个线程安全的函数,实现in_addr类型的地址到点分十进制1Pv4地址的转换。
原型
char *inet_ntoa_safe(struct in_addr stInAddr, char *pszAddr);
入口参数
返回值
返回字符串指针,指向转换后的点分十进制格式的IPv4地址字符串,其地址其实就是参数pszAddr指向的地址。
示例
CHAR szAddr[20];
struct in_addr stAddr;
stSrcAddr.s_addr = inet_addr_small("192.168.0.9");
printf("%s\r\n", inet_ntoa_safe(stAddr, szAddr));
功能
注意,这是一个线程安全的函数,实现4字节无符号整型地址到点分十进制1Pv4地址的转换。
原型
char *inet_ntoa_safe_ext(in_addr_t unAddr, char *pszAddr);
入口参数
返回值
返回值为字符串指针,指向转换后的点分十进制格式的IPv4地址字符串,其地址其实就是参数pszAddr指向的地址。
示例
CHAR szAddr[20];
in_addr_t unAddr = inet_addr_small("192.168.0.9");
printf("%s\r\n", inet_ntoa_safe_ext(unAddr, szAddr));
功能
比较两个IPv4地址是否属于同一网段。
原型
BOOL ip_addressing(in_addr_t un1stIp, in_addr_t un2ndIp, in_addr_t unGenmask);
入口参数
返回值
返回TRUE表示在同一网段,FALSE则不属于同一网段。
示例
略
功能
线程安全的strtok函数。
原型
CHAR *strtok_safe(CHAR **ppszStart, const CHAR *pszSplitStr);
入口参数
返回值
返回字符串指针,指向下一个分隔符之前的字符串;返回值为NULL则截取完毕。
示例
CHAR szTestStr[64];
sprintf(szTestStr, "123;456;789,ABC;,EFG");
CHAR *pszStart = szTestStr;
CHAR *pszItem = strtok_safe(&pszStart, ";");
while(NULL != pszItem)
{
printf("%s\r\n", pszItem);
pszItem = strtok_safe(&pszStart, ";");
}
功能
将16进制数据格式化为字符串。
原型
void snprintf_hex(const UCHAR *pubHexData, USHORT usHexDataLen, CHAR *pszDstBuf, UINT unDstBufSize, BOOL blIsSeparate);
入口参数
返回值
无
示例
UCHAR ubHexData[16] = "\xAB\xCD\x2A\x1F\x3C\x4D";
CHAR szHexDataStr[64];
snprintf_hex(ubHexData, 6, szHexDataStr, 64, TRUE);
printf("%s\r\n", szHexDataStr);
功能
将16进制数据格式化为字符串输出到控制台。
原型
void printf_hex(const UCHAR *pubHexData, USHORT usHexDataLen, UCHAR ubBytesPerLine);
入口参数
返回值
无
示例
UCHAR ubHexData[16] = "\xAB\xCD\x2A\x1F\x3C\x4D\xAA\x4E\xFE\x45\x6B\x9A\x05\x71\x8E\x1B\x52\x78";
printf_hex(ubHexData, 18, 16);
功能
将协议栈返回的错误码转换成具体的描述字符串。
原型
const CHAR *onps_error(EN_ONPSERR enErr);
入口参数
返回值
返回值为字符串指针,指向具体的错误描述字符串的指针。
示例
略
手机扫一扫
移动阅读更方便
你可能感兴趣的文章