本帖最后由 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的状态机比较复杂,目前也在完善中。
外包兼职人员,有问题可以直接问我,希望参与的人,玩的开心。
|