本帖最后由 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目录下,不定期更新
目录结构上的一些说明:
netif是底层的网络接口,目前底层只实现了eth以太网,其他的什么PPP等,都没有做。
proto下面是一些支持的协议,目前只有DHCP,以后会陆续增加DNS,HTTP等
vsfip.c是核心的协议栈代码,vsfip_buffer.c是用户根据应用,使用不同的内存分配方法
代码导读(以太网为例):
1. 首先看vsfip_buffer.c中的内存处理
2. 数据接收过程
- void vsfip_eth_input(struct vsfip_buffer_t *buf)
- {
- struct vsfip_ethhead_t *head = (struct vsfip_ethhead_t *)buf->buf.buffer;
-
- buf->buf.buffer += sizeof(struct vsfip_ethhead_t);
- buf->buf.size -= sizeof(struct vsfip_ethhead_t);
- switch (BE_TO_SYS_U16(head->type))
- {
- case VSFIP_ETH_TYPE_IP:
- vsfip_netif_ip4_input(buf);
- break;
- case VSFIP_ETH_TYPE_IP6:
- vsfip_netif_ip6_input(buf);
- break;
- case VSFIP_ETH_TYPE_ARP:
- vsfip_netif_arp_input(buf);
- break;
- default:
- case VSFIP_ETH_TYPE_RARP:
- // not supported
- vsfip_buffer_release(buf);
- }
- }
底层驱动收到以太网数据包后,调用vsfip_eth_input,如果是ARP,调用vsfip_netif_arp_input交由netif通用层里的ARP处理,如果是IPv4或者IPv6,调用vsfip_netif_ip4_input或者vsfip_netif_ip6_input。
- switch (head->op)
- {
- case ARP_REQUEST:
- // for ARP request, send sem to arps.sm
- bufptr = (uint8_t *)head + sizeof(struct vsfip_arphead_t);
- if ((head->hwlen != netif->macaddr.size) ||
- (head->protolen != netif->ipaddr.size) ||
- (memcmp(bufptr + 2 * head->hwlen + head->protolen,
- netif->ipaddr.addr.s_addr_buf, head->protolen)))
- {
- break;
- }
-
- buf->next = NULL;
- vsfip_bufferlist_queue(&netif->arps.requestlist, buf);
- vsfsm_sem_post(&netif->arps.sem);
- return;
- 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协议栈。
- /* if (is multicast)
- {
- }
- else
- */ {
- switch (iphead->proto)
- {
- case IPPROTO_UDP:
- vsfip_udp_input(buf);
- break;
- case IPPROTO_TCP:
- vsfip_tcp_input(buf);
- break;
- case IPPROTO_ICMP:
- vsfip_icmp_input(buf);
- break;
- default:
- vsfip_buffer_release(buf);
- break;
- }
- }
vsfip_ip4_input解析报文后,会根据IP头的设置,处理IP组包(特么设想很好啊,现在根本没有支持)。如果组包得到一个完整的IP报文,则根据类型,调用vsfip_udp_input/vsfip_tcp_incpu/vsfip_icmp_input。就用简单的UDP举例吧。
- static void vsfip_udp_input(struct vsfip_buffer_t *buf)
- {
- struct vsfip_udphead_t *udphead = (struct vsfip_udphead_t *)buf->buf.buffer;
-
- // endian fix
- udphead->port.src = BE_TO_SYS_U16(udphead->port.src);
- udphead->port.dst = BE_TO_SYS_U16(udphead->port.dst);
- udphead->len = BE_TO_SYS_U16(udphead->len);
- udphead->checksum = BE_TO_SYS_U16(udphead->checksum);
-
- buf->app.buffer = buf->buf.buffer + sizeof(struct vsfip_udphead_t);
- buf->app.size = buf->buf.size - sizeof(struct vsfip_udphead_t);
- vsfip_proto_input(vsfip.udp_listeners, buf, &udphead->port);
- }
vsfip_udp_input处理好UDP报文头后,会调用vsfip_proto_inpput(通用的数据处理)。
- if (socket->callback.input != NULL)
- {
- socket->callback.input(socket->callback.param, buf);
- }
- else
- {
- buf->ttl = VSFIP_CFG_TTL_INPUT;
- vsfip_bufferlist_queue(&socket->input_bufferlist, buf);
- vsfsm_sem_post(&socket->input_sem);
- }
vsfip_proto_input也都是一些无聊的代码,匹配socket,并且如果socket设置了callback,就直接调用callback,如果没有的话,就把收到的报文加进input_bufferlist队列,并且发送事件给socket的input_sem。
- if (socket->callback.input != NULL)
- {
- socket->callback.input(socket->callback.param, buf);
- }
- else
- {
- buf->ttl = VSFIP_CFG_TTL_INPUT;
- vsfip_bufferlist_queue(&socket->input_bufferlist, buf);
- vsfsm_sem_post(&socket->input_sem);
- }
UDP可以通过设置callback来实现callback方式,顺便说一下,TCP不支持callback,是因为TCP本来就是callback,所以API接口上,就不能使用callback。
- vsf_err_t vsfip_udp_recv(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
- struct vsfip_socket_t *socket, struct vsfip_sockaddr_t *sockaddr,
- struct vsfip_buffer_t **buf)
- {
- vsfsm_pt_begin(pt);
-
- if ((NULL == socket) || (NULL == sockaddr) || (NULL == buf) ||
- !socket->local_sockaddr.sin_port)
- {
- return VSFERR_INVALID_PARAMETER;
- }
-
- socket->remote_sockaddr = *sockaddr;
-
- *buf = vsfip_udp_match(socket, sockaddr);
- if (*buf != NULL)
- {
- return VSFERR_NONE;
- }
-
- // set pending with timeout
- if (socket->timeout_ms)
- {
- socket->rx_timer.interval = socket->timeout_ms;
- socket->rx_timer.sm = pt->sm;
- socket->rx_timer.evt = VSFIP_EVT_SOCKET_TIMEOUT;
- vsftimer_register(&socket->rx_timer);
- }
-
- while (1)
- {
- if (vsfsm_sem_pend(&socket->input_sem, pt->sm))
- {
- evt = VSFSM_EVT_NONE;
- vsfsm_pt_entry(pt);
- if ((evt != VSFIP_EVT_SOCKET_RECV) &&
- (evt != VSFIP_EVT_SOCKET_TIMEOUT))
- {
- return VSFERR_NOT_READY;
- }
- if (VSFIP_EVT_SOCKET_RECV == evt)
- {
- *buf = vsfip_udp_match(socket, sockaddr);
- if (*buf != NULL)
- {
- vsftimer_unregister(&socket->rx_timer);
- return VSFERR_NONE;
- }
- }
- else if (VSFIP_EVT_SOCKET_TIMEOUT == evt)
- {
- vsftimer_unregister(&socket->rx_timer);
- vsfsm_sync_cancel(&socket->input_sem, pt->sm);
- return VSFERR_FAIL;
- }
- }
- else
- {
- *buf = vsfip_udp_match(socket, sockaddr);
- if (*buf != NULL)
- {
- vsftimer_unregister(&socket->rx_timer);
- return VSFERR_NONE;
- }
- }
- }
-
- vsfsm_pt_end(pt);
- return VSFERR_NONE;
- }
应用层调用了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接口。
- static vsf_err_t vsfip_ip4_output(struct vsfip_socket_t *socket)
- {
- struct vsfip_ippcb_t *pcb = &socket->pcb.ippcb;
- vsf_err_t err = VSFERR_NONE;
-
- if (pcb->buf->buf.size >
- (pcb->buf->netif->mtu - sizeof(struct vsfip_ip4head_t)))
- {
- err = VSFERR_NOT_ENOUGH_RESOURCES;
- goto cleanup;
- }
-
- pcb->id = vsfip.ip_id++;
- err = vsfip_add_ip4head(socket);
- if (err) goto cleanup;
-
- return vsfip_netif_ip_output(pcb->buf);
- cleanup:
- vsfip_buffer_release(pcb->buf);
- return err;
- }
这里就只说vsfip_ip4_output,也就是一样操作add_head,调用vsfip_netif_ip_output交由netif层去处理。
- vsf_err_t vsfip_netif_ip_output(struct vsfip_buffer_t *buf)
- {
- struct vsfip_netif_t *netif = buf->netif;
- struct vsfip_macaddr_t *mac;
- struct vsfip_ipaddr_t dest;
- vsf_err_t err = VSFERR_NONE;
-
- vsfip_netif_get_ipaddr(buf, &dest);
- mac = vsfip_netif_get_mac(netif, &dest);
- if (NULL == mac)
- {
- vsfip_bufferlist_queue(&netif->arpc.requestlist, buf);
- err = vsfsm_sem_post(&netif->arpc.sem);
- if (err) goto cleanup; else goto end;
- }
- else if (buf->buf.size)
- {
- return vsfip_netif_ip_output_do(buf, VSFIP_NETIF_PROTO_IP, mac);
- }
-
- cleanup:
- vsfip_buffer_release(buf);
- end:
- return err;
- }
vsfip_netif_ip_output里,会先从ARP缓冲里匹配MAC地址,如果没有匹配到的话,那就加到arpc(ARP client)的requestlist里去,并发送事件。之后ARPC会执行ARP,得到MAC后,把这个报文发给驱动层处理。
注意,无论成功还是出错,传进来的buf都需要被release。
其他N多细节,自己看吧。
TCP的状态机比较复杂,目前也在完善中。
外包兼职人员,有问题可以直接问我,希望参与的人,玩的开心。
|