[应用相关] 没事开个源,只准看不准用,随便玩玩的TCPIP协议栈

[复制链接]
4194|19
 楼主| Simon21ic 发表于 2015-5-1 02:03 | 显示全部楼层 |阅读模式
本帖最后由 Simon21ic 于 2015-5-1 13:47 编辑

开发目的:没有目的,闲的蛋疼+钱多了没地方用
开源目的:为了以后找TCPIP相关的外包和兼职人员更加方便

不建议非兼职/外包人员放着成熟稳定的uip和lwip不用,而用我的协议栈。

一些基本的说明:
1. 针对cortexM或者类似等级的处理器
2. 基本的系统构架是基于vsfsm的协作式多任务内核,vsfip的API接口,都是PT函数
3. 协议栈规模介于uip和lwip之间
4. UDP应用层可以只用PT线程的方式,也可以使用回调,TCP只支持PT线程
5. 面向对象的代码
6. buffer结构不使用链表,直接一个连续的内存,简单粗暴
7. socket层的API接口,类似windows和linux的socket接口
8. 巨多的BUG,以及各种明坑暗坑
9. 总之,最大的特点就是没特点,非兼职外包人员绕道

开源地址,在vsf的stack里:github.com/versaloon/vsf
vsf/stack/vsfip目录下,不定期更新

目录结构上的一些说明:
vsfip.png
netif是底层的网络接口,目前底层只实现了eth以太网,其他的什么PPP等,都没有做。
proto下面是一些支持的协议,目前只有DHCP,以后会陆续增加DNS,HTTP等
vsfip.c是核心的协议栈代码,vsfip_buffer.c是用户根据应用,使用不同的内存分配方法

代码导读(以太网为例):
1. 首先看vsfip_buffer.c中的内存处理
2. 数据接收过程
  1. void vsfip_eth_input(struct vsfip_buffer_t *buf)
  2. {
  3.         struct vsfip_ethhead_t *head = (struct vsfip_ethhead_t *)buf->buf.buffer;
  4.         
  5.         buf->buf.buffer += sizeof(struct vsfip_ethhead_t);
  6.         buf->buf.size -= sizeof(struct vsfip_ethhead_t);
  7.         switch (BE_TO_SYS_U16(head->type))
  8.         {
  9.         case VSFIP_ETH_TYPE_IP:
  10.                 vsfip_netif_ip4_input(buf);
  11.                 break;
  12.         case VSFIP_ETH_TYPE_IP6:
  13.                 vsfip_netif_ip6_input(buf);
  14.                 break;
  15.         case VSFIP_ETH_TYPE_ARP:
  16.                 vsfip_netif_arp_input(buf);
  17.                 break;
  18.         default:
  19.         case VSFIP_ETH_TYPE_RARP:
  20.                 // not supported
  21.                 vsfip_buffer_release(buf);
  22.         }
  23. }
底层驱动收到以太网数据包后,调用vsfip_eth_input,如果是ARP,调用vsfip_netif_arp_input交由netif通用层里的ARP处理,如果是IPv4或者IPv6,调用vsfip_netif_ip4_input或者vsfip_netif_ip6_input。
  1.         switch (head->op)
  2.         {
  3.         case ARP_REQUEST:
  4.                 // for ARP request, send sem to arps.sm
  5.                 bufptr = (uint8_t *)head + sizeof(struct vsfip_arphead_t);
  6.                 if ((head->hwlen != netif->macaddr.size) ||
  7.                         (head->protolen != netif->ipaddr.size) ||
  8.                         (memcmp(bufptr + 2 * head->hwlen + head->protolen,
  9.                                         netif->ipaddr.addr.s_addr_buf, head->protolen)))
  10.                 {
  11.                         break;
  12.                 }
  13.                
  14.                 buf->next = NULL;
  15.                 vsfip_bufferlist_queue(&netif->arps.requestlist, buf);
  16.                 vsfsm_sem_post(&netif->arps.sem);
  17.                 return;
  18.                 break;
netif中的ARP,自己看代码,实现了2个线程,分别用于ARP server以及ARP client。vsfip_netif_arp_input会把ARP_REQUEST的报文放到arps.requestlist队列里,并且发送信号,等待ARP server处理。ARP server收到信号后,解析ARP请求,并且发送应答(TODO:之后会优化掉ARP server进程,直接解析并发送应答)。
vsfip_netif_ip4_input只是一个netif层中的过渡接口,直接调用vsfip_ip4_input提交给TCPIP协议栈。
  1. /*        if (is multicast)
  2.         {
  3.         }
  4.         else
  5. */        {
  6.                 switch (iphead->proto)
  7.                 {
  8.                 case IPPROTO_UDP:
  9.                         vsfip_udp_input(buf);
  10.                         break;
  11.                 case IPPROTO_TCP:
  12.                         vsfip_tcp_input(buf);
  13.                         break;
  14.                 case IPPROTO_ICMP:
  15.                         vsfip_icmp_input(buf);
  16.                         break;
  17.                 default:
  18.                         vsfip_buffer_release(buf);
  19.                         break;
  20.                 }
  21.         }
vsfip_ip4_input解析报文后,会根据IP头的设置,处理IP组包(特么设想很好啊,现在根本没有支持)。如果组包得到一个完整的IP报文,则根据类型,调用vsfip_udp_input/vsfip_tcp_incpu/vsfip_icmp_input。就用简单的UDP举例吧。
  1. static void vsfip_udp_input(struct vsfip_buffer_t *buf)
  2. {
  3.         struct vsfip_udphead_t *udphead = (struct vsfip_udphead_t *)buf->buf.buffer;
  4.         
  5.         // endian fix
  6.         udphead->port.src = BE_TO_SYS_U16(udphead->port.src);
  7.         udphead->port.dst = BE_TO_SYS_U16(udphead->port.dst);
  8.         udphead->len = BE_TO_SYS_U16(udphead->len);
  9.         udphead->checksum = BE_TO_SYS_U16(udphead->checksum);
  10.         
  11.         buf->app.buffer = buf->buf.buffer + sizeof(struct vsfip_udphead_t);
  12.         buf->app.size = buf->buf.size - sizeof(struct vsfip_udphead_t);
  13.         vsfip_proto_input(vsfip.udp_listeners, buf, &udphead->port);
  14. }
vsfip_udp_input处理好UDP报文头后,会调用vsfip_proto_inpput(通用的数据处理)。
  1.         if (socket->callback.input != NULL)
  2.         {
  3.                 socket->callback.input(socket->callback.param, buf);
  4.         }
  5.         else
  6.         {
  7.                 buf->ttl = VSFIP_CFG_TTL_INPUT;
  8.                 vsfip_bufferlist_queue(&socket->input_bufferlist, buf);
  9.                 vsfsm_sem_post(&socket->input_sem);
  10.         }
vsfip_proto_input也都是一些无聊的代码,匹配socket,并且如果socket设置了callback,就直接调用callback,如果没有的话,就把收到的报文加进input_bufferlist队列,并且发送事件给socket的input_sem。
  1.         if (socket->callback.input != NULL)
  2.         {
  3.                 socket->callback.input(socket->callback.param, buf);
  4.         }
  5.         else
  6.         {
  7.                 buf->ttl = VSFIP_CFG_TTL_INPUT;
  8.                 vsfip_bufferlist_queue(&socket->input_bufferlist, buf);
  9.                 vsfsm_sem_post(&socket->input_sem);
  10.         }
UDP可以通过设置callback来实现callback方式,顺便说一下,TCP不支持callback,是因为TCP本来就是callback,所以API接口上,就不能使用callback。
  1. vsf_err_t vsfip_udp_recv(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
  2.                         struct vsfip_socket_t *socket, struct vsfip_sockaddr_t *sockaddr,
  3.                         struct vsfip_buffer_t **buf)
  4. {
  5.         vsfsm_pt_begin(pt);
  6.         
  7.         if ((NULL == socket) || (NULL == sockaddr) || (NULL == buf) ||
  8.                 !socket->local_sockaddr.sin_port)
  9.         {
  10.                 return VSFERR_INVALID_PARAMETER;
  11.         }
  12.         
  13.         socket->remote_sockaddr = *sockaddr;
  14.         
  15.         *buf = vsfip_udp_match(socket, sockaddr);
  16.         if (*buf != NULL)
  17.         {
  18.                 return VSFERR_NONE;
  19.         }
  20.         
  21.         // set pending with timeout
  22.         if (socket->timeout_ms)
  23.         {
  24.                 socket->rx_timer.interval = socket->timeout_ms;
  25.                 socket->rx_timer.sm = pt->sm;
  26.                 socket->rx_timer.evt = VSFIP_EVT_SOCKET_TIMEOUT;
  27.                 vsftimer_register(&socket->rx_timer);
  28.         }
  29.         
  30.         while (1)
  31.         {
  32.                 if (vsfsm_sem_pend(&socket->input_sem, pt->sm))
  33.                 {
  34.                         evt = VSFSM_EVT_NONE;
  35.                         vsfsm_pt_entry(pt);
  36.                         if ((evt != VSFIP_EVT_SOCKET_RECV) &&
  37.                                 (evt != VSFIP_EVT_SOCKET_TIMEOUT))
  38.                         {
  39.                                 return VSFERR_NOT_READY;
  40.                         }
  41.                         if (VSFIP_EVT_SOCKET_RECV == evt)
  42.                         {
  43.                                 *buf = vsfip_udp_match(socket, sockaddr);
  44.                                 if (*buf != NULL)
  45.                                 {
  46.                                         vsftimer_unregister(&socket->rx_timer);
  47.                                         return VSFERR_NONE;
  48.                                 }
  49.                         }
  50.                         else if (VSFIP_EVT_SOCKET_TIMEOUT == evt)
  51.                         {
  52.                                 vsftimer_unregister(&socket->rx_timer);
  53.                                 vsfsm_sync_cancel(&socket->input_sem, pt->sm);
  54.                                 return VSFERR_FAIL;
  55.                         }
  56.                 }
  57.                 else
  58.                 {
  59.                         *buf = vsfip_udp_match(socket, sockaddr);
  60.                         if (*buf != NULL)
  61.                         {
  62.                                 vsftimer_unregister(&socket->rx_timer);
  63.                                 return VSFERR_NONE;
  64.                         }
  65.                 }
  66.         }
  67.         
  68.         vsfsm_pt_end(pt);
  69.         return VSFERR_NONE;
  70. }
应用层调用了vsfip_udp_recv后,会先在input_bufferlist里看看有没有要的数据,没有的话,如果需要就注册个超时定时器,之后就等待input_sem信号。vsfip_proto_input发送了input_sem信号后,就会唤醒这个线程,继续运行下去。之后当然就是判断唤醒的时间,可以是接收到到数据的时间,也可以是定时器发来的超时事件。注意,这2个事件是互斥的,收到一个后,要取消掉另一个。
注意,数据报文的内存,是底层驱动,调用vsfip_buffer_get分配的,并且在处理过程中,任何层里,一旦处理完成,必须调用vsfip_buffer_release释放。

3. 发送过程,还是用UDP,这个就太简单了,UDP发送虽然接口上是使用PT,但是,代码完全是非阻塞的,直接add_head后,调用vsfip_ip_output发送IP报文。vsfip_proto_input会根据版本,调用IPv4或者IPv6的output接口。
  1. static vsf_err_t vsfip_ip4_output(struct vsfip_socket_t *socket)
  2. {
  3.         struct vsfip_ippcb_t *pcb = &socket->pcb.ippcb;
  4.         vsf_err_t err = VSFERR_NONE;
  5.        
  6.         if (pcb->buf->buf.size >
  7.                         (pcb->buf->netif->mtu - sizeof(struct vsfip_ip4head_t)))
  8.         {
  9.                 err = VSFERR_NOT_ENOUGH_RESOURCES;
  10.                 goto cleanup;
  11.         }
  12.        
  13.         pcb->id = vsfip.ip_id++;
  14.         err = vsfip_add_ip4head(socket);
  15.         if (err) goto cleanup;
  16.        
  17.         return vsfip_netif_ip_output(pcb->buf);
  18. cleanup:
  19.         vsfip_buffer_release(pcb->buf);
  20.         return err;
  21. }
这里就只说vsfip_ip4_output,也就是一样操作add_head,调用vsfip_netif_ip_output交由netif层去处理。
  1. vsf_err_t vsfip_netif_ip_output(struct vsfip_buffer_t *buf)
  2. {
  3.         struct vsfip_netif_t *netif = buf->netif;
  4.         struct vsfip_macaddr_t *mac;
  5.         struct vsfip_ipaddr_t dest;
  6.         vsf_err_t err = VSFERR_NONE;
  7.        
  8.         vsfip_netif_get_ipaddr(buf, &dest);
  9.         mac = vsfip_netif_get_mac(netif, &dest);
  10.         if (NULL == mac)
  11.         {
  12.                 vsfip_bufferlist_queue(&netif->arpc.requestlist, buf);
  13.                 err = vsfsm_sem_post(&netif->arpc.sem);
  14.                 if (err) goto cleanup; else goto end;
  15.         }
  16.         else if (buf->buf.size)
  17.         {
  18.                 return vsfip_netif_ip_output_do(buf, VSFIP_NETIF_PROTO_IP, mac);
  19.         }
  20.        
  21. cleanup:
  22.         vsfip_buffer_release(buf);
  23. end:
  24.         return err;
  25. }
vsfip_netif_ip_output里,会先从ARP缓冲里匹配MAC地址,如果没有匹配到的话,那就加到arpc(ARP client)的requestlist里去,并发送事件。之后ARPC会执行ARP,得到MAC后,把这个报文发给驱动层处理。
注意,无论成功还是出错,传进来的buf都需要被release。

其他N多细节,自己看吧。
TCP的状态机比较复杂,目前也在完善中。
外包兼职人员,有问题可以直接问我,希望参与的人,玩的开心。
wank2887 发表于 2015-5-1 08:44 | 显示全部楼层
先收藏 :lol
lkl0305 发表于 2015-5-1 09:53 | 显示全部楼层
学习下哈
那就地方iv 发表于 2015-5-1 10:07 | 显示全部楼层
谢谢楼主分享
mochou 发表于 2015-5-1 16:26 | 显示全部楼层
charrijon 发表于 2015-5-2 16:52 | 显示全部楼层
谢谢,正好在玩这个。用stm32发送UDP包给PC,在PC中截数据包看到的数据都正确,可是用网络调试助手,老是读不到,求教啊
Serge_Ding 发表于 2015-5-2 20:14 | 显示全部楼层
学习了,楼主好强大
 楼主| Simon21ic 发表于 2015-5-3 05:06 | 显示全部楼层
charrijon 发表于 2015-5-2 16:52
谢谢,正好在玩这个。用stm32发送UDP包给PC,在PC中截数据包看到的数据都正确,可是用网络调试助手,老是读 ...

我敢打赌,你正好玩的不是我的协议栈。
 楼主| Simon21ic 发表于 2015-5-3 05:07 | 显示全部楼层
Serge_Ding 发表于 2015-5-2 20:14
学习了,楼主好强大

在TCPIP方面确实不强,没什么经验,这个是第一次做
小浣熊 发表于 2015-5-3 22:18 | 显示全部楼层
全面细致,深入剖析。。。
 楼主| Simon21ic 发表于 2015-5-4 00:45 | 显示全部楼层
小浣熊 发表于 2015-5-3 22:18
全面细致,深入剖析。。。

这个只是最粗略的,详细的要自己看了
charrijon 发表于 2015-5-5 18:58 | 显示全部楼层
本帖最后由 charrijon 于 2015-5-5 21:36 编辑
Simon21ic 发表于 2015-5-3 05:06
我敢打赌,你正好玩的不是我的协议栈。

的确不是你的协议栈,我是自己按照以前51的例程改的,目前与网络调试工具已经调通。但是有一件奇怪的事情,我如果按照全双工的模式工作,即pc端与stm端各自发送数据,那么pc发送的数据就会出错,用抓包软件看的。stm发送至pc端的数据照样正确,不知道是什么原因,还要查
xuan309170083 发表于 2015-5-5 21:28 | 显示全部楼层
81190865 发表于 2015-5-6 12:20 | 显示全部楼层
阳光豆苗 发表于 2015-5-6 13:41 | 显示全部楼层
感觉挺牛B。顶一个。
yinhaix 发表于 2015-5-7 14:12 | 显示全部楼层
谢谢楼主分享
andydisplay 发表于 2015-5-7 16:07 | 显示全部楼层
yhy123456 发表于 2015-5-7 20:53 | 显示全部楼层
路过 ,看看
 楼主| Simon21ic 发表于 2015-5-10 01:29 | 显示全部楼层
charrijon 发表于 2015-5-5 18:58
的确不是你的协议栈,我是自己按照以前51的例程改的,目前与网络调试工具已经调通。但是有一件奇怪的事情 ...

TCPIP协议栈以及底层驱动,甚至还包括高层的应用,调试稳定并不是很容易的
有时候,花了几天调试的问题,结果就只是一个小地方而已,不过乐趣不就在这里吗?
搞IT的 发表于 2015-5-10 10:19 | 显示全部楼层
我也正想研究一下,具体项目中还没用到过。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:www.versaloon.com --- under construction

266

主题

2597

帖子

104

粉丝
快速回复 在线客服 返回列表 返回顶部