本例以LWIP为例,学习LWIP的数据结构。
LWIP经过分层之后,每一层函数所需要使用的数据结构都是不同的。
在硬件访问层,最关键的结构体是struct netif。
struct netif {
/** pointer to next in linked list */
struct netif *next;
/** IP address configuration in network byte order */
ip_addr_t ip_addr;
ip_addr_t netmask;
ip_addr_t gw;
/** This function is called by the network device driver
* to pass a packet up the TCP/IP stack. */
netif_input_fn input;
/** This function is called by the IP module when it wants
* to send a packet on the interface. This function typically
* first resolves the hardware address, then sends the packet. */
netif_output_fn output;
/** This function is called by the ARP module when it wants
* to send a packet on the interface. This function outputs
* the pbuf as-is on the link medium. */
netif_linkoutput_fn linkoutput;
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
/** maximum transfer unit (in bytes) */
u16_t mtu;
/** number of bytes used in hwaddr */
u8_t hwaddr_len;
/** link level hardware address of this interface */
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
/** flags (see NETIF_FLAG_ above) */
u8_t flags;
};
struct ip_addr {
u32_t addr;
};
typedef struct ip_addr ip_addr_t;
这个结构体NETIF,描述了一个网络接口所需的硬件信息,并提供了向下的硬件接口层的函数接口。input和output。
low_level_input会返回一个PBUF的结构体。
struct pbuf {
/** next pbuf in singly linked pbuf chain */
struct pbuf *next;
/** pointer to the actual data in the buffer */
void *payload;
/**
* total length of this buffer and all next buffers in chain
* belonging to the same packet.
*
* For non-queue packet chains this is the invariant:
* p->tot_len == p->len + (p->next? p->next->tot_len: 0)
*/
u16_t tot_len;
/** length of this buffer */
u16_t len;
/** pbuf_type as u8_t instead of enum to save space */
u8_t /*pbuf_type*/ type;
/** misc flags */
u8_t flags;
/**
* the reference count always equals the number of pointers
* that refer to this pbuf. This can be pointers from an application,
* the stack itself, or pbuf->next pointers from a chain.
*/
u16_t ref;
};
可以看到,PBUF是一个单链表的链节(LinkNode),用来描述一个Package的一个Segment。
多个PBUF可以组成一个Package。
另外,PBUF的TYPE,可以表示Package是哪一层的数据包。这样,协议栈的每一层,都用PBUF来传递Package,但是由于TYPE是不同的,所以不同的层能够正确的处理PBUF。
#define IP_PCB \
/* ip addresses in network byte order */ \
ip_addr_t local_ip; \
ip_addr_t remote_ip; \
/* Socket options */ \
u8_t so_options; \
/* Type Of Service */ \
u8_t tos; \
/* Time To Live */ \
u8_t ttl \
/* link layer address resolution hint */ \
IP_PCB_ADDRHINT
struct ip_pcb {
/* Common members of all PCB types */
IP_PCB;
};
#define TCP_PCB_COMMON(type) \
type *next; /* for the linked list */ \
void *callback_arg; \
/* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \
DEF_ACCEPT_CALLBACK \
enum tcp_state state; /* TCP state */ \
u8_t prio; \
/* ports are in host byte order */ \
u16_t local_port
struct tcp_pcb_listen {
/* Common members of all PCB types */
IP_PCB;
/* Protocol specific PCB members */
TCP_PCB_COMMON(struct tcp_pcb_listen);
};
struct tcp_pcb {
/** common PCB members */
IP_PCB;
/** protocol specific PCB members */
TCP_PCB_COMMON(struct tcp_pcb);
/* ports are in host byte order */
u16_t remote_port;
u8_t flags;
/* Timers */
u8_t polltmr, pollinterval;
u8_t last_timer;
u32_t tmr;
/* receiver variables */
u32_t rcv_nxt; /* next seqno expected */
u16_t rcv_wnd; /* receiver window available */
u16_t rcv_ann_wnd; /* receiver window to announce */
u32_t rcv_ann_right_edge; /* announced right edge of window */
u16_t mss; /* maximum segment size */
/* sender variables */
u32_t snd_nxt; /* next new seqno to be sent */
u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last
window update. */
u32_t snd_lbb; /* Sequence number of next byte to be buffered. */
u16_t snd_wnd; /* sender window */
u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */
u16_t acked;
u16_t snd_buf; /* Available buffer space for sending (in bytes). */
u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */
/* These are ordered by sequence number: */
struct tcp_seg *unsent; /* Unsent (queued) segments. */
struct tcp_seg *unacked; /* Sent but unacknowledged segments. */
struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */
/* Function to be called when more send buffer space is available. */
tcp_sent_fn sent;
/* Function to be called when (in-sequence) data has arrived. */
tcp_recv_fn recv;
/* Function to be called when a connection has been set up. */
tcp_connected_fn connected;
/* Function which is called periodically. */
tcp_poll_fn poll;
/* Function to be called whenever a fatal error occurs. */
tcp_err_fn errf;
};
tcp_pcb_listen只用作STUB,并不能作为控制块使用。所以它只具有资源描述的数据成员。
tcp_pcb是控制块,具有控制TCP通信状态的各种变量和FLAG。另外,它还具有向上的应用接口层的函数接口。例如,sent,recv,connected。
STUB中有一个Callback,accept,当监听成功时,会malloc一个新的TCP_PCB,并将STUB中注册的accept这个Callback填充给新创建的TCP_PCB。
新建的TCP_PCB,它的state被设置为SYN_RCVD。所以在tcp_process处理时,会调用accept这个Callback。
err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
static int connection = 1;
/* set the receive callback for this connection */
tcp_recv(newpcb, recv_callback);
/* just use an integer number indicating the connection id as the
callback argument */
tcp_arg(newpcb, (void*)(UINTPTR)connection);
/* increment for subsequent accepted connections */
connection++;
return ERR_OK;
}
通常在accept_callback中,为新建的TCP_PCB配置其他的Callback。例如recv。这样,在进行TCP通信时,当tcp_input处理了TCP的报文,得到了载荷之后,就可以调用recv_callback,进行后续的载荷处理任务。
err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err)
{
/* do not read the packet if we are not in ESTABLISHED state */
if (!p) {
tcp_close(tpcb);
tcp_recv(tpcb, NULL);
return ERR_OK;
}
/* indicate that the packet has been received */
tcp_recved(tpcb, p->len);
/* echo back the payload */
/* in this case, we assume that the payload is < TCP_SND_BUF */
if (tcp_sndbuf(tpcb) > p->len) {
err = tcp_write(tpcb, p->payload, p->len, 1);
} else
xil_printf("no space in tcp_sndbuf\n\r");
/* free the received pbuf */
pbuf_free(p);
return ERR_OK;
}
在recv_callback中,做了简单的ECHO处理,将LWIP提交给应用层的载荷,利用TCP_PCB,再发送出去。
tcp_write只是负责把PBUF挂载到sendqueue中去,并不完成实际发送任务。
发送任务的执行,是在tcp_output中被执行的。