uip源码分析

更新时间:2024-04-26 23:45:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

uIP的ARP协议代码分析之一 ARP请求

(是根据IP地址获取物理地址的一个TCP/IP协议 同时将IP地址和硬件地址存入本机ARP缓存中,下次请求时直接查询ARP缓存。)

对于一个设备用的ARP协议而言,重要的东西包括三方面:

1. 一个本地的IP与MAC地址的缓存表.以有对应的更新和查询操作. 2. 一个发送ARP请求的函数. 3. 一个处理ARP回复的函数.

下面我们来看uIP中是如何实现的(代码见uip_arp.c: 首先,定义一个缓存表的数据结构,99行起: struct arp_entry { u16_t ipaddr[2];

struct uip_eth_addr ethaddr; u8_t time; };

只有三个项,很简单

第一项是ip地址,16*2=4*8位的,保存四个八位组. 第二项是MAC地址. 第三项是缓存更新时间.

下来是ARP请求发送函数:uip_arp.c L325

/*-----------------------------------------------------------------------------------*/ /**

* Prepend Ethernet header to an outbound IP packet and see if we need * to send out an ARP request.

*为传出的IP包添加以太网头并看是否需要发送ARP请求.

* This function should be called before sending out an IP packet. The * function checks the destination IP address of the IP packet to see * what Ethernet MAC address that should be used as a destination MAC * address on the Ethernet.

*此函数应该在发送IP包时调用,它会检查IP包的目的IP地址,看看以太网应该使用什么目的MAC地址.

* If the destination IP address is in the local network (determined * by logical ANDing of netmask and our IP address), the function * checks the ARP cache to see if an entry for the destination IP * address is found. If so, an Ethernet header is prepended and the * function returns. If no ARP cache entry is found for the

* destination IP address, the packet in the uip_buf[] is replaced by * an ARP request packet for the IP address. The IP packet is dropped * and it is assumed that they higher level protocols (e.g., TCP) * eventually will retransmit the dropped packet.

*如果目的IP地址是在局域网中(由IP地址与子网掩码的与逻辑决定),函数就会从ARP缓存表中查找有

*无对应项.若有,就取对应的MAC地址,加上以太网头,并返回,否则uip_buf[]中的数据包会被替换成一个

*目的IP在址的ARP请求.原来的IP包会被简单的仍掉,此函数假设高层协议(如TCP)会最终重传扔掉的包.

* If the destination IP address is not on the local network, the IP * address of the default router is used instead.

*如果目标IP地址并非一个局域网IP,则会使用默认路由的IP地址.

* When the function returns, a packet is present in the uip_buf[] * buffer, and the length of the packet is in the global variable * uip_len.函数返回时,uip_buf[]中已经有了一个包,其长度由uip_len指定. */

/*-----------------------------------------------------------------------------------*/ void

uip_arp_out(void) {

struct arp_entry *tabptr;

/* Find the destination IP address in the ARP table and construct the Ethernet header. If the destination IP addres isn't on the local network, we use the default router's IP address instead.

//在ARP表中找到目的IP地址,构成以太网头.如果目的IP地址不在局域网中,则使用默认//路由的IP.

If not ARP table entry is found, we overwrite the original IP packet with an ARP request for the IP address. */

//如果ARP表中找不到,则将原来的IP包替换成一个ARP请求.

/* First check if destination is a local broadcast. 首先检查目标是不是广播*/ if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr)) { memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6); } else {

/* Check if the destination address is on the local network. 检查目标地址是否在局域网内 */

if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask)) { /* Destination address was not on the local network, so we need to use the default router's IP address instead of the destination

address when determining the MAC address. 目的地址不在局域网内,所以保用默认路由器的地址来确在MAC地址*/

uip_ipaddr_copy(ipaddr, uip_draddr); } else {

/* Else, we use the destination IP address. 否则,使用目标IP地址*/ uip_ipaddr_copy(ipaddr, IPBUF->destipaddr); }

for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {//这里遍历表,对比目的IP与ARP缓存表中的IP.

tabptr = &arp_table; if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr)) { break; } } if(i == UIP_ARPTAB_SIZE) { /* The destination address was not in our ARP table, so we overwrite the IP packet with an ARP request. 如果遍历到头没找到,将原IP包替换为ARP请求并返回*/ memset(BUF->ethhdr.dest.addr, 0xff, 6); memset(BUF->dhwaddr.addr, 0x00, 6); memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); uip_ipaddr_copy(BUF->dipaddr, ipaddr); uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr); BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */ BUF->hwtype = HTONS(ARP_HWTYPE_ETH); BUF->protocol = HTONS(UIP_ETHTYPE_IP); BUF->hwlen = 6; BUF->protolen = 4; BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN]; uip_len = sizeof(struct arp_hdr); return; } /* Build an ethernet header. 如果是在局域网中,且在ARP缓存中找到了(如果没找到进行不到这一步,在上面就返回了),则构建以太网头*/ memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6); } memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6); IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP); uip_len += sizeof(struct uip_eth_hdr); }

以上内容自325行起.下面再总结一下其基本顺序:

用IPBUF->ethhdr.dest.addr来存储目的IP地址,它有可能是局域网内一主机IP,也可能是路由器IP(如果是发往外网,即原来的目的IP不在局域网内),还有可能是广播专用的IP. 先看是不是在广播:如果是广播,将IPBUF->ethhdr.dest.addr设为广播IP.

再看是不是在局域网内:如果不是,则将IPBUF->ethhdr.dest.addr设为路由器IP.

如果在局域网内,查看是否已经存在于ARP缓存表中:如果不在,将要发送的换成ARP请求,返回.

如果已存在,则查找使用查找到的MAC地址为IP包添加以太网头.

这里还要解释一些细节问题,主要是:

1. 如何在IP包上添加以太网头

2. 如果将IP包替换成ARP请求,ARP请求的格式是什么. 3. 广播地址

这些问题将在二楼来说.

将IP包替换成ARP请求部分代码(实际上IP包是放在uip_buf[]里的,这里只是将uip_buf[]填充上ARP请求即可),于uip_arp.c的388行:

/* The destination address was not in our ARP table, so we overwrite the IP packet with an ARP request. */

memset(BUF->ethhdr.dest.addr, 0xff, 6); memset(BUF->dhwaddr.addr, 0x00, 6);

memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);

uip_ipaddr_copy(BUF->dipaddr, ipaddr);

uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr);

BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */ BUF->hwtype = HTONS(ARP_HWTYPE_ETH); BUF->protocol = HTONS(UIP_ETHTYPE_IP); BUF->hwlen = 6; BUF->protolen = 4;

BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);

uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];

uip_len = sizeof(struct arp_hdr); return;

首先解释这里的BUF(于uip_arp.c的116行): #define BUF ((struct arp_hdr *)&uip_buf[0])

可见这里的BUF就是uip_buf[],只不过这里将它取做一个struct arp_hdr的结构体: struct arp_hdr {

struct uip_eth_hdr ethhdr; u16_t hwtype; //硬件类型

u16_t protocol; //协议类型 u8_t hwlen; u8_t protolen;

u16_t opcode; //操作码

struct uip_eth_addr shwaddr; //源以太网地址 u16_t sipaddr[2]; //源IP地址

struct uip_eth_addr dhwaddr; //目的以太网地址 u16_t dipaddr[2]; //目的IP地址 };

struct uip_eth_hdr { struct uip_eth_addr dest; struct uip_eth_addr src; u16_t type; };

这是arp_hdr的第一个成员ethhdr的类型定义,对应图片中的前三项:6+6+2,目的以太网地址,源以太网地址,2字节数据类型(ARP请求和应答为0x0806).

struct arp_hdr的第二个成员u16_t hwtype,对应图片中第三项,2字节硬件类型(值为1表示以太网).

struct arp_hdr的第三个成员u16_t protocol,对应图片中第四项,2字节要映射的协议地址类型(ip地址为0x0800).

struct arp_hdr的第四个成员u8_t hwlen,对应图片中第五项,1字节硬件地址长度(对MAC地址来说为6).

struct arp_hdr的第五个成员u8_t protolen,对应图片中第六项,1字节协议地址长度(对IP地址来说为4).

struct arp_hdr的第六个成员u16_t opcode,对应图片中第七项,2字节操作码(1 ARP请求,2 ARP应答,3 RARP请求,4 RARP应答,必须字段).

struct arp_hdr的第七个成员struct uip_eth_addr shwaddr,对应图片中第八项,6字节源以太网地址.

struct arp_hdr的第八个成员u16_t sipaddr[2];,对应图片中第九项,2字节源IP地址.

struct arp_hdr的第九个成员struct uip_eth_addr dhwaddr,对应图片中第十项,6字节目标以太网地址.

struct arp_hdr的第十个成员u16_t dipaddr[2];,对应图片中第十一项,2字节目标IP地址. 上面绿色的表示已经详解的,红字的表示要进一步说明的.

这就是一个ARP帧的结构,可以看到,里面的源和目的以太网地址都是重复的. 我们再看函数中的代码:

memset(BUF->ethhdr.dest.addr, 0xff, 6); memset(BUF->dhwaddr.addr, 0x00, 6);

memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);

memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);

这里四个memset,重复的源和目的以太网地址一起设置了,四个memset对应图片中的1,10,2,8项.但是:对1和10两项,都是源以太网地址,但置的值是不同的,分别为0xff*6和0x00*6.为什么会这样呢?因为他们的用处不一样,见:【相关资料】ARP分组格式(帧格式,报文格式) 6+6– 以太网的源地址和目的地址,目的地址为全1的为广播地址

注意这里说,目的地址为全为1的广播地址.什么意思?当你发送一个ARP请求是,你是想知道一个IP对应的以太网地址(即MAC地址),但是你现在不知道目的主机的以太网地址,你问谁啊?不知道问谁,这种情况下,只能是广播一下了,0xff*6就是广播地址.

从图片中可以看到,ARP包是分成两部分的,前6+6+2叫做\以太网首部\它的意义就是\分组是从谁(源地址)发给谁(目的地址)的什么类型(帧类型,请求和应答为0x0806)\第二部分则是内容.

来看这个例子:

请求帧如下(为了清晰在每行的前面加了字节计数,每行16个字节): 以太网首部(14字节)

0000: ff ff ff ff ff ff 00 05 5d 61 58 a8 08 06 ARP帧(28字节)

0000: 00 01 0010: 08 00 06 04 00 01 00 05 5d 61 58 a8 c0 a8 00 37 0020: 00 00 00 00 00 00 c0 a8 00 02 填充位(18字节)

0020: 00 77 31 d2 50 10 0030: fd 78 41 d3 00 00 00 00 00 00 00 00

以太网首部:

目的主机采用广播地址,源主机的MAC地址是00:05:5d:61:58:a8,上层协议类型0x0806表示ARP。 ARP帧:

硬件类型0x0001表示以太网, 协议类型0x0800表示IP协议, 硬件地址(MAC地址)长度为6, 协议地址(IP地址)长度为4,

op为0x0001表示请求目的主机的MAC地址, 源主机MAC地址为00:05:5d:61:58:a8,源主机IP地址为c0 a8 00 37(192.168.0.55), 目的主机MAC地址全0待填写,目的主机IP地址为c0 a8 00 02(192.168.0.2)。 由于以太网规定最小数据长度为46字节,ARP帧长度只有28字节,因此有18字节填充位,填充位的内容没有定义,与具体实现相关。

uIP的ARP协议代码分析之二 ARP应答

如果读懂了上面的文章,这里的理解就相对容易了.

ARP应答部分代码为uip_arp.c中的void uip_arp_arpin(void)函数. 这个函数是在设备接收到ARP包时,由驱动程序调用的.

如果收到是ARP包是一个对本地主机上次发送的ARP请求的应答,那么就从包中取得自己想要的主机的MAC地址,加入自己的ARP缓存表中.

如果收到是一个ARP请求,那就把自己的MAC地址打包成一个ARP应答,发送给请求的主机. 看代码uip_arp.c的254行:

1. /*-----------------------------------------------------------------------------------*/ 2. /**

3. * ARP processing for incoming ARP packets. 4. *对传入的ARP包的处理.

5. * This function should be called by the device driver when an ARP 6. * packet has been received. The function will act differently 7. * depending on the ARP packet type: if it is a reply for a request 8. * that we previously sent out, the ARP cache will be filled in with 9. * the values from the ARP reply. If the incoming ARP packet is an ARP 10. * request for our IP address, an ARP reply packet is created and put 11. * into the uip_buf[] buffer.

12. *此函数在收到ARP包时由设备驱动调用,函数行为会因包类型而有不同.如果收到的是一个对前先发送

的请求的应答

13. *则根据应答的值填充缓存.如果传入的包是对我们的IP的请求,则创建一个ARP应答,并放入uip_buf[]

中.

14. * When the function returns, the value of the global variable uip_len 15. * indicates whether the device driver should send out a packet or 16. * not. If uip_len is zero, no packet should be sent. If uip_len is 17. * non-zero, it contains the length of the outbound packet that is 18. * present in the uip_buf[] buffer.

19. *函数返回时,全局变量uip_len的值指明了设备驱动要不要发送包.若uip_len为0,则不需发送,若

uip_len不是0,

20. * 则其值是uip_buf[]中包含的要传出的包的大小.

21. * This function expects an ARP packet with a prepended Ethernet 22. * header in the uip_buf[] buffer, and the length of the packet in the

23. * global variable uip_len.此函数预期中的uip_buf中有一个带以太网头的ARP包.其长度存为

uip_len中. 24. */

25. /*-----------------------------------------------------------------------------------*/ 26. void

27. uip_arp_arpin(void) 28. { 29.

30. if(uip_len < sizeof(struct arp_hdr)) { 31. uip_len = 0; 32. return; 33. }

34. uip_len = 0; 35.

36. switch(BUF->opcode) { 37. case HTONS(ARP_REQUEST):

38. /* ARP request. If it asked for our address, we send out a

39. reply. 如果是一个ARP请求,则发送应答.*/

40. if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) {

41. /* First, we register the one who made the request in our ARP 42. table, since it is likely that we will do more communication

43. with this host in the future.首先,我们将发送请求的主机注册到ARP缓存表中,因为我们很可能要

跟它要有更多的交流 */

44. uip_arp_update(BUF->sipaddr, &BUF->shwaddr); 45.

46. /* The reply opcode is 2. 应答的操作码为2*/ 47. BUF->opcode = HTONS(2); 48.

49. memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6); 50. memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); 51. memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); 52. memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6); 53.

54. BUF->dipaddr[0] = BUF->sipaddr[0]; 55. BUF->dipaddr[1] = BUF->sipaddr[1]; 56. BUF->sipaddr[0] = uip_hostaddr[0]; 57. BUF->sipaddr[1] = uip_hostaddr[1]; 58.

59. BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); 60. uip_len = sizeof(struct arp_hdr); 61. } 62. break;

63. case HTONS(ARP_REPLY):

64. /* ARP reply. We insert or update the ARP table if it was meant

65. for us. 如果收到的是一个ARP应答,而且也是我们所要的应答的话,就插件并更新ARP缓存表*/ 66. if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) { 67. uip_arp_update(BUF->sipaddr, &BUF->shwaddr); 68. } 69. break; 70. } 71. 72. return; 73. }

复制代码

这里有一件事是很有意思的,就是说如果某个主机请求得到我们的MAC的地址,我们先把它的MAC地址加入到自己的表中.就好比社交网络中,别人请求加我们为好友,如果我们接收的话,也自动加对方为好友一样.既然对方找上我们了,肯定是要做进一步的交流,互加MAC地址也很自然的.

有了上一篇文章,这里似乎不必做过多的解释了.但还是说一下吧,看看我们怎么做应答的. 如果收到了一个请求,我们要做应答:

1. /* The reply opcode is 2. */ 2. BUF->opcode = HTONS(2); 3.

4. memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6); 5. memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); 6. memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); 7. memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6); 8.

9. BUF->dipaddr[0] = BUF->sipaddr[0]; 10. BUF->dipaddr[1] = BUF->sipaddr[1]; 11. BUF->sipaddr[0] = uip_hostaddr[0]; 12. BUF->sipaddr[1] = uip_hostaddr[1]; 13.

14. BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); 15. uip_len = sizeof(struct arp_hdr);

复制代码

由于请求和应答包很多地方是相同的,如上文中的绿色部分.我们只需将收到的请求稍加修改就可以发送回去了. 首先,要改一下MAC地址,四个地方.然后要将目标主机IP设为设为请求包的源主机IP,目的主机IP设为我们的IP.就可以了.

另外说下对ARP缓存表的设置:

1. 这个函数是集插入,更新一体的,有两个参数,IP地址,MAC地址. 2. static void

3. uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) 4. {

5. register struct arp_entry *tabptr;

6. /* Walk through the ARP mapping table and try to find an entry to 7. update. If none is found, the IP -> MAC address mapping is

8. inserted in the ARP table. 遍历ARP映射表,看看有没有需要更新的表项,如果没有,就将新的表项

插入.*/

9. for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 10.

11. tabptr = &arp_table[i];

12. /* Only check those entries that are actually in use. 所谓更新就是传入参数IP在表中己经存

在了,这时不需要新建表项,而是要修改己存表项.只查使用中的表项,如果有些表项IP是0,那么就不是使用中的*/

13. if(tabptr->ipaddr[0] != 0 && 14. tabptr->ipaddr[1] != 0) { 15.

16. /* Check if the source IP address of the incoming packet matches 17. the IP address in this ARP table entry. 看看传入的IP有没有匹配项.*/ 18. if(ipaddr[0] == tabptr->ipaddr[0] && 19. ipaddr[1] == tabptr->ipaddr[1]) {

20.

21. /* An old entry found, update this and return. 如果有己存的匹配项,修改该项的MAC地址和更

新时间.*/

22. memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); 23. tabptr->time = arptime; 24. 25. return; 26. } 27. } 28. } 29.

30. /* If we get here, no existing ARP table entry was found, so we 31. create one. 如果运行到这里,说明没有己存的表项,则创建一个.*/ 32.

33. /* First, we try to find an unused entry in the ARP table. 先看看有没有空表项可用.*/ 34. for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 35. tabptr = &arp_table[i]; 36. if(tabptr->ipaddr[0] == 0 && 37. tabptr->ipaddr[1] == 0) { 38. break; 39. } 40. } 41.

42. /* If no unused entry is found, we try to find the oldest entry and 43. throw it away. 如果没空表项,就找到一个最旧的表项,扔掉它,换成我们的.*/ 44. if(i == UIP_ARPTAB_SIZE) { 45. tmpage = 0; 46. c = 0;

47. for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 48. tabptr = &arp_table[i];

49. if(arptime - tabptr->time > tmpage) { 50. tmpage = arptime - tabptr->time; 51. c = i; 52. } 53. } 54. i = c;

55. tabptr = &arp_table[i]; 56. }

57. /* Now, i is the ARP table entry which we will fill with the new 58. information. 现在i就是我们最终得到的表项,我们把新的信息插入到这里.*/ 59. memcpy(tabptr->ipaddr, ipaddr, 4);

60. memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); 61. tabptr->time = arptime;

复制代码

OK,uip_arp.c中还有两个函数,楼下继续. 最后两个函数:

1.看代码:

1. /*-----------------------------------------------------------------------------------*/ 2. /**

3. * Periodic ARP processing function. 4. *ARP周期性处理函数.

5. * This function performs periodic timer processing in the ARP module 6. * and should be called at regular intervals. The recommended interval 7. * is 10 seconds between the calls.

8. *此函数在ARP模块中施行周期性处理,它应该每隔一段时间就调用一次.推荐为10秒. 9. */

10. /*-----------------------------------------------------------------------------------*/ 11. void

12. uip_arp_timer(void) 13. {

14. struct arp_entry *tabptr; 15.

16. ++arptime;

17. for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 18. tabptr = &arp_table[i];

19. if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 && 20. arptime - tabptr->time >= UIP_ARP_MAXAGE) { 21. memset(tabptr->ipaddr, 0, 4); 22. } 23. } 24. }

复制代码

1. #define UIP_ARP_MAXAGE 120//即20分钟.

复制代码

从这里可以看出,ARP表项中的更新时间是一个数,每调用一个上面这个函数,这个数就加1. 这个周期性处理函数的作用就是每隔一段时间把超过20分钟都没有更新过的表项扔掉.

2.看代码:

1. /*-----------------------------------------------------------------------------------*/ 2. /**

3. * Initialize the ARP module.初始化ARP模块. 4. *

5. */

6. /*-----------------------------------------------------------------------------------*/ 7. void

8. uip_arp_init(void) 9. {

10. for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 11. memset(arp_table[i].ipaddr, 0, 4); 12. } 13. }

一目了解,所谓初始化就是把数组中很一个表项都扔掉,清空成0,变成空表项. 到这里,并于ARP的代码就说到这里,欢迎提问.

UIP中文文档第一 与设备驱动接口

实现设备驱动与uIP的对接,需要开发者实现如下七个接口程序: #define uip_input() 处理一个输入的数据包。

#define uip_periodic(conn) 通过连接号,对连接周期性进行处理。

#define uip_conn_active(conn) (uip_conns[conn].tcpstateflags!=UIP_CLOSED)

#define uip_periodic_conn(conn) 周期性处理一个连接,此操作要借助指向这个连接的结构体的指针。

#define uip_poll_conn(conn) 请求轮询(poll)某一指定连接.

#define uip_udp_periodic(conn) 周期性处理udp连接,此操作要借助连接号。 #define uip_udp_periodic_conn(conn) 周期性处理一udp连接,要借助指向此连接结构体的指针。

还有一个变量在接口中用到: u8_t uip_buf [UIP_BUFSIZE+2] uip包缓冲区。

下面对以上接口进行详细介绍: 1. #define uip_input() 处理输入数据包。

此函数应该在设备从网络上接收到数据包时得到调用。设备驱动应该在调用此函数之前,将接收到的数据包内容存入uip_buf缓冲区,并将其长度赋给uip_len变量。

此函数返回时,如果系统有数据要输出,会直接将数据存入uip_buf,并将其长度值赋给uip_len。如果没有数据要发送,则将uip_len赋0. 通常使用此函数的方法如下:

1. uip_len = devicedriver_poll(); 2. if(uip_len > 0) { 3. uip_input(); 4. if(uip_len > 0) { 5. devicedriver_send(); 6. } 7. }

注意:

如果你写的uip设备驱动需要使用ARP协议(Address Resolution Protocal),比如说在以太网内使用uip的时候,你就得在调用此函数之前先调用uip的ARP代码:

1. #define BUF ((struct uip_eth_hdr *)&uip_buf[0]) 2. uip_len = ethernet_devicedrver_poll(); 3. if(uip_len > 0) {

4. if(BUF->type == HTONS(UIP_ETHTYPE_IP)) { 5. uip_arp_ipin(); 6. uip_input(); 7. if(uip_len > 0) { 8. uip_arp_out();

9. ethernet_devicedriver_send(); 10. }

11. } else if(BUF->type == HTONS(UIP_ETHTYPE_ARP)) { 12. uip_arp_arpin(); 13. if(uip_len > 0) {

14. ethernet_devicedriver_send(); 15. } 16. }

使用例程:example-mainloop-with-arp.c, and example-mainloop-without-arp.c. 此接口定义于uip.h的第257行。 2. #define uip_periodic(conn) 周期性处理一连接,需借助其连接号。

此函数对uip的tcp连接进行一些必要的周期性处理(定时器,轮询等),它应该在周期性uip定时器消息到来时被调用。它应该对每一个连接都得到调用,不管连接是打开的还是关闭的。

此函数返回时,可能会有要传送出去的数据包等待得到服务,包内容会被放到uip包缓冲区中。如果真的有数据包需要送出,则uip_len的值会被置为一个大于零的数,此时设备驱动应该将数据包发送出去。(这一段与(1)中相应段落的意思应该是一致的。) 通常调用此函数时,会使用一个如下的for循环:

1. for(i = 0; i < UIP_CONNS; ++i) { 2. uip_periodic(i); 3. if(uip_len > 0) { 4. devicedriver_send(); 5. } 6. }

注意:

如果你所写的设备驱动需要使用ARP协议(Address Resolution Protocal),例如是在以太网中使用uip,你就得在调用驱动函数发送传出数据之间调用uip_arp_out()函数:

1. for(i = 0; i < UIP_CONNS; ++i) { 2. uip_periodic(i); 3. if(uip_len > 0) { 4. uip_arp_out();

5. ethernet_devicedriver_send(); 6. } 7. }

参数:

conn 将要轮询的连接号。 使用例程:

example-mainloop-with-arp.c, and example-mainloop-without-arp.c. 此接口函数定义于uip.h的301行。 3. #define uip_periodic_conn(conn)

对一个连接进行周期性处理,需要借助指向其结构体的指针

这个函数与uip_periodic执行的操作是相同的,不同之处仅在于传入的参数是一个指向uip_conn结构体的指针。此函数可以用于对某个连接强制进行周期性处理。 参数:

conn 指向要进行处理的连接结构体的指针。 此接口函数定义于uip.h的323行。 4. #define uip_poll_conn(conn) 请求对特定连接进行轮询

函数功能与uip_periodic_conn()相同,但不是执行任何定时器处理。轮询以得到新数据。 参数:

conn 指向要进行处理的连接结构体uip_conn的指针。 此接口函数定义于uip.h的337行。 5. #define uip_udp_periodic(conn) 借助连接号,周期性处理udp连接。

此函数基本上与uip_periodic()相同,区别之处仅在于这里处理的是udp连接。其调用方式也与uip_periodic()类似:

1. for(i = 0; i < UIP_UDP_CONNS; i++) { 2. uip_udp_periodic(i); 3. if(uip_len > 0) { 4. devicedriver_send(); 5. } 6. }

注意:

对于uip_periodic()函数,在uip中使用arp和以太网时,要特别注意一些事情,这对于uip_udp_periodic()也一样:

1. for(i = 0; i < UIP_UDP_CONNS; i++) { 2. uip_udp_periodic(i); 3. if(uip_len > 0) {

4. uip_arp_out();

5. ethernet_devicedriver_send(); 6. } 7. }

参数:

conn 要处理的udp连接号 使用例程:

example-mainloop-with-arp.c, and example-mainloop-without-arp.c. 此接口定义于uip.h的373行。

6. #define uip_udp_periodic_conn(conn)

周期性处理udp连接,需借助指向该连接结构体的指针。

函数功能与uip_udp_periodic(conn)相同,不同之处在于所用参数为指向连接结构体的指针而非连接号。此函数可用于强制执行周期性处理某连接。 参数:

conn 指向要处理的udp连接的uip_udp_conn结构体的指针。 此接口定义于uip.h的 390 行。

7. 变量 u8_t uip_buf[UIP_BUFSIZE+2] uip数据包缓冲区

uip_buf数列用于盛放传入/传出的数据包。设备驱动应将传入的数据放于此缓冲区中。发送数据时,设备驱动从缓冲区中读取链路层的头和TCP/IP头。链路层头的大小在UIP_LLH_LEN中定义。 注意:

应用数据无需放在这个缓冲区中,所以设备驱动需要从uip_appdata指针所指的地方读取数据。下面是一个例子:

1. void

2. devicedriver_send(void) 3. {

4. hwsend(&uip_buf[0], UIP_LLH_LEN);

5. if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) { 6. hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN); 7. } else {

8. hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN);

9. hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN); 10. } 11. }

此变量于uip.c的139 定义。 此变量被uip_process()引用。 追加:下面是原文档中另一页的内容,但仅有一项,且与本页内容一体,所以追加于本页.

变量 u16_t uip_len

uip_buf缓冲区中的数据包长度(译者注:uip_buf是一数列,其长度固定,此处指的是动态存的数据长度,不是指数列本身长度)。

这个全局函数盛放存储于uip_buf中的数据包的长度。

当网络设备驱动调用uip输入函数时,uip_len要被设为传入数据包的大小。

当向处发送数据包时,设备驱动通过这个变量确定要发送的数据包在uip_buf中的长度。 应用例程:

example-mainloop-with-arp.c, andexample-mainloop-without-arp.c. 此变量定义于uip.c中的155行。

此变量被uip_arp_arpin(),uip_process(), anduip_split_output()引用。

UIP中文文档第二 uIP初始化函数

uIP初始化函数用于启动uIP.它包括以下两个函数:

1. 2.

1.

void uip_init(void) void uip_setipid(u16_t id)

void uip_init(void)

此函数用于在启动时初始化uIP的TCP/IP栈。 应用示例:

example-mainloop-with-arp.c, and example-mainloop-without-arp.c. 定义于uip.c的379行。

2. void uip_setipid(u16_t id) 此函数用于启动时设置初始的ip_id. 此函数定义于uip.c的181行。

追加:下面为uip源代码中上述两函数作注。

文中所涉代码属于uip 1.0:http://www.sics.se/~adam/uip/uip-1.0-refman/a00202.html#l00379 1. void uip_init(void)代码分析

2. 3. 4. 5. 6. 7. 8. 9.

void uip_init(void) {

for(c = 0; c < UIP_LISTENPORTS; ++c) { uip_listenports[c] = 0;

} //将uip_listenports数组全部清零 for(c = 0; c < UIP_CONNS; ++c) { uip_conns[c].tcpstateflags = UIP_CLOSED;

} //将所有连接的tcpstateflags状态标志设为关闭,表示此连接为关闭状态。

10. #if UIP_ACTIVE_OPEN 11. lastport = 1024;

12. #endif /* UIP_ACTIVE_OPEN */ 13. //上面的段不知什么意思。 14. #if UIP_UDP

15. for(c = 0; c < UIP_UDP_CONNS; ++c) {

16. uip_udp_conns[c].lport = 0; 17. }

18. #endif /* UIP_UDP *///如果定义了UIP_UDP则将uip_udp_conns的lport清零。 19. /* IPv4 initialization. */

20. #if UIP_FIXEDADDR == 0/* uip_hostaddr[0] = uip_hostaddr[1] = 0;*/ 21. #endif /* UIP_FIXEDADDR *///如果主机地址要为固定的,在上面这里赋值。}

UIP_LISTENPORTS:可同时监听的TCP端口数最大值,每监听一个TCP端口都要占用两字节内存。 此宏定义于uipopt.h的259行。其默认值为20。

UIP_CONNS: 可同时打开的TCP端口数的最大值,由于TCP连接是静态分配的,所以降低这个值的大小可以降低占用的内存空间,每个TCP连接大约需要30字节内存。 此宏定义于uipopt.h的245行。其默认值为10。

UIP_UDP_CONNS:最大的并行UDP连接数。 此宏定义于uipopt.h的206行。其默认值是10。

uip_conns: struct uip_conn型结构体数列,数列大小为UIP_CONNS.每个uip_conn代表一个TCP连接。

uip_udp_conns:struct uip_udp_conn型号结构体数列,数列大小为UIP_UDP_CONNS.每个uip_udp_conn代表一个UDP连接。

UIP_ACTIVE_OPEN:是否编译可以让上层应用程序动态打开外向的TCP连接的代码。关闭此功能可以降低uIP的代码大小。

此宏定义于uipopt.h的233行。 2. uip_conn结构体 代表一个uip的TCP连接

此结构体用于鉴定一个TCP连接,对应用来说,除一个域之外,所有的域都是只读的。例外的是appstate域,它的目的是让应用存取应用相关的连接状态,此域的类型定义于uipopt.h中。 此结构体定义于uip.h的1153行。

1. 2. 3.

struct uip_conn {

uip_ipaddr_t ripaddr; /**< The IP address of the remote host. 运程主机的ip地址*/

u16_t lport; /**< The local TCP port, in network byte order. 本地主机的TCP端口,以网络传输中的字节顺序*/

4. 5. 6. 7. 8. 9.

order. 远程主机的TCP端口,以网络传输中的字节顺序*/ u8_t rcv_nxt[4]; /**< The sequence number that we expect to receive next.我们期待接收的下一个序号*/

u8_t snd_nxt[4]; /**< The sequence number that was last sent by us. 我们上次发送的序号*/

u16_t len; /**< Length of the data that was previously sent.上次发送的数据长度 */

10. u16_t mss; /**< Current maximum segment size for the 11. connection. 现行连接的段大小最大值*/

12. u16_t initialmss; /**< Initial maximum segment size for the 13. connection. 连接的初始段大小最大值*/

14. u8_t sa; /**< Retransmission time-out calculation state

15. variable. 重传超时计算状态变量*/

16. u8_t sv; /**< Retransmission time-out calculation state 17. variable. 重传超时计算状态变量*/

18. u8_t rto; /**< Retransmission time-out.重传超时 */ 19. u8_t tcpstateflags; /**< TCP state and flags. TCP状态标志*/ 20. u8_t timer; /**< The retransmission timer. 重传定时器*/ 21. u8_t nrtx; /**< The number of retransmissions for the last 22. segment sent. 上次发送的段的重传次数*/ 23.

24. /** The application state.应用状态 */ 25. uip_tcp_appstate_t appstate; 26. };

27. order. 远程主机的TCP端口,以网络传输中的字节顺序*/ 28. u8_t rcv_nxt[4]; /**< The sequence number that we expect to 29. receive next.我们期待接收的下一个序号*/

30. u8_t snd_nxt[4]; /**< The sequence number that was last sent by 31. us. 我们上次发送的序号*/

32. u16_t len; /**< Length of the data that was previously sent.上次发送的数据长度 */ 33. u16_t mss; /**< Current maximum segment size for the 34. connection. 现行连接的段大小最大值*/

35. u16_t initialmss; /**< Initial maximum segment size for the 36. connection. 连接的初始段大小最大值*/

37. u8_t sa; /**< Retransmission time-out calculation state 38. variable. 重传超时计算状态变量*/

39. u8_t sv; /**< Retransmission time-out calculation state 40. variable. 重传超时计算状态变量*/

41. u8_t rto; /**< Retransmission time-out.重传超时 */ 42. u8_t tcpstateflags; /**< TCP state and flags. TCP状态标志*/ 43. u8_t timer; /**< The retransmission timer. 重传定时器*/ 44. u8_t nrtx; /**< The number of retransmissions for the last 45. segment sent. 上次发送的段的重传次数*/ 46. /** The application state.应用状态 */ 47. uip_tcp_appstate_t appstate;};

uip_udp_conn结构体 代表一个uIP UDP连接 定义于uip.h的1210行。

1. 2. 3. 4. 5. 6. 7.

struct uip_udp_conn {

uip_ipaddr_t ripaddr; /**< The IP address of the remote peer. 远程对等主机的ip地址*/ u16_t lport; /**< The local port number in network byte order. 本地端口号,以网络字节顺序*/ u16_t rport; /**< The remote port number in network byte order. 远程端口号,以网络字节顺序*/ u8_t ttl; /**< Default time-to-live. 默认存活时间*/ /** The application state. 应用状态*/ uip_udp_appstate_t appstate;};

3. uip_udp_conn结构体 代表一个uIP UDP连接 定义于uip.h的1210行。

8. 9.

struct uip_udp_conn {

uip_ipaddr_t ripaddr; /**< The IP address of the remote peer. 远程对等主机的ip地址*/

10. u16_t lport; /**< The local port number in network byte order. 本地端口号,以网络字节顺序*/ 11. u16_t rport; /**< The remote port number in network byte order. 远程端口号,以网络字节顺序*/ 12. u8_t ttl; /**< Default time-to-live. 默认存活时间*/ 13.

14. /** The application state. 应用状态*/ 15. uip_udp_appstate_t appstate;

};

4. void uip_setipid(u16_t id)

void uip_setipid(u16_t id) { ipid = id; }//此函数只是将传来的参数赋值给ipid.

UIP中文文档第三 uIP配置函数

uIP配置函数用于设置一些如ip地址等的uIP运行时参数.

它包括以下一些函数:

#define uip_sethostaddr(addr) 设定主机IP地址 #define uip_gethostaddr(addr) 获取主机IP地址 #define uip_setdraddr(addr) 设定默认路由器地址 #define uip_getdraddr(addr) 获取默认路由器地址 #define uip_setnetmask(addr) 设定网络掩码 #define uip_getnetmask(addr) 获取网络掩码

#define uip_setethaddr(eaddr) 设定以态网MAC地址

前六个函数皆定义于uip.h,最后一个定义于uip_arp.h 1. uip_sethostaddr(addr)

#define uip_sethostaddr(addr) uip_ipaddr_copy(uip_hostaddr, (addr))

获取主机IP地址,主机IP地址由4字节的数列表示,第一个八位数就是数组的第一个成员.(八位数,octet,是网络术语,即是一个字节,但网络术语不叫字节),常用使用方法如下:

1. 2. 3.

uip_ipaddr_t addr;

uip_ipaddr(&addr, 192,168,1,2); uip_sethostaddr(&addr);

参数:

addr 指向IP地址类型uip_ipaddr_t的指针. 参考: uip_ipaddr() 应用例程:

dhcpc.c, example-mainloop-with-arp.c, and example-mainloop-without-arp.c. 定义于uip.h的106行. 2. uip_gethostaddr(addr)

#define uip_gethostaddr(addr) uip_ipaddr_copy((addr), uip_hostaddr)使用方法例:

1. 2.

uip_ipaddr_t hostaddr; uip_gethostaddr(&hostaddr) 3. uip_setdraddr(addr) 设定的是默认路由器地址.

#define uip_setdraddr(addr) uip_ipaddr_copy(uip_draddr, (addr))

4. uip_getdraddr(addr) 获取默认路由器地址

#define uip_getdraddr(addr) uip_ipaddr_copy((addr), uip_draddr) 5. uip_setnetmask(addr) 设定子网掩码

#define uip_setnetmask(addr) uip_ipaddr_copy(uip_netmask, (addr)) 6. uip_getnetmask(addr) 获取子网掩码

#define uip_getnetmask(addr) uip_ipaddr_copy((addr), uip_netmask) 7. uip_setethaddr(eaddr)

1. 2. 3. 4. 5. 6.

#define uip_setethaddr(eaddr) do {uip_ethaddr.addr[0] = eaddr.addr[0]; \\ uip_ethaddr.addr[1] = eaddr.addr[1];\\ uip_ethaddr.addr[2] = eaddr.addr[2];\\ uip_ethaddr.addr[3] = eaddr.addr[3];\\ uip_ethaddr.addr[4] = eaddr.addr[4];\\ uip_ethaddr.addr[5] = eaddr.addr[5];} while(0)

获取以太网的MAC地址,ARP代码需要知道以太网卡的MAC地址,才能回应ARP查询,并产生以太网头. 注意:

此宏只能用来确定以太网卡的MAC地址,并不能用来改变它. 参数:

eaddr 指向uip_eth_addr结构体的指针,里面包含了以太网卡的MAC地址. 定义于:

line 134 of file uip_arp.h.

1. 2. 3.

struct uip_eth_addr { u8_t addr[6]; };

定义于:line1542of file uip.h.

UIP中文文档第四 应用层要调用的函数

这一部分包含的内容较多,包括一些宏定义和函数. 宏定义::

#define uip_outstanding(conn) ((conn)->len)

#define uip_datalen() 存放在uip_appdata中的现行可用的传入数据长度 #define uip_urgdatalen() 到达连接的带外数据长度(紧急的) #define uip_close() 关闭当前连接 #define uip_abort() 中止当前连接

#define uip_stop() 告诉发送方主机停止发送数据

#define uip_stopped() 查明当前连接是否以前被uip_stop()停止过. #define uip_restart() 如果当前连接被uip_stop()停止过,重新开始. #define uip_udpconnection() 查明当前连接是否是udp连接. #define uip_newdata() 查明新传入的数据是否可用

#define uip_acked() 查明以前发送的数据是否得到回应了 #define uip_connected() 查明连接是否连接上了. #define uip_closed() 查明连接是否是被另一端关闭. #define uip_aborted() 查明连接是否被另一端中止. #define uip_timeout() 查明连接是否超时.

#define uip_rexmit() 查明是否需要将上次传送的数据重新传送. #define uip_poll() 查明连接是否被uip轮询了.

#define uip_initialmss() 获得当前连接的初始最大段大小. #define uip_mss() 获取可以在当前连接上发送的最大段大小. #define uip_udp_remove(conn) 移除一个udp连接.

#define uip_udp_bind(conn,port) 绑定一个udp连接到本地端口

#define uip_udp_send(len) 在当前连接上发送一个长度为len的udp数据报.

void uip_listen(u16_t port); 开始监听指定的端口. void uip_unlisten(u16_t port);停止监听指定的端口.

uip_conn * uip_connect(uip_ipaddr_t * ripaddr, u16_t port); 通过TCP连接到远程主机. void uip_send(const void * data,int len); 在当前连接上发送数据.

uip_udp_conn * uip_udp_new(uip_ipaddr_t * ripaddr ,u16_t port);建立一个新的udp连接.

1. #define uip_datalen()

如果有当前可用的传入数据的话,获取其长度.必需先调用uip_data()查明是否有当前可用的传入数据. 应有例程:

dhcpc.c, telnetd.c, and webclient.c. 此宏定义于uip.h的550行. 2. #define uip_urgdatalen()

任何到达连接的带外数据(紧迫数据)长度.要使用此宏,应配置UIP_URGDATA宏为真. 此宏定义于

此宏定义于uip.h的561行. 3. #define uip_close()

此函数会以一种谨慎的方式关闭连接.

应用例程: telnetd.c.

此宏定义于uip.h的570行. 4. #define uip_abort()

此函数用于中止(重置)当前连接,多用于出现错误导致无法使用uip_close()的场合. 应用示例: webclient.c.

此宏定义于uip.h的581行. 5. #define uip_stop()

告诉发送主机停止发送数据.此函数会关闭接收机窗口,停止从当前连接接收数据. 此宏定义于uip.h的591行.

6. #define uip_restart()

如果当前连接曾被uip_stop()停止,那么重新开始. 该函数会重新打开接收机窗口,从当前连接上接收数据. 此宏定义于uip.h的610行. 7. #define uip_udpconnection() 此函数查明当前连接是否是一个udp连接. 此宏定义于uip.h的626行. 8. #define uip_newdata()

如果uip_appdata指针所指之处有送给应用的新数据,此宏得到一个非零值.数据的大小可以通过uip_len获得. 应用例程:

dhcpc.c, resolv.c, telnetd.c, and webclient.c. 此宏定义于uip.h的637行. 9. #define uip_acked()

如果上次发送给远程主机的数据得到回应了,此宏得到一个非零值,这意味着应用可以发送新的数据. 应用例程:

telnetd.c, and webclient.c. 此宏定义于uip.h的648行. 10. #define uip_connected()

如果当前连接己经与远程主机连接,则此宏得到非零值.这包括两种情形,一是连接被主动打开(通过uip_connect()),二是连接被被动打开(通过uip_listen()). 应用例程:

hello-world.c, telnetd.c, and webclient.c 此宏定义于uip.h的660行. 11. #define uip_closed()

如果远程主机关闭了当前连接,则此宏得到非零值.这时应用可能要做一些清理工作. 应用例程:

smtp.c, telnetd.c, and webclient.c. 此宏定义于uip.h的670行.

12. #define uip_aborted()

如果当前连接被远程主机中止或重置,则为非零值. 应用例程:

smtp.c, telnetd.c, and webclient.c此宏定义于uip.h的680行. 13. #define uip_timeout()

如果当前连接由于多次重传导致超时中止,则为非零值. 应用例程:

smtp.c, telnetd.c, and webclient.c此宏定义于uip.h的690行. 14. #define uip_rexmit()

如果上次传输的数据在网络中丢失,而应用应该重新传输,则此宏得非零值.应用应该使用uip_send()重新传输与上次传输的完全相同的数据. 应用例程:

telnetd.c, and webclient.c. 该宏定义于uip.h中的702行. 参考webclient_appcall(). 15. #define uip_poll()

这个宏解决的问题是连接是不是由uIP轮询的. 应用例程:

resolv.c, telnetd.c, and webclient.c. 此宏定义于uip.h中的716行.

参考 httpd_appcall(),resolv_appcall(), and webclient_appcall(). 16. #define uip_mss()

在连接上传送的当前最大段大小是由接收机的窗口计算和连接的MSS计算的(它可以由uip_initialmss()计算). 应用例程:

telnetd.c, and webclient.c. 此宏定义于uip.h中的737行. 17. #uip_udp_remove(conn) 移除一个udp连接. 参数:

conn 指向代表连接的uip_udp_conn的结构体. 应用例程: resolv.c.

此宏定义于uip.h中的775行. 参考resolv_conf();

18. #define uip_udp_bind(conn,port) 绑定一个udp连接到本地端口. 参数:

conn 指向代表udp连接的uip_udp_conn结构体的指针. port 本地端口号,以网络字节顺序. 应用例程: dhcpc.c.

此宏定义于uip.h中的775行. 19. #define uip_udp_send(len)

在当前连接收发送一个长度为len的数据报.

该函数只有在udp事件(轮询或新数据)才能调用.要发送的数据要提前送到uip_buf缓冲区中uip_appdata指针所指示的位置. 参数:

len 存放在uip_buf中,要发送的数据报的长度. 应用例程: resolv.c.

此宏定义于uip.h中的800行. 20. void listen(u16_t port) 开始监听指定端口. 注意:

由于此函数中所用的参数port是网络字节顺序的,所以需要用到转换函数HTONS()或htons().

1. uip_listen(HTONS(80));

参数:

port 一个16位以网络字节顺序的端口号. 应用例程:

hello-world.c, and telnetd.c. 此宏定义于uip.h中的529行. 21. void uip_unlisten(u16_t port) 停止监听指定端口. 注意:

由于这里面用到的port是以网络字节顺序的,所以需要用到HTONS()或htons()函数.

1. uip_ipaddr_t ipaddr;

2. uip_ipaddr(&ipaddr, 192,168,1,2); 3. uip_connect(&ipaddr, HTONS(80))

参数:

ripaddr 远程主机的IP地址. port 16位的网络字节顺序的端口号. 返回值:

指向新连接的连接标识符的指针,当没有分配新连接时为NULL. 应用例程:

smtp.c, and webclient.c. 此函数定义于uip.c的407行. 引用了htons(),

uip_conn::lport, uip_conn::tcpstateflags, UIP_CLOSED, uip_conn, UIP_CONNS, and uip_conns. 22. void uip_send( void *data, int len) 在当前连接上发送数据

此函数用于发送一个单段TCP数据.只有uIP因为事件处理而请求的应用才能发送数据.

调用这个函数后实际发送数据量多少取决于TCP允许的数据量多少.uIP会自动裁剪数据,以保证发出去的数据是适量的.可以用uip_mss()来查询实际可以发送的数据量.

注意:

此函数不保证发送的数据能到达目的地.如果数据在网络中丢失,应用会接到请求,此求时uip_rexmit()事件被置位.这时应用就得使用此函数重发数据. 参数:

data 指向将要发送的数据的指针. len 要发送的数据的长度最大值. 应用例程:

dhcpc.c, telnetd.c, and webclient.c. 此函数定义于uip.c的1888行. 引用了uip_sappdata, and uip_slen.

23. struct uip_udp_conn * uip_udp_new(uip_ipaddr_t * ripaddr ,u16_t rport) 建立新的UDP连接.

此函数用于建立一个新的UDP连接.此函数会自动为新连接分配一个本地未使用的端口.然而,也可以调用uip_udp_new函数之后,调用uip_udp_bind()选择一个新的端口. 例如:

1. 2. 3. 4. 5. 6. 7. 8.

uip_ipaddr_t addr; struct uip_udp_conn *c;

uip_ipaddr(&addr, 192,168,2,1);

c = uip_udp_new(&addr, HTONS(12345)); if(c != NULL) {

uip_udp_bind(c, HTONS(12344)); }

参数: ripaddr 远程主机的IP地址. rport 远程主机的端口号,以网络字节顺序. 返回值: 指向新连接结构体uip_udp_conn的指针,如果连接未能分配则为NULL. 应用例程: dhcpc.c, and resolv.c. 定义于uip.c的473行. 引用了htons(), uip_udp_conn::lport, uip_udp_conn, UIP_UDP_CONNS, and uip_udp_conns. UIP中文文档第五 原始套接字(protosockets)库

详细说明:

原始套接字(protosocket)为uIP提供了一个与传统BSD套接字接口类似的接口.

不同于为传统uIP事件驱动接口写的程序,为原始套接字(protosocket)接口写的程序是顺序方式执行的,并且无需以明确的状态机方式实现.

原始套接字(protosocket)只能用于TCP连接.

原始大接字(protosocket)库使用\原始线程(protothreads)\来提供顺序控制流.这使得原始套接字在内存方面变得轻

量型,但也同时意味着原始套接字继承了\原始线程\的功能限制.每个原始套接字只能生存于单个函数中.自动变量(栈变量)不能跨原始套接字函数调用存在. 注意:

由于原始套接字库使用的是\原始线程(protothreads)\在调用原始套接字库函数时,局部变量并不总能得到保存.所以这里建议局部变量的使用要十分小心。

原始套接字库提供了一些无需处理重传和回应的发送数据函数,和一些无需对被分解成多个TCP段的数据进行处理的读取数据函数。

由于每个原始套接字都作为一个“原始线程”来运行,应在使用原始套接字的函数起始处通过调用PSOCK_BEGIN() 的方式启用原始套接字。与之类似,原始套接字可以通过调用PSOCK_EXIT()结束掉。 相关文件:

psock.h 原始套接字库头文件 相关结构体:

struct psock_bufstruct psock 代表一个原始套接字。

#define PSOCK_INIT(psock, buffer, buffersize) 初始化一个原始套接字。

#define PSOCK_BEGIN(psock) 在一个函数中启用一个原始套接字的原始线程。 #define PSOCK_SEND(psock, data, datalen) 发送数据。

#define PSOCK_SEND_STR(psock, str) 发送一个以零结尾的字符串。

#define PSOCK_GENERATOR_SEND(psock, generator, arg) 通过函数(generator)产生数据并发送出去。

#define PSOCK_CLOSE(psock) 关闭一个原始套接字。 #define PSOCK_READBUF(psock) 读数据直到缓冲区满。 #define PSOCK_READTO(psock, c) 读数据到字符c.

#define PSOCK_DATALEN(psock) 获得上次读到的数据长度。 #define PSOCK_EXIT(psock) 退出原始套接字的原始线程。

#define PSOCK_CLOSE_EXIT(psock) 关闭一个原始套接字,并退出其原始线程。 #define PSOCK_END(psock) 声明一个原始套接字的原始线程的结尾。 #define PSOCK_NEWDATA(psock) 查明是否有数据到达原始套接字。

#define PSOCK_WAIT_UNTIL(psock, condition) 等待,直到条件(condition)为真。 #define PSOCK_WAIT_THREAD(psock, condition) PT_WAIT_THREAD(&((psock)->pt), (condition))

u16_t psock_datalen(struct psock *psock) char psock_newdata(psock * s)

追加:

1. #define PSOCK_BEGIN(psock) 启用一个原始套接字的原始线程。

此宏启用一个原始套接字关联的原始线程,必须在使用此原始套接字的函数调用其他原始套接字函数之前出现。 参数:

psock (struct psock *)指向要启用的原始套接字的结构体指针。 应用例程:

hello-world.c, and smtp.c. 定义于psock.h的158行.

2. #define PSOCK_CLOSE(psock)

此宏用于关闭一个原始套接字,只能用于此原始套接字生存的原始线程中. 参数:

psock (struct psock *)指向要关闭的原始套接字的结构体指针。 应用例程:

hello-world.c, and smtp.c.

定义于psock.h的235行.

3. #define PSOCK_CLOSE_EXIT(psock)

此宏用于关闭一个原始套接字,并退出原始套接字生存的线程. 参数:

psock (struct psock *)指向要关闭的原始套接字的结构体指针。 定义于psock.h的308行.

4. #define PSOCK_DATALEN(psock)

返回之前通过 PSOCK_READTO() 或 PSOCK_READ()读到的数据长度. 参数:

psock (struct psock *)指向盛放数据的原始套接字的结构体指针。 定义于psock.h的281行. 5. #define PSOCK_END(psock)

此宏用于声明原始套接字的原始线程结束.常于PSOCK_BEGIN(psock)配合使用. 参数:

psock (struct psock *)指向原始套接字的结构体指针 应用例程:

hello-world.c, and smtp.c. 定义于psock.h的325行.

6. #define PSOCK_EXIT(psock)

此宏用于终止原始套接字的原始线程,必须与PSOCK_CLOSE一起使用. 也可以直接使用PSOCK_CLOSE_EXIT(); 参数:

psock (struct psock *)指向原始套接字的结构体指针. 应用例程: smtp.c.

定义于psock.h的297行.

7. #define PSOCK_GENERATOR_SEND(psock,generator,arg) 通过函数产生数据并发送. 参数:

psock 指向原始套接字的指针. generator 指向产生数据的函数的指针. arg 要传给函数的参数.

使宏用于通过函数产生数据,并通过原始套接字发送.它可以用于动态产生数据并传输,而不必先将产生的数据存入缓冲区.此函数减小了缓冲区内存的开销.产生器函数由应用程序实现,使用时要将函数的指针作为一个参数传给宏. 产生器函数应该将产生的数据直接存放在uip_appdata缓冲区中,并返回产生数据的长度.数据每一次发送时,原始套接字层会调用产生器函数,并且如果出现重发的情况,则也会调用一次产生器函数,也就是说重发的值并非第一次产生器函数调用时的值.

定义于psock.h的219行.

8. #define PSOCK_INIT(psock ,buffer,buffersize)

这个宏初始化一个原始套接字,它必须在使用套接字之间得到调用. 此宏还规定了套接字的输入缓冲区. 参数:

psock (struct psock *) 指向要初始化的原始套接字的指针. buffer (char * ) 指向原始套接字的输入缓冲区的指针. buffersize (unsigned int) 输入缓冲区的大小. 应用例程:

hello-world.c, and smtp.c. 定义于psock.h的144行.

9. #define PSOCK_NEWDATA(psock) 此宏用于查明是否有数据到达套接字。

它常和PSOCK_WAIT_UNTIL() 连用,查看套接字上是否有新的数据到达。 参数:

psock (struct psock *) 指向套接字的指针。 定义于psock.h的339行。

10. #define PSOCK_READBUF(psock) 读数据直到缓冲区满。

此宏会阻塞数据等待,把数据读入由PSOCK_INIT()指定的输入缓冲区。读数据操作会进行到缓冲区满为止。

参数:

psock (struct psock *) 指向数据读取来源套接字的指针。 定义于psock.h的250行。

11. #define PSOCK_READTO(psock,c) 读数据直到某字符出现。

此宏会阻塞数据等待并开始读取数据到由PSOCK_INIT()指定的输入缓冲区,数据读取会一直持续到由参数c指定的字符出现为止。 参数:

psock (struct psock *)指向数据读取来源套接字的指针。 c (char )用于指示停止读取数据的字符。 应用例程:

hello-world.c, and smtp.c. 定义于psock.h的268行。

12. #define PSOCK_SEND(psock,data,datalen) 发送数据。

此宏通过原始套接字发送数据。原始套接字阻塞原始线程,直到所有的数据发送完毕,并且已经被完程TCP终端接收。 参数:

psock (struct psock *) 指向用于发送数据的原始套接字的指针。 data (char *) 指向要发送的数据的指针。 datalen (unsigned int)要发送的数据长度。 应用例程: smtp.c.

定义于psock.h的178行。

13. #define PSOCK_SEND_STR(psock,str) 发送一个以零结尾的字符串。 参数:

psock (struct psock *) 指向要用来发送数据的套接字指针。 str 要发送的字符串。 应用例程:

hello-world.c, and smtp.c.

定义于psock.h的191行。

14. #define PSOCK_WAIT_UNTIL(psock,condition) 等待直接条件为真。

下宏阻塞原始线程,直到条件为真。宏PSOCK_NEWDATA()可以用来检查此宏等待时有没有新数据到来。 此宏的典型用法如下:

1. 2. 3. 4. 5. 6. 7. 8. 9.

PT_THREAD(thread(struct psock *s, struct timer *t)) {

PSOCK_BEGIN(s);

PSOCK_WAIT_UNTIL(s, PSOCK_NEWADATA(s) || timer_expired(t));

if(PSOCK_NEWDATA(s)) { PSOCK_READTO(s, '\\n'); } else {

10. handle_timed_out(s); 11. } 12.

13. PSOCK_END(s); 14. }

复制代码 参数:

psock (struct psock *)套接字指针。 condition 等待条件。 定义于psock.h的372行。

UIP中文文档第六 uIP原始线程(protothreads)

详细说明:“原始线程”是一种轻量级的、无需堆栈的线程。它主要用于内存极为受限的系统,如深入嵌入式系统、传感器网络等。

“原始线程”是以C代码实现的,为事件驱动的系统提供了线性的代码执行空间。“原始线程”即可以用在有实时操作系统的系统中,也可以用在没有实时操作系统的系统中。

它在事件驱动的系统上层提供了阻塞上下文,省去了每个线程的单独堆栈空间开销。原始线程的目标是在没有复杂的状态机或多线程的情况下实现控制顺序流。原始线程在C语言内部实现了条件阻塞。

原始线程相对于纯事件驱动方法的优势是原始线程提供了可以阻塞函数的顺序代码结构。在纯事件驱动的方法中,阻塞的实现必须将一个函数手动分成两个部分。一部分放在阻塞调用前,一部分放在阻塞调用后。这让使用if()和while()语句变得困难。

原始线程相对于普通线程的优势是,它不需要为每个线程分配堆栈。在内存受限的系统中,为每个线程分配堆栈空间会占用大量的内存。相比而言,原始线程的每个线程只需2到12个字节的状态字,具体多大与构架有关。 注意:

由于原始线程有阻塞调用中中不需保存堆栈上下文,原始线程阻塞时,局部变量也不再保留。这意味着局部变量的使用

必须十分小心。如果不确定的话,尽量不要有原始线程中使用局部变量。 主要特性:

? ? ? ? ?

无机器相关代码-代码完成是C写的。

不使用错误-倾向于像longjump()这样的函数。 很小的RAM开销-每条线程仅两个字节。 即可使用OS,也可以不使用。

提供阻塞等待而无需多线程或堆栈切换。

主要应用:

? ? ? ?

内存受限系统 事件驱动的协议栈 深入嵌入式系统 传感器网络

原始线程包括四个基本的操作:初妈化(PT_INIT()),执行(PT_BEGIN()),条件阻塞(PT_WAIT_UNTIL()),和退出(PT_EXIT()).在此之上还有两个方便使用的函数:反向条件阻塞(PT_WAIT_WHILE())和原始线程阻塞(PT_WAIT_THREAD()).

见:uIP中文文档第五 原始套接字

原始线程线的分布是基于一个类似于BSD的协议的。也就是说允许商业的和非商业的使用。唯的需要是\is given\作者: Adam Dunkels 作者的工作得到了Oliver Schmidt 的支持。

这里算一段,追加见楼下。

原始线程:原始线程是极端轻量级的、无需堆栈的线程。它在事件驱动的系统上层提供阻塞上下文,无需为每个线程提供堆栈的开销。原始线程的目标是在没有复杂的状态机或多线程的情况下实现控制顺序流。原始线程在C函数内部实现了条件阻塞。

在内存受限的系统中,如深入嵌入式系统,传统的多线程的内存开销可能会显得过大。在传统线程中每条线程都需要自己堆栈,这种要求并非必要的。堆栈空间可能花去大部分的内存空间。

原始线程相对于多线程的优势在于原始线程是相当轻量极的,原始线程不需要自己的堆栈。相反,所有的线程都运行在同一个堆栈中,上下文的切换是通过堆栈的回转实现的。这在内存受限的系统中是具有优势的,因为那里一个线程的堆栈可能就占用了很大的一部分内存。原始线程的每条线程只需两字节,而且原始线程是纯用C实现的,不需要任何机器有关的汇编语言代码。

一个原始线程在一个C语言函数中运行,不能跨过多个函数。原始线程可以调用普通的C语言函数,在调用的C语言函数不能阻塞。嵌套调用的函数内部的阻塞是通过为可以阻塞的函数产生一人新的线程来实现的。这种方法的优点是阻塞是明确的,程序员确切的知道哪些函数是可以阻塞的,哪些函数是永远也不会阻塞的。

原始线程与不对称的协同程是相似的,主要的不同在于协同程序为每个协同程序使用单独的堆栈而原始线程无需堆栈。与原始线程最相似的机制是Python产生器。这些产生器也是无堆栈的结构,但用途不同。原始线程在C函数内部实现阻塞上下文,而python产生器从产生函数中提供了多个退出点。 局部变量: 注意:

由于原始线程有阻塞调用中中不需保存堆栈上下文,原始线程阻塞时,局部变量也不再保留。这意味着局部变量的使用必须十分小心。如果不确定的话,尽量不要有原始线程中使用局部变量。

调度:

原始线程是通过重复调用宿主函数驱动的。每调用宿主函数一次,原始线程都会运行到阻塞或退出为止。这样说来,原始线程的高度是由使用原始线程的应用做到的。 实现:

原始线程的实现采用了\机制.\代表的是一个程序在某个位置的当前执行状态,但不提供任何调用历史或局部变量.\可以在指定函数内设定,以捕获程序的状态.\continuation\设定后可以恢复,以复原\设定时函数的状态. \的实现的多种方法: 1. 使用机器相关的汇编语言 2. 使用标准C结构体. 3.使用编译器扩展.

第一种方式通过保存和恢复处理器状态实现,所说的处理器状态不包括堆栈指针.这种方式每个原始线程需要16到32个字节的内存空间,具体是多少与处理器构架有关.

标准C语言的实现方式每个线程只需要2字节,它以一种不明显的方式优化了C语言中的switch()语句,这与Duff的设备类似.然而,这种实现对使用原始线程的代码稍有限制,不能在代码中使用switch()语句.

某些编译器有C语言的扩展也可以实现原始线程.比如说GCC支持标签指针,这就可以用于此目的.在这种实现方式下,每个原始线程需要4字节内存.

说实话,以上的内容没读懂,大家报着怀疑的态度去看,英语好的直接看原英文版吧. 文件:

ft.h 原始线程的实现 模块:

Local continuation 实现原始线程的基础 数据结构: struct pt 初始化:

#define PT_INIT(pt) 初始化原始线程 声明和定义:

#define PT_THREAD(name_args) 声明原始线程

#define PT_BEGIN(pt) 在要实现原始线程的函数内部声明原始线程的开始 #define PT_END(pt) 声明原始线程的结尾 阻塞等待:

#define PT_WAIT_UNTIL(pt,condition) 等待直到条件为真. #define PT_WAIT_WHILE(pt,cond) 等待只要条件为真. 分级的原始线程:

#define PT_WAIT_THREAD(pt,thread) 阻塞等待子线程完成 #define PT_SPAWN(pt,child,thread) 产生子进程并等待其结束. 退出和重启:

#define PT_RESTART(pt) 重启原始线程 #define PT_EXIT(pt) 退出原始线程

调用原始线程:#define PT_SCHEDULE(pt) 高度原始线程 原始线程让位:

#define PT_YIELD(pt) 当前原始线程让位(即暂时中止,让别的线程先执行). #define PT_YIELD_UNTIL(pt) 面前原始线程让位,直到条件为真.

定义:

#define PT_WAITING 0 #define PT_EXITED 1 #define PT_ENDED 2 #define PT_YIELDED 3

翻译的不好,大家多多包涵~

1. #define PT_BEGIN(pt)在实现原始线程的C函数中声明原始线程开始.

此宏用于声明原始线程的开始点.应该把它放在使用原始线程的函数的开头儿.放在此句之前的C语句每次原始线程的调度都会执行一次. 参数:

pt 指向线程控制结构的指针 应用例程: dhcpc.c.

定义于pt.h的115行.

2. #define PT_END(pt) 声明原始线程的结尾

此宏用于声明原始线程的结尾,它通常必须与PT_BEGIN()宏匹配使用. 参数:

pt 指向原始线程控制结构的指针. 应用例程: dhcpc.c.

定义于pt.h的127行.

3. #define PT_EXIT(pt) 退出原始线程

此宏使用始线程退出.如果退出的线程是一个子线程,则其父线程结束阻塞状态,得以继续运行. 参数:

pt 指向原始线程控制结构的指针. 定义于pt.h的246行.

4. #define PT_INIT(pt)

初始化原始线程.初始化必须在使用之前完成.参数: pt 指向原始线程控制结构的指针. 见:PT_SPAWN() 应用例程: dhcpc.c.

定义于pt.h的80行.

引用于

httpd_appcall().

5. #define PT_RESTART(pt)

重启原始线程.此宏会致使运行的线程从PT_BEGIN()处重新开始执行. 参数:

pt 指向原始线程控制结构的指针.

应用例程: dhcpc.c.

定义于pt.h的229行.

6. #define PT_SCHEDULE(f)调试线程,此函数调度线程,如果调度的线程在运行则此函数返回非零值,如已经退出,则返回零值. 参数:

f 要调度的线程的宿主函数的调用. 定义于ph.h的271行.

7. #define PT_SPAWN(pt,child,thread) 此宏用于产生一个子线程并等待直到子线程的退出 .它只能用于线程内部. 参数:

pt 指向线程控制结构的指针. child 指向子线程的控制结构的指针. 带参数的子线程. 定义于pt.h的206行.

8.#define PT_THREAD(name_args)

此宏用于声明原始线程,所有的原始线程都必须由此声明. 应用例程:

dhcpc.c 和smtp.c. 定义于pt.h的100行.

9. #define PT_WAIT_THREAD(pt,thread) 阻塞并等待子线程无成.

这个宏调度了一个子线程,并等待子线程的完成 . 注意:

在此函数使用之前,子原始线程必须通过PT_INIT()初始化. 参数:

pt 指向原始线程控制结构体的指针. thread 带有参数的子线程. 定义于pt.h的192行.

10. #define PT_WAIT_UNTIL(pt,condition) 阻塞等待,直到条件为真.

此宏阻塞线程直到指定的条件为真. 参数:

pt 指向原始线程控制结构体的指针. condition 指定的条件.

应用例程: dhcpc.c .

定义于pt.h的148行.

11. #define PT_WAIT_WHILE(pt,cond) 阻塞等待,直到条件为真.

此宏阻塞线程只要指定的条件为真. 参数:

pt 指向原始线程控制结构体的指针. condition 指定的条件. 定义于pt.h的167行.

12. #define PT_YIELD(pt) 从当前线程中让位.

这个函数会让当前线程让位,从而使其它线程先在系统中运行. 参数:

pt 指向原始个线程控制结构体的指针.

应用例程: dhcpc.c.

定义于pt.h的290行.

13. #define PT_YEILD_UNTIL(pt,cond) 使当前线程让位,直到某条件发生. 参数:

pt 指向原始线程控制结构的指针. cond 条件.

此函数会让当前线程让位,直到某条件为真. 定义于pt.h的310行.

UIP中文文档第七 uIP编译时配置选项

详细说明:uIP针对每个工程都可以通过uipopt.h进行配置.

这个文件包含了uIP的所有编译时选项,应该针对每一个工程对这些选项进行调整.uIP的发行版包含了一个文档化的uipopt.h文件,用户可以复制和修改这个文件来达到调整选项的目的. 注意:

uipopt.h中的大部分内容都不必修改,反而是uip-conf.h需要的修改较多.

相关文件:

uip-conf.h uip配置文件. uipopt.h uip配置选项 针对于工程的配置选项:

uip有大量的配置选项,它们都可以根据工程需要重载.它们被保存在一个工程相关的uip-conf.h文件中,所有配置的名称都有UIP_CONF前辍.

#define UIP_CONF_MAX_CONNECTIONS 最大的TCP连接数. #define UIP_CONF_MAX_LISTENPORTS 最大监听的TCP端口数. #define UIP_CONF_BUFFER_SIZE uip 缓冲区大小. #define UIP_CONF_BYTE_ORDER CPU字节顺序. #define UIP_CONF_LOGGING 是否开启日志 #define UIP_CONF_UDP 是否启用UDP功能

#define UIP_CONF_UDP_CHECKSUMS 是否启用UDC校验和. #define UIP_CONF_STATISTICS UIP配置统计是否开启. typedef uint8_t u8_t 8位的数据类型. typedef uint16_t u16_t 16位的数据类型.

typedef unsigned short uip_stats_t 统计数据类型. 复制代码

静态配置选项:

这些选项可以用来静态配置IP地址,条件是UIP_FIXEDADDR必须设为1. 指定节点的配置选项包括IP地址,子网掩码,默认路由及以太网址.而后三项只有在以太网中时才可用.

#define UIP_FIXEDADDR 决定uIP使用固定IP与否. #define UIP_PINGADDRCONF 设置ping地址.

#define UIP_FIXEDETHADDR ARP模块的编译是否用固定的MAC地址. 复制代码 IP配置选项:

#define UIP_TTL 64 定义UIP包的生存时间.TTL. #define UIP_REASSEMBLY 打开IP包重新组装功能.

#define UIP_REASS_MAXAGE 40 IP片断扔掉之前在重组装缓冲区等待的时间. 复制代码

UDP配置选项:

#define UIP_UDP UDP支持是否编译

#define UIP_UDP_CHECKSUMS. 是否使用UDP校验和. #define UIP_UDP_CONNS 最大并发UDP连接数. 复制代码

TCP配置选项:

#define UIP_ACTIVE_OPEN 决定UIP的打开连接支持是否编译. #define UIP_CONNS 最大同时打开的TCP连接数. #define UIP_LISTENPORTS 最大TCP监听端口数. #define UIP_URGDATA 是否编译TCP紧迫数据提醒.

#define UIP_RTO 3 初始重传超时计数,以定时器脉冲个数计. #define UIP_MAXRTX 8 一段数据最大重传多少次才取消连接.

#define UIP_MAXSYNRTX 5 一段SYN数据要最大得传多少次,才认定连接请求失败.

#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) TCP最大段大小

#define UIP_RECEIVE_WINDOW 广播接收器的最大窗口大小.

#define UIP_TIME_WAIT_TIMEOUT 120 一个连接处于TIME_WAIT状态的最大时间. 复制代码

ARP配置选项:

#define UIP_ARPTAB_SIZE ARP表大小.

#define UIP_ARP_MAXAGE 120 ARP表的最大存活年龄,单位为10s. 复制代码

通用配置选项:

#define UIP_BUFSIZE UIP包缓冲区大小. #define UIP_STATISTICS 是否编译统计功能.

#define UIP_LOGGING 某些事件的日志功能是否编译. #define UIP_BROADCAST 广播功能是否支持. #define UIP_LLH_LEN 链路层头长度.

void uip_log (char *msg) 打印UIP日志信息. 复制代码

CPU架构配置:

这里指定的是CPU的大小端模式.当今的CPU多时小端的,然而最著名的例外就是motorola的CPU.它是大端的.应根据CPU的大小端模式不同,配置 BYTE_ORDER. #define UIP_BYTE_ORDER UIP所运行的CPU大小端模式. 复制代码

针对应用的配置:

UIP应用是使用单个应用函数数实现的.只要TCP/IP事件发生,uIP就会调用这个函数.这个函数的名字必须在编译时使用UIP_APPCALL注册到uIP.

uIP应用可以在uip_conn结构中保存应用状态.这是通过利用typedef uip_tcp_appstate_t和 uip_udp_appstate_t指定应用的类型实现的.包含此定义的文件必须被包含在uipopt.h文件中. 下面是一个例子:

void httpd_appcall(void);

#define UIP_APPCALL httpd_appcall

struct httpd_state { u8_t state; u16_t count; char *dataptr; char *script; };

typedef struct httpd_state uip_tcp_appstate_t 复制代码

#define UIP_APPCALL smtp_appcall TCP/IP事件的应答函数名称.

typedef smtp_state uip_tcp_appstate_t 存储在uip_conn中的应用状态类型. typedef int uip_udp_appstate_t 存储在uip_conn中的应用状态类型 #define UIP_LITTLE_ENDIAN 3412 #define UIP_BIG_ENDIAN 1234 复制代码

1. #define UIP_ACTIVE_OPEN

决定是否支持在uip中打开连接.

如果此工程中工作于uip上层的应用不需要打开TCP连接,可以关闭此项以减小编译后的代码大小. 定义于uipopt.h中的233行.

2. 2. #define UIP_ARP_MAXAGE 120

以10s为单位的ARP表项的最大年龄. 120代表的是20分钟(BSD中的默认值). 定义于uipopt.h中的358行. 引用于uip_arp_timer().

3. 3. #define UIP_ARPTAB_SIZE

1. #define UIP_ACTIVE_OPEN 决定是否支持在uip中打开连接.

如果此工程中工作于uip上层的应用不需要打开TCP连接,可以关闭此项以减小编译后的代码大小. 定义于uipopt.h中的233行.

2. #define UIP_ARP_MAXAGE 120 以10s为单位的ARP表项的最大年龄. 120代表的是20分钟(BSD中的默认值).

定义于uipopt.h中的358行. 引用于uip_arp_timer().

3. #define UIP_ARPTAB_SIZE ARP表的大小.

如果uip节点可能在本地网络中有很多连接,则此值应设得大些. 定义于uipopt.h中的349行.

引用于uip_arp_init(), uip_arp_out(), anduip_arp_timer().

4. #define UIP_BROADCAST 支持广播.

此标志用于配置广播的支持,仅在开启UDP时才有意义. 定义于uipopt.h中的423行.

5. #define UIP_BUFSIZE uip包缓冲区大小.

不就小于60字节,不须大于1500字节.这个值越小,TCP的吞吐量就越小,相反越大. 定义于uipopt.h中的379行. 引用于uip_split_output().

6. #define UIP_BYTE_ORDERuip运行的CPU构架的字节顺序,可选值仅有两个,分别为BIG_ENDIAN和 LITTLE_ENDIAN. 应用全程:example-mainloop-with-arp.c, and example-mainloop-without-arp.c. 定义于uipopt.h中的475行.

7. #define UIP_CONNS 可同时打开的最大TCP连接数.

由TCP连接是静态开辟的,所以减小此选项的值可以减小RAM占用量,每个TCP连接需要大约30字节的RAM. 定义于uipopt.h中的245行. 引用于uip_connect().

8. #define UIP_FIXEDADDR 决定uIP是否使用固定IP地址. 如果使用固定IP地址,则此地址可以在

uipopt.h中指定.否则,如果想用动态的IP地址,则可以使用uip_sethostaddr(), uip_setdraddr() and uip_setnetmask()

三个宏动态指定.

定义于uipopt.h中的97行.

9. #define UIP_FIXEDETHADDR 决定uIP是否使用固定的MAC地址.

若不使用固定的MAC地址,则可用uip_setethaddr()在运行时动态指定. 定义于uipopt.h中的127行.

10. #define UIP_LISTENPORTS

可以同时监听的最大端口数.每监听一个TCP端口需要两字节内存. 定义于uipopt.h中的259行. 引用于

uip_init(), uip_listen(), and uip_unlisten().

11. #define UIP_LLH_LEN 链路层头的长度.

这个IP头在uip_buf中的编移量.对于以太网来说,此值为14.对于SLIP来说,其值0 . 定义于uipopt.h中的448行.

引用于uip_ipchksum(), uip_process(), and uip_split_output().

12. #define UIP_LOGGING 定义是否编译事件日志.

这对于调试是非常有帮助的.如果打开此项,或必须在工程的构架上实现uip_log(). 定义于uipopt.h中的408行.

13. #define UIP_MAXRTX 8

在最多多少次重新发送同一段数据之后,就得取消连接. 此项不应改变.

定义于uipopt.h中的288行. 引用于uip_process().

14. #define UIP_MAXSYNRTX 5

一个SYN数据最多可以重传多少次,之后就得认为连接请求失败. 此项亦不应改变.

本文来源:https://www.bwwdw.com/article/raup.html

Top