uip协议栈uip_process函数工作流程详解
- 格式:pdf
- 大小:112.85 KB
- 文档页数:7
竭诚为您提供优质文档/双击可除uip协议栈,下载篇一:uip之udp应用笔记千兆网项目中,移植了uip到mcu中,采用udp通信方式,主要用来做一些控制协议的处理。
刚开始接手的时候,并没有做过网络方面的应用,而且对tcp/ip及udp通信又不太熟悉。
好在网上有一些文档,加上仔细阅读uip_process 代码,一边用抓包软件一边调试,总算把uip很好的应用了起来,而且还针对项目某些应用的特殊性,对uip源码进行了一些修改。
本文前半部分对uip源码的一些重要函数进行介绍,后半部分将对修改的部分做个记录,以备往后查阅。
本次使用的是uip-1.0,抓包软件用的wireshark1.6.7,这个软件真的很不错,居然支持gigevision,这点真的很意外。
一、一个完整的udp数据报文格式其实uip就是将你要发送到网络上的数据加上报头,好让它被成功发送到目的主机。
所以我们要先搞清楚一个完整的数据报文,才能搞清楚uip到底在做些什么。
ethernetheader:由目标mac和本机mac及type组成,共14byte,当目标mac全为ff时,表示是udp广播。
type=0x0800表示是ip。
在uip中,ethernetheader结构体定义如下:ipheader:0x45表示version=4,headerlength=20byte;0028表示ipheader+udpheader+userdata长度为40byte;6c14为包的id,每发一个包,这个id会自加1。
80的意义是timetolive,表示这个包的存活时间,路由每转发一次,就会对它自减1。
17表示通信协议类型为udp,4a0a为ipheader的校验码。
再后面就是源ip和目的ip地址了。
udpheader:0aaa表示srcport为2730;0f74表示dstprot为3956;14表示udpheader+userdata长度为20byte,c477表示udpheader的校验码,在一般的情况下,这个可以为0。
uIP的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请求发送函数:/*-----------------------------------------------------------------------------------*//*** 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指定.*//*-----------------------------------------------------------------------------------*/voiduip_arp_out(void){struct arp_entry *tabptr;/* Find the destination IP address in the ARP table and constructthe Ethernet header. If the destination IP addres isn't on thelocal 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 IPpacket 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 touse the default router's IP address instead of the destinationaddress 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 weoverwrite 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 weoverwrite 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 06ARP帧(28字节)0000: 00 010010: 08 00 06 04 00 01 00 05 5d 61 58 a8 c0 a8 00 370020: 00 00 00 00 00 00 c0 a8 00 02填充位(18字节)0020: 00 77 31 d2 50 100030: fd 78 41 d3 00 00 00 00 00 00 00 00以太网首部:目的主机采用广播地址,源主机的MAC地址是00:05:5d:61:58:a8,上层协议类型0x0806表示ARP。
1. 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行。
1. void uip_init(void)代码分析1.void2.uip_init(void)3.{4.for(c = 0; c < UIP_LISTENPORTS; ++c) {5.uip_listenports[c] = 0;6.} //将uip_listenports数组全部清零7.for(c = 0; c < UIP_CONNS; ++c) {8.uip_conns[c].tcpstateflags = UIP_CLOSED;9.} //将所有连接的tcpstateflags状态标志设为关闭,表示此连接为关闭状态。
10.#if UIP_ACTIVE_OPENstport = 1024;12.#endif /* UIP_ACTIVE_OPEN */13.//上面的段不知什么意思。
14.#if UIP_UDP15.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.20.21./* IPv4 initialization. */22.#if UIP_FIXEDADDR == 023./* uip_hostaddr[0] = uip_hostaddr[1] = 0;*/24.#endif /* UIP_FIXEDADDR *///如果主机地址要为固定的,在上面这里赋值。
OSIP工作原理和工作过程雷/wcl0715感谢OSIP代码整理小组的工作。
一、概述:首先说明一个概念:OSIP是一个开原的标准C的sip 3261的CORE,实际上是一个SIP 的信令实现,从另一个角度说,它是SIP的一个信令解释器,任务是负责生成和解析SIP信令,仅此而已,其它的事情,比如收包,发包,建立RTP流的过程等等和OSIP没有任何必然关系。
理论上OSIP可以应用在任何可以编译C语言的系统上。
二、工作原理OSIP实现的核心是状态机,为了便于保持逻辑的清晰和代码模块化的实现,OSIP分成两对状态机,分别用来处理正常的CALL流程和其它非CALL流程,对应的每对状态机又分成out和in两个状态,因此OSIP共有四个状态机。
详细的状态机部分文挡请参考OSIP 的状态机分析,在我们的资源连接里你可以找到它,你也可以登陆我的BLOG来寻找它。
对不同的状态OSIP相应的用不同的状态机处理,在这些状态机下,OSIP对本身或者来自对方的消息进行处理,从而推动自身状态的改变,这也就是OSIP的核心工作原理。
和其它的SIP协议栈的实现一样,OSIP采用CALLBACK函数的方法来对用户程序提供接口,当系统有事件发生,或者状态改变的时候,OSIP调用这些CALLBACK函数,来完成用户的要求(比如收到对方180消息后,本地要响铃,实现方法就是在OSIP的收到180消息的CALLBACK函数里实现响铃代码),因此OSIP用户需要自己编写这些CALLBACK 函数,实现自己所需要的功能,然后在系统初始化过程中,和系统callback函数挂接,这样当系统调用CALLBACK的时候就会执行你的函数,这也就是OSIP初始化的时候,要设定一大堆CALLBACK函数的原因,在接触协议初期,也许你觉得烦琐,但尽可能多的让用户能对事件进行处理,才能保证协议栈的可用性这样,这在系统越来越复杂的情况下,或者应用比较复杂的情况下,尤为重要。
uip_process(u8_t flag)(1)if(flag ==UIP_UDP_SEND_CONN),若是则goto udp_send;不是则向下执行;(2)if(flag == UIP_POLL_REQUEST){if(tcpstateflags== UIP_ESTABLISHED &&!uip_outstanding(uip_connr))如果处于稳定连接状态且没有数据在缓存中等待确认则:{①uip_flags = UIP_POLL;②UIP_APPCALL();③goto appsend;}goto drop;}else if(flag == UIP_TIMER){uip_len = 0;uip_slen = 0;如果连接处于等待超时关闭状态则增加超时计数器,如果到达超时期限则关闭当前连接tcpstateflags = UIP_CLOSED;if(tcpstateflags != UIP_CLOSED) 如果连接不处于关闭状态{if(uip_outstanding(uip_connr)&&(timer-- == 0)) 已经发送的数据包还未接收到对其的ACK,超时计数器减一且超时计数器值为0 {①如果到达所设定的重发次数则:1、tcpstateflags = UIP_CLOSED;关闭当前连接2、uip_flags = UIP_TIMEDOUT;通知应用程序超时;3、UIP_APPCALL();4、设置RST+ACK终止连接标志5、goto tcp_send_nodata;②没有到达设定的重发次数则重传数据:1、重置重传计数器2、 switch(tcpstateflags)根据连接处的不同状态重发不同的数据包case UIP_SYN_RCVD:goto tcp_send_synack;重新发送先前发送的SYN+ACKcase UIP_SYN_SENT:goto tcp_send_syn;重发SYN请求连接case UIP_ESTABLISHED:uip_flags = UIP_REXMIT;UIP_APPCALL(); 调用上层应用程序,通知重新生成数据重发goto apprexmit;进入重发阶段case UIP_FIN_WAIT_1:case UIP_CLOSING:case UIP_LAST_ACK:goto tcp_send_finack;重发FIN+ACK关闭连接}else if(tcpstateflags) == UIP_ESTABLISHED) 处于稳定连接状态且上次发送的数据接收到正确的ACK,可以继续发送新数据{①uip_flags = UIP_POLL;询问应用程序是否有数据要发送②UIP_APPCALL();调用应用程序产生数据③goto appsend;发送数据}}goto drop;}if(flag == UIP_UDP_TIMER){当前连接的本地端口不为0则{①uip_len = uip_slen = 0;②uip_flags = UIP_POLL;询问应用程序是否有数据要发送③UIP_UDP_APPCALL();调用应用程序产生数据④goto udp_send;}本地端口为0,表明没有建立DUP连接,则{goto drop;}}(3)检查IP帧头中的IP版本及IP头长度是否符合要求:①不符合:goto drop;丢弃此包②符合继续向下执行(4)检查目的IP地址是否为本机地址:①不是,goto drop;丢弃此包②是,向下继续执行(5)if(BUF->proto == UIP_PROTO_TCP)IP上层协议是否为TCP协议①是,goto tcp_input;进入TCP数据处理模块②不是,继续向下执行(6)if(BUF->proto == UIP_PROTO_UDP)IP上层协议是否为UDP协议①是,goto udp_input;进入UDP数据处理模块②不是,继续向下执行(7)if(BUF->proto != UIP_PROTO_ICMP) 不是TCP不是UDP也不是ICMP协议①goto drop;本机只处理UDP、TCP、ICMP数据包,其它包都将丢弃(8)运行到此处,表明接收到的是ICMP数据包,继续向下执行;———————————————————————————————————————icmp_input:此处为ICMP数据包处理部分,比较简单不做详解。
uIP协议栈分析uIP特性uIP协议栈往掉了完整的TCP/IP中不常用的功能,简化了通讯流程,但保存了网络通讯必须使用的协议,设计重点放在了IP/TCP/ICMP/UDP/ARP这些网络层和传输层协议上,保证了其代码的通用性和结构的稳定性。
由于uIP协议栈专门为嵌进式系统而设计,因此还具有如下优越功能:(1)代码非常少,其协议栈代码不到6K,很方便阅读和移植。
(2)占用的内存数非常少,RAM占用仅几百字节。
(3)其硬件处理层、协议栈层和应用层共用一个全局缓存区,不存在数据的拷贝,且发送和接收都是依靠这个缓存区,极大的节省空间和时间。
(4)支持多个主动连接和被动连接并发。
(5)其源代码中提供一套实例程序:web服务器,web客户端,电子邮件发送程序(SMTP 客户端),Telnet服务器,DNS主机名解析程序等。
通用性强,移植起来基本不用修改就可以通过。
(6)对数据的处理采用轮循机制,不需要操纵系统的支持。
由于uIP对资源的需求少和移植轻易,大部分的8位微控制器都使用过uIP协议栈, 而且很多的著名的嵌进式产品和项目(如卫星,Cisco路由器,无线传感器网络)中都在使用uIP协议栈。
uIP架构uIP相当于一个代码库,通过一系列的函数实现与底层硬件和高层应用程序的通讯,对于整个系统来说它内部的协议组是透明的,从而增加了协议的通用性。
uIP协议栈与系统底层和高层应用之间的关系如图2-1所示。
从上图可以看出,uIP协议栈主要提供了三个函数供系统底层调用。
即uip_init(), uip_input() 和uip_periodic()。
其与应用程序的主要接口是UIP_APPCALL( )。
uip_init()是系统初始化时调用的,主要初始化协议栈的侦听端口和默认所有连接是封闭的。
当网卡驱动收到一个输进包时,将放进全局缓冲区uip_buf中,包的大小由全局变量uip_len约束。
同时将调用uip_input()函数,这个函数将会根据包首部的协议处理这个包和需要时调用应用程序。
processing的if和else函数-回复Processing是一种强大的编程语言和开发环境,广泛应用于艺术、设计和交互媒体等领域。
在Processing中,if和else函数是用来控制程序流程的重要工具。
本文将一步一步解释如何使用if和else函数在Processing 中实现条件分支。
首先,我们需要了解if语句的基本语法和用法。
if语句用于判断某个条件是否为真,如果条件为真,则执行if代码块中的语句,否则跳过if代码块。
其基本语法结构如下:if (condition) {if代码块执行此处的语句}其中,`condition`代表一个逻辑表达式,当其结果为真时,执行if代码块中的语句。
如果我们想要执行多个语句,可以使用花括号`{}` 来包围多条语句。
下面是一个简单的例子,演示了如何使用if语句来判断某个变量是否大于10,并在控制台输出不同的信息:int num = 15; 初始化一个变量num为15if (num > 10) {如果num大于10print("变量num大于10"); 输出信息}在上述例子中,由于变量`num`的值为15,所以条件`num > 10`为真,因此if代码块中的语句被执行,控制台将输出"变量num大于10"。
如果我们把变量`num`的值改为8,则控制台不会输出任何信息,因为条件`num > 10`为假。
接下来,我们可以介绍else语句的使用。
else语句紧跟在if语句之后,它用于在条件不满足的情况下执行一组语句。
基本语法如下:if (condition) {if代码块执行此处的语句} else {else代码块执行此处的语句}当if条件满足时,执行if代码块中的语句;当if条件不满足时,执行else 代码块中的语句。
以下是一个例子,演示了如何使用if-else语句来判断一个数是奇数还是偶数:int num = 7; 初始化一个变量num为7if (num 2 == 0) {如果num是偶数print("变量num是偶数");} else {如果num是奇数print("变量num是奇数");}在上述例子中,由于变量`num`除以2的余数为1,表明它是一个奇数。
I/O Request Packet(IRP)IRP基本数据结构:IRP是由I/O管理器发出的,I/O管理器是用户态与内核态之间的桥梁,当用户态进程发出I/O请求时,I/O管理器就捕获这些请求,将其转换为IRP请求,发送给驱动程序。
I/O 管理器无疑是非常重要的,具有核心地位。
它负责所有I/O请求的调度和管理工作,根据请求的不同内容,选择相应的驱动程序对象,设备对象,并生成、发送、释放各种不同的IRP。
整个I/O处理流程是在它的指挥下完成的。
一个IRP是从非分页内存中分配的可变大小的结构,它包括两部分:IRP首部和辅助请求参数数组,如图1所示。
这两部分都是由I/O管理器建立的。
图1IRP简单结构图IRP首部中包含了指向IRP输入输出缓冲区指针、当前拥有IRP的驱动指针等。
紧接着首部的是一个IO_STACK_LOCATION结构的数组。
它的大小由设备栈中的设备数确定。
IO_STACK_LOCATION结构中保存了一个I/O请求的参数及代码、请求当前对应的设备指针、完成函数指针(IoCompletion)等。
IRP运行流程:操作系统用设备对象(device object)表示物理设备,每一个物理设备都有一个或多个设备对象与之相关联,设备对象提供了在设备上的所有操作。
也有一些设备对象并不表示物理设备。
一个唯软件驱动程序(software-only driver,处理I/O请求,但是不把这些请求传递给硬件)也必须创建表示它的操作的设备对象。
设备常常由多个设备对象所表示,每一个设备对象对应一个驱动程序来管理设备的I/O 请求。
一个设备的所有设备对象被组织成一个设备栈(device stack)。
而且,IO_STACK_LOCATION数组中的每个元素和设备栈中的每个设备是一一对应的,一般情况下,只允许层次结构中的每个设备对象访问它自己对应的IO_STACK_LOCATION。
无论何时,一个请求操作都在一个设备上被完成,I/O管理器把IRP请求传递给设备栈中顶部设备的驱动程序(IRP是传递给设备对象的,通过设备对象的DriverObject成员找到驱动程序)。
奋斗版 STM32 开发板例程文档———uIP1.0 ENC28J60 以太网例程uIP1.0 ENC28J60 以太网例程实验平台:奋斗版STM32开发板V2、V2.1、V3 实验内容:本例程演示了在奋斗STM32开发板上完成ARP,ICMP,TCP服务器、WEB 服务器以及UDP服务器,该实验学习了基于uIP1.0网络协议栈的程序编制。
预先需要掌握的知识1.ENC28J60ENC28J60是MICROCHIP公司的带SPI 接口的独立以太网控制器, 以太网控制器特性 • IEEE 802.3 兼容的以太网控制器 • 集成MAC 和10 BASE-T PHY • 接收器和冲突抑制电路 • 支持一个带自动极性检测和校正的10BASE-T 端口 • 支持全双工和半双工模式 • 可编程在发生冲突时自动重发 • 可编程填充和CRC 生成 • 可编程自动拒绝错误数据包 • 最高速度可达10 Mb/s 的SPI 接口 缓冲器 • 8 KB 发送/ 接收数据包双端口SRAM • 可配置发送/ 接收缓冲器大小 • 硬件管理的循环接收FIFO • 字节宽度的随机访问和顺序访问(地址自动递增) • 用于快速数据传送的内部DMA • 硬件支持的IP 校验和计算 介质访问控制器(MAC)特性 • 支持单播、组播和广播数据包 • 可编程数据包过滤,并在以下事件的逻辑“与” 和“或”结果为真时唤醒主机: - 单播目标地址 - 组播地址 广播地址 - Magic Packet - 由64 位哈希表定义的组目标地址 - 多达64 字节的可编程模式匹配(偏移量可由用户定义)淘宝店铺:1奋斗版 STM32 开发板例程文档———uIP1.0 ENC28J60 以太网例程• 环回模式 物理层(PHY)特性 • 整形输出滤波器 • 环回模式 工作特性 • 两个用来表示连接、发送、接收、冲突和全/ 半双工状态的可编程LED 输出 • 使用两个中断引脚的七个中断源 • 25 MHz 时钟 • 带可编程预分频器的时钟输出引脚 • 工作电压范围是3.14V 到3.45V • TTL 电平输入 • 温度范围:-40°C 到+85°C (工业级), 0°C 到 +70°C (商业级)(仅SSOP 封装) • 28 引脚SPDIP、SSOP、SOIC 和QFN 封装概述ENC28J60 是带有行业标准串行外设接口(SerialPeripheral Interface,SPI)的独立以太网控制器。
UIP中文文档第四应用层要调用的函数这一部分包含的内容较多,包括一些宏定义和函数.宏定义:1.#define uip_outstanding(conn) ((conn)->len)2.#define uip_datalen() 存放在uip_appdata中的现行可用的传入数据长度3.#define uip_urgdatalen() 到达连接的带外数据长度(紧急的)4.#define uip_close() 关闭当前连接5.#define uip_abort() 中止当前连接6.#define uip_stop() 告诉发送方主机停止发送数据7.#define uip_stopped() 查明当前连接是否以前被uip_stop()停止过.8.#define uip_restart() 如果当前连接被uip_stop()停止过,重新开始.9.#define uip_udpconnection() 查明当前连接是否是udp连接.10.#define uip_newdata() 查明新传入的数据是否可用11.#define uip_acked() 查明以前发送的数据是否得到回应了12.#define uip_connected() 查明连接是否连接上了.13.#define uip_closed() 查明连接是否是被另一端关闭.14.#define uip_aborted() 查明连接是否被另一端中止.15.#define uip_timeout() 查明连接是否超时.16.#define uip_rexmit() 查明是否需要将上次传送的数据重新传送.17.#define uip_poll() 查明连接是否被uip轮询了.18.#define uip_initialmss() 获得当前连接的初始最大段大小.19.#define uip_mss() 获取可以在当前连接上发送的最大段大小.20.#define uip_udp_remove(conn) 移除一个udp连接.21.#define uip_udp_bind(conn,port) 绑定一个udp连接到本地端口22.#define uip_udp_send(len) 在当前连接上发送一个长度为len的udp数据报. 复制代码函数:1.void uip_listen(u16_t port); 开始监听指定的端口.2.void uip_unlisten(u16_t port);停止监听指定的端口.3.uip_conn * uip_connect(uip_ipaddr_t * ripaddr, u16_t port); 通过TCP连接到远程主机.4.void uip_send(const void * data,int len); 在当前连接上发送数据.5.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轮询的.如果应用收到请求的原因是因为当前连接空闭太久被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. uip_ipaddr_t addr;2. struct uip_udp_conn *c;3.4. uip_ipaddr(&addr, 192,168,2,1);5. c = uip_udp_new(&addr, HTONS(12345));6. if(c != NULL) {7. uip_udp_bind(c, HTONS(12344));8. }复制代码参数: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 一个免费的TCP/IP栈原文:Adam Dunkels****************翻译:张伟林*******************摘要这个文档描述uIP TCP/IP栈。
uIP TCP/IP栈是使用于低至8位或16位微处理器的嵌入式系统的一个可实现的极小的TCP/IP协议栈。
现时,uIP代码的大小和RAM的需求比其它一般的TCP/IP 栈要小。
uIP栈使用一个基于编程模块事件去减少代码的大小和RAM的使用量。
基于系统的底层和uIP 之间的接口的回应会在文档里描述。
系统的底层和uIP之间的接口是隐蔽的。
文档后面包含了一些uIP应用编程例子。
uIP 的代码和这个文档的新版本可以在uIP的主页下载/adam/uip/。
这个文档描述了uIP的0.6版。
1 引言新近这些年里,人们对连接一个甚至只是小装置到一个现有的IP网络例如全球因特网的兴趣增加了。
为了可以通过因特网通讯,一个可实现的TCP/IP协议栈是必须的。
uIP是一个可实现的TCP/IP协议组件的一个非常重要的部分。
uIP的实现目标是保持代码大小和储存器使用量最小。
现时,uIP代码的大小和RAM的需求比其它一般的TCP/IP栈要小。
uIP使用C编程语言,它可以自用分发和使用于商业和非商业目的。
其它的TCP/IP栈,储存器经常用于数据缓存,等待一个数据已经成功送达的确应信号。
事实上,数据包丢失了,数据必须重发。
有特色的是,数据是缓存在RAM里,如果需要重发数据,应用程序可以快速重生数据。
例如,一个HTTP服务器服务的大部分是ROM里的静态和半静态页,不需要在RAM里缓存静态内容。
所以,如果一个包丢失了,HTTP服务器可以容易地从ROM里重生数据。
数据简单地从原先的位置读回来。
uIP的优越性是允许应用程序参加数据重发。
这个文档由以下部分组成,第2节描述在系统和应用的立场上怎样使用uIP。
第3节详细讨论协议实现细节。
第4 节覆盖了uIP的配置,第5节描述uIP的结构部分。
uIP嵌入式TCP/ IP协议栈uIP1.0参考手册2006年6月Adam Dunkelsadam@sics.se瑞典计算机科学研究所目录:1 uIP的TCP/ IP协议栈11.1 简介. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (1)1.2 TCP/IP通讯. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (2)1.3 主控制回路. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................................ . (2)1.4 结构的特定功能. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (3)1.5 内存管理. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (3)1.6 应用程序接口(API). . . . . . . . . . . . . . . . . . . . . . . . . . . . . (4)1.7 例子. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .............................................. (7)1.8 协议实现. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (14)1.9 性能. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (16)2 uIP1.0模块文件172.1 Protothreads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (17)第1章uIP的TCP / IP堆栈作者:亚当,Adam Dunkels/,http://www.sics.se/uIP的TCP / IP堆栈旨在使 TCP/ IP通信协议套件使用甚至有可能小型8位微控制器。
竭诚为您提供优质文档/双击可除uip协议栈下载篇一:uip之udp应用笔记千兆网项目中,移植了uip到mcu中,采用udp通信方式,主要用来做一些控制协议的处理。
刚开始接手的时候,并没有做过网络方面的应用,而且对tcp/ip及udp通信又不太熟悉。
好在网上有一些文档,加上仔细阅读uip_process 代码,一边用抓包软件一边调试,总算把uip很好的应用了起来,而且还针对项目某些应用的特殊性,对uip源码进行了一些修改。
本文前半部分对uip源码的一些重要函数进行介绍,后半部分将对修改的部分做个记录,以备往后查阅。
本次使用的是uip-1.0,抓包软件用的wireshark1.6.7,这个软件真的很不错,居然支持gigevision,这点真的很意外。
一、一个完整的udp数据报文格式其实uip就是将你要发送到网络上的数据加上报头,好让它被成功发送到目的主机。
所以我们要先搞清楚一个完整的数据报文,才能搞清楚uip到底在做些什么。
ethernetheader:由目标mac和本机mac及type组成,共14byte,当目标mac全为ff时,表示是udp广播。
type=0x0800表示是ip。
在uip中,ethernetheader结构体定义如下:ipheader:0x45表示version=4,headerlength=20byte;0028表示ipheader+udpheader+userdata长度为40byte;6c14为包的id,每发一个包,这个id会自加1。
80的意义是timetolive,表示这个包的存活时间,路由每转发一次,就会对它自减1。
17表示通信协议类型为udp,4a0a为ipheader的校验码。
再后面就是源ip和目的ip地址了。
udpheader:0aaa表示srcport为2730;0f74表示dstprot为3956;14表示udpheader+userdata长度为20byte,c477表示udpheader的校验码,在一般的情况下,这个可以为0。
T C P I P协议规范及U I P处理流程精编Document number:WTT-LKK-GBB-08921-EIGG-22986目录一、简要历史1973年,ARPANET核心组成员Vint Cerf 和 Bob Kahn 发表了一篇里程碑论文,阐述了实现分组的端到端交付的协议。
这篇关于传输控制协议(TCP)的论文包括:封装、数据报,以及网关的功能。
后来,TCP被划分为两个协议:传输控制协议(TCP)和网际互联协议(IP)。
IP处理数据报的路由选择,而TCP负责高层的一些功能,如分段、重装和差错检测。
这个用来进行网际互联的协议后来就被称为TCP/IP。
二、TCP/IP协议族2.1.简介TCP/IP协议族由5层组成:物理层、数据链路层、网络层、运输层和应用层。
前四层与OSI模型的前四层相对应,提供物理标准、网络接口、网际互联、以及运输功能。
而应用层与OSI模型中最高的三层相对应。
TCP/IP协议族中的各层包含了一些相对独立的协议。
在物理层和数据链路层,TCP/IP并没有定义任何协议。
在网络层TCP/IP支持网际互联协议(IP),而IP又由四个支撑协议组成:ARP、RARP、ICMP和IGMP。
在传统上,TCP/IP协议族在运输层有两个运输协议:TCP和UDP,然而现在已经设计出一个新的运输层协议SCTP以满足新的应用的需要。
IP是主机到主机的协议,即把分组从一个物理设备交付到另一个物理设备。
UDP和TCP是运输机协议,负责把报文从一个进程(运行着的程序)交付到另一个进程。
2.2.编址使用TCP/IP协议的互联网使用3个等级的地址:物理(链路)地址、逻辑(IP)地址以及端口地址。
每一种地址属于TCP/IP体系结构中的特定层。
2.2.1物理地址物理地址也叫链路地址,是结点的地址,由它所在的局域网或广域网定义。
物理地址包含在数据链路层使用的帧中。
以太网的地址是6字节(48位)长,通常用十六进制记法,如:07:01:02:01:2C:4B。
uip客户端工作流程网络上关于uIP协议栈的文章不少,大多是讲解自带的http服务器为例子,没有过多的说明作为CS客户端在实际中的应用。
本文主要讲述ENC28J60和uIP协议栈作为CS模式在客户端的应用,即采用主动连接与服务器进行用户数据交互,保持长连接,支持自动重连。
编译器:Keil3 C51 8.18uIP版本:0.9ENC28J60:ENC28J60-I/SO 28-Lead SOIC单片机:SST89E516RD(1K RAM,64K program ROM 支持在线仿真,兼容51单片机)STC89C58RD+ (512 RAM 32K program ROM)烧录测试特点:查询方式收包,定时更新ARP缓存表,协议栈、收、发共用缓存(内存开销少)事件回调函数uip_appcall()支持ICMP/TCP/UDP,端口监听,主/被动连接2 代码文件结构图:2.1 文件列表:2.2 代码流程图:3 系统开销:对于一个完成的TCP /IP协议栈来说,uIP算占用资源比较少的,根据实际应用,本例将去除了demo程序中自带的http服务器,fs部分,将连接数、监听端口表、ARP缓存表大小都设置为1,关掉日志,统计信息,重组包,把系统开销降到更低。
下表描述uIP系统主要开销情况(估算):如果自己新建工程使用本例代码,请将工程属性“Target->Memocy Mode”设置成:Large: variables in XDATA,即使用最大外部内存,否则将产生编译错误,提示内存不足,因为uIP 的RAM开销超过了单片机内部内存128字节(超出mov寻址范围),所以需要movx来完成更多内存访问,有些单片机都内置了外存,打开此选项,Keil C51 C编译器会自动完成外部内存访问。
4 网卡硬件原理图:下图为ENC28J60网卡的参考设计图,SCK,CS,SO,SI直接PIN TO PIN接到单片机(SI和SO不需要反接,不同于串口的是SPI的SO,SI 都是相对于slave而言的),有些单片自带SPI接口,例如本例中使用的SST89E516RD,但我们程序中仍然采用IO口模拟SPI方式,通用性更好。
Uip协议栈初步分析5.1Uip协议栈架构uIP 的代码和这个文档的新版本可以在uIP的主页下载/adam/uip/。
Uip协议栈架构图Uip协议栈包含以下几层:1.硬件驱动程序:包含rtl8019as/ax88796/dm9000等的驱动程序由于uip是个免费的协议栈,在不同芯片合系统上,需要对原有的协议栈进行修改,这个过程就叫做移植,一般的uip协议栈没有提供网络芯片的驱动程序,所幸遇的是/projects/uipAVR.htm已经将uip移植到avr上,并提供了rtl8019as和ax88796的驱动程序,dm9000的驱动程序没有提供。
本开发板提供的软件也是从上述地址的软件移植而来,类似的charon ii 和ethernut软件也提供类似的驱动程序,有兴趣的也可以参考一下。
驱动程序完成,芯片的初始化,复位,溢出处理,读写函数和收发包等,主要函数如下另外对网络芯片的寻址也在此完成!!!!主要文件在rtl8019.h和rtl8019.c中2.nic网络层主要完成网络的初始化,网络芯片的选取,网络芯片的轮训poll等主要文件在nic.h和nic.c中3.uip协议栈层主要实现网络协议栈的具体实现,支持arp/icmp/udp/tcp/http/等为了实现在8位单片机上运行,系统设计时没有采用socket编程方式本层是uip协议栈的核心,所以得协议处理都在本层实现主要函数有:主要文件在uip.h和uip.c中4.uip_app层支持基于udp/tcp的上层应用函数的实现例如如果需要网页功能,需要支持http,那么在app.h/.c中实现具体的功能!!主要文件在app.h和app.c中编译文件其他介绍:Uipopt.h 这个是uip协议栈的配置函数,例如本机ip地址,本机网关,本机屏蔽码,本机mac 地址以及uip协议栈的一些常用设置等都在此修改!Uip_arp.h/.c定义了arp协议的处理函数Uip_arch.h/.c定义了协议栈需要的校验和函数5.2应用程序主要文件列表上图是一个项目的主要包含文件,其中html.h/.c是网页显示的文件!5.3. uip主要函数简介:5.4.uip main函数介绍:/******************************************************************** * Main Control Loop**********************************************************************/ int main(void){unsigned char i;unsigned char arptimer=0;////////////// MY CODE ADD//串口初始化USART_init();/////////////////////////// init NIC device driver//网络初始化函数nic_init();//uip协议栈初始化// init uIPuip_init();// init app 应用程序初始化,比如tcp或者udp ,http的应用!example1_init();// httpd_init();// init ARP cache 初始化arp协议的缓冲uip_arp_init();// init periodic timer 初始化周期函数定时器initTimer();//开放中断sei();//主循环while(1){// look for a packet查询网卡是否有数据包uip_len = nic_poll();if(uip_len == 0)//如果没有数据包{// if timed out, call periodic function for each connection if(timerCounter > TIMERCOUNTER_PERIODIC_TIMEOUT){timerCounter = 0;for(i = 0; i < UIP_CONNS; i++){uip_periodic(i);//周期性检查函数// transmit a packet, if one is readyif(uip_len > 0) //如果包长度大于0 发送包{//主动发送和重发数据包在此进行uip_arp_out();nic_send();}}/* Call the ARP timer function every 10 seconds. */if(++arptimer == 20)//更新arp表{uip_arp_timer();arptimer = 0;}}}else // packet received 接收到网络数据包{// process an IP packet 处理ip数据包if(BUF->type == htons(UIP_ETHTYPE_IP)){// add the source to the ARP cache// also correctly set the ethernet packet length before processinguip_arp_ipin();uip_input();// transmit a packet, if one is readyif(uip_len > 0){uip_arp_out();nic_send();}}// process an ARP packet 处理arp包else if(BUF->type == htons(UIP_ETHTYPE_ARP)){uip_arp_arpin();// transmit a packet, if one is readyif(uip_len > 0)nic_send();}}}return 1;}串口转TCP程序初步本范例主要实现串口数据发送到远程ip的对应端口,串口数据包含帧头和帧尾,主要有串口中断函数,和串口处理函数2个函数构成,分析如下:volatile unsigned int UartTxCount,UartRxCount,UartRxCount1;volatile unsigned char TxSendReady,RxOK,RS232Enable;//串口1通讯程序//没有增加超时处理20060822//SIGNAL(SIG_USART_RECV)#pragma interrupt_handler RS485COM:31//atmega32l @14void RS485COM(void){Rs485_Data=UDR1;//Rs485_Data=UDR;#ifdef MCUA TMEGA32//printf("=%x\n",Rs485_Data);////if(RxOK==1)printf("RxOK==1!\n");#if RS485HEADER//如果是帧头if(Rs485_Data==RSStart ) //接收到开始标志{RsStart=1; //开始接受串口1的数据Rs485Counter=0; //接收计数器清零RxBuf[Rs485Counter]=Rs485_Data; //把数据送入接收缓冲区RXBUFRSCRC=0; //RSCRC清零RxOK=0; //没有接收完成//加上0X55//#if CRC0//RSCRC+=Rs485_Data;//#endifRs485Counter++; //接收计数器加一}else if(Rs485_Data==RSEnd) //接收到结束标志{//检查CRC// if(RSCRC==RxBuf[Rs485Counter-1])// {RxBuf[Rs485Counter]=Rs485_Data; //将结束的数据送入接收缓冲区// UartRxCount=++Rs485Counter;RSLEN=++Rs485Counter; //把接收计数器的值送入RSLENRxOK=1; //接收完成RsStart=0; //清除RSSTARTRS485process();//Rs485Counter=0;// }/*RxBUF[Rs485Counter]=Rs485_Data;//加上0XAARSCRC+=Rs485_Data;//加上0XAA的字节长度Rs485Counter++;*//*else{//清除RxBuffor(j=0;j<TxBufLen;j++)RxBuf[j]='';//置位标志//3.初始化串口变量和标志// UartTxCount=0;UartRxCount=0;TxSendReady=0;RxOK=0;RS232Enable=1;}*/}else{#endif //RS485HEADER//开始接收数据帧中除了头和尾的中间数据//首先检查状态if(RxOK!=1)//如果没有结束{//检查计数器的值是否溢出//将接收的数据送入接收缓冲区RXBUF,同时计算CRC,计数器自动加一if(RsStart==1||Rs485Counter!=0){RxBuf[Rs485Counter]=Rs485_Data;//RSCRC+=Rs485_Data;Rs485Counter++;}#if RS485HEADER}#endif //RS485HEADER}}/*1. 串口及程序初始化;{TxSendReady=0;RxOK=0;RS232Enable=1;}2. 首先从串口接收数据,由于数据以0X55开始,0XAA结束,接收时数据写入RxBuf[];3. 接收完成,置接收标志RxOK=1;4. 判断RS232Enable=1,=1则复制到TxBuf, RxOK=0;TxSendReady=1;5. TCPAPP中判断if(TxSengReady==1),=1启动发送,发送完成清除TxSendReady=1;置位RS232Enable=1,允许数据装载到TxBuf*/void RS485process(void){unsigned char i;//if(UartRxCount!=0){//printf("UartRxCount=%x\n",UartRxCount);//for(i=0;i<UartRxCount;i++)printf("%x",RxBuf[i]);//printf("\n");//}//printf("RxOK=%x ",RxOK);//printf("TCP_SEND_OK=%x " ,TCP_SEND_OK);//printf("RS232Enable=%x ",RS232Enable);//printf("TCP_SEND_READY=%x \n",TCP_SEND_READY);if(TCP_SEND_OK==0){printf("TCP_SEND_OK==0\n");return;//如果发送没有完成则返回}//printf("RS232Enable=%x\n",RS232Enable);//printf("TCP_SEND_READY=%x\n",TCP_SEND_READY);//接收完成if(RSLEN!=0||RS232Enable==1)//如果不是空包和串口更新允许RS232ENABLE=1 {//如果缓冲中已经有数据包,需要把缓冲重的数据保存起来if(TCP_SEND_READY==1){//接受成功//UDR0 = j++;RxOK=0;//清接收成功标志UartRxCount1=RSLEN;//Rs485Counter;//包含包头和包尾的//把数据放入上一个数据包的TCP发送缓冲区的后面for(i=0;i<UartRxCount1;i++){TxBuf[i+UartRxCount]=RxBuf[i];}UartRxCount+=UartRxCount1;RSLEN=0;return;}if(TCP_SEND_OK)TCP_SEND_READY=1;//TCP准备发送else {UartRxCount=0;printf("TCP_SEND_OK=0\n");return;}//接受成功//printf("RxOK=1\n");RxOK=0;//清接收成功标志UartRxCount=RSLEN;//Rs485Counter;//包含包头和包尾的//把数据放入TCP发送缓冲区for(i=0;i<UartRxCount;i++){TxBuf[i]=RxBuf[i];//printf("TxBuf[%x]=%x\n",i,TxBuf[i]);}RSLEN=0;//for(i=0;i<TxBufLen;i++)RxBuf[i]='';//printf("\nTCP_SEND_READY=0\n");//RS232Enable=0;}else{printf("wait");}return;}。
uIP移植笔记2007-07-20 16:44:02| 分类:tcp/ip | 标签:|字号大中小订阅uIP移植笔记本笔记适用于uIP1.0。
移植平台介绍:MSP430F149+cs8900a+IAR1、阅读The uIP Embedded TCP/IP Stack The uIP 1.0 Reference Manual.2、建立一个文件夹,起名myport,将uip-1.0下的uIP和lib两个文件夹拷贝过去,然后再在myport下建立app文件夹。
3、将unix子文件夹下的clock-arch.c、clock-arch.h拷贝到myport下,这个文件实现协议栈所用的时钟,由430的定时器完成,有三个函数:clock_time_t clock_time(void){return ticks;}void clock_init(void){定时器的初始化工作}__interrupt void timer_interrupt(void)/*定时器中断函数*/{++ticks;}。
4、将unix子文件夹下的uip-conf.h拷贝到myport下,这个文件实现协议栈所用的配置,按照需要修改之。
5、写cs8900a的驱动函数,这里采用8位、查询模式,替换tapdev.c 或slipdev.c。
6、将unix子文件夹下的main.c函数拷贝到myport下,这个是主调度流程,按照需要修改。
7、建立自己的工程,将以上文件包含。
8、调试,改错。
其中,uip的缓冲区是以字节数组的形式产生,为了保证它的起始地址是偶数,必须指定地址。
UDP的初始化如下void myudp_init(void){uip_ipaddr_t ipaddr;//定义IP类型变量uip_ipaddr(ipaddr, 210,29,104,88); //远程IP为210.29.104.88if(myudp_conn != NULL){uip_udp_remove(myudp_conn);//如果连接已经建立,则删除之}myudp_conn = uip_udp_new(&ipaddr, HTONS(1000));//建立到远程ipaddr,端口为1000的连接if(myudp_conn != NULL){uip_udp_bind(myudp_conn, HTONS(2000));//绑定本地端口为2000,也就是2000-->1000 发数据}}void myudp_send(char *str,short n){char *nptr;nptr = (char *)uip_appdata;memcpy(nptr, str, n);uip_udp_send(n); //发送n个数据}void newdata(){char *nptr;short len;len = uip_datalen();//读取数据长度nptr = (char *)uip_appdata; //取得数据起始指针if(len<4)myudp_send("Please check the command!\n",26);else if(strncmp(nptr,"getname",7)==0)myudp_send("My name is xiaomu.",19);else myudp_send("Unkown command!\n",16);}/*---------------------------------------------------------------------------*//** \internal* The main UDP function.*//*---------------------------------------------------------------------------*/voidmyudp_appcall(void){if(uip_udp_conn->rport == HTONS(1000)){if(uip_poll()) {myudp_send("hello\n",6);//定时时间到,发hello}if(uip_newdata()) //如果指定IP的指定端口发来数据{newdata();}}}TCP的和这个差不多,初始化时就监听端口uip_listen(HTONS(23));myudp_conn = uip_udp_new(&ipaddr, HTONS(0));//如果远程ipaddr为0,端口也为0,则可以接收来自任何ip任何端口的信息,但必须指定本地端口,即要绑定。