设为首页 加入收藏

TOP

onps栈使用说明(3)——tcp、udp通讯测试(三)
2023-07-23 13:31:21 】 浏览:152
Tags:onps tcp udp 通讯测
ytes > 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)); break; } } } } int main(void) { EN_ONPSERR enErr; if(open_npstack_load(&enErr)) { printf("The open source network protocol stack (ver %s) is loaded successfully. \r\n", ONPS_VER); //* 协议栈加载成功,在这里初始化ethernet网卡,并注册网卡到协议栈 emac_init(); } else { printf("The open source network protocol stack failed to load, %s\r\n", onps_error(enErr)); return -1; } //* 启动tcp服务器 l_hSockSrv = tcp_server_start(LTCPSRV_PORT, LTCPSRV_BACKLOG_NUM); if(INVALID_SOCKET != l_hSockSrv) { //* 在这里添加工作线程启动代码,启动tcp服务器数据读取线程THTcpSrvRead …… } //* 进入主线程的主逻辑处理循环,等待tcp客户端连接请求到来 while(TRUE) { //* 接受连接请求 in_addr_t unCltIP; USHORT usCltPort; SOCKET hSockClt = accept(l_hSockSrv, &unCltIP, &usCltPort, 1, &enErr); if(INVALID_SOCKET != hSockClt) { //* 在这里你自己的代码处理新到达的客户端 …… } else { printf("accept() failed, %s\r\n", onps_error(enErr)); break; } } //* 关闭socket,释放占用的协议栈资源 close(l_hSockSrv); return 0; }

编写tcp服务器的几个主要步骤: 

  1. 调用socket函数,申请一个数据流(tcp)类型的socket;
  2. bind()函数绑定一个ip地址和端口号;
  3. listen()函数启动监听;
  4. accept()函数接受一个tcp连接请求;
  5. 调用tcpsrv_recv_poll()函数利用协议栈提供的poll模型(非传统的select模型)等待客户端数据到达;
  6. 调用recv()函数读取客户端数据并处理之,直至所有数据读取完毕返回第5步,获取下一个已送达数据的客户端socket;
  7. 定期检查不活跃的客户端,调用close()函数关闭tcp链路,释放客户端占用的协议栈资源;

与传统的tcp服务器编程并没有两样。

       协议栈实现了一个poll模型用于服务器的数据读取。poll模型利用了rtos的信号量机制。当某个tcp服务器端口有一个或多个客户端有新的数据到达时,协议栈会立即投递一个或多个信号到用户层。注意,协议栈投递信号的数量取决于新数据到达的次数(tcp层每收到一个携带数据的tcp报文记一次),与客户端数量无关。用户通过tcpsrv_recv_poll()函数得到这个信号,并得到最先送达数据的客户端socket,然后读取该客户端送达的数据。注意这里一定要把所有数据读取出来。因为信号被投递的唯一条件就是有新的数据到达。没有信号, tcpsrv_recv_poll()函数无法得到一个有效的客户端socket,那么剩余数据就只能等到该客户端再次送达新数据时再读了。

       其实,poll模型的运作机制非常简单。tcp服务器每收到一组新的数据,就会将该数据所属的客户端socket放入接收队列尾部,然后投信号。所以,数据到达、获取socket与投递信号是一系列的连锁反应,且一一对应。tcpsrv_recv_poll()函数则在用户层接着完成连锁反应的后续动作:等信号、摘取接收队列首部节点、取出首部节点保存的socket、返回该socket以告知用户立即读取数据。非常简单明了,没有任何拖泥带水。从这个运作机制我们可以看出:

  1. poll模型的运转效率取决于rtos的信号量处理效率;
  2. tcpsrv_recv_poll()函数每次返回的socket有可能是同一个客户端的,也可能是不同客户端;
  3. 单个客户端已送达的数据长度与信号并不一一对应,一一对应的是该客户端新数据到达的次数与信号投递的次数,所以当数据读取次数小于信号数时,存在读取数据长度为0的情形;
  4. tcpsrv_recv_poll()函数返回有效的sokcet后,尽量读取全部数据到用户层进行处理,否则会出现剩余数据无法读取的情形,如果客户端不再上发新的数据的话;

6. udp通讯

       相比tcp,udp通讯功能的实现相对简单很多。为udp绑定一个固定端口其就可以作为服务器使用,反之则作为一个客户端使用。

……
#include "onps.h"

#define RUDPSRV_IP   "192.168.0.2" //* 远端udp服务器的地址
#define RUDPSRV_PORT 6416          //* 远端udp服务器的端口
#define LUDPSRV_PORT 6415          //* 本地udp服务器的端口

//* udp通讯用缓冲区(接收和发送均使用)
static UCHAR l_ubaUdpBuf[256];

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 0
        emac_init(); //* etherne
首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇STM32F7xx外设驱动2-delay(寄存.. 下一篇STM32F7xx外设驱动7-dac(寄存器)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目