uip协议栈uip_process函数工作流程详解

合集下载

uip协议栈,下载

uip协议栈,下载

竭诚为您提供优质文档/双击可除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

uip

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。

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

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

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工作原理和过程

osip工作原理和过程

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流程

uip_process流程

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协议栈分析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的if和else函数 -回复

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,表明它是一个奇数。

IRP原理以及派遣函数基本工作流程

IRP原理以及派遣函数基本工作流程

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 以太网例程讲解

奋斗STM32开发板uIP1.0 以太网例程讲解

奋斗版 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中文文档第四应用层要调用的函数

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

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-中文说明

uip-中文说明

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简介

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位微控制器。

TCPIP协议规范及UIP处理流程

TCPIP协议规范及UIP处理流程

2.4.6 传输控制协议(TCP) ................................................................................. 18
三、 UIP 处理流程........................................................................................................................21 3.1. 简介 ..................................................................................................................... 21
2.4. 分层协议讲解...................................................................................................... 11
2.4.1 ARP 和 RARP................................................................................................. 12
3.2.4 uip 的初始化与配置函数 ............................................................................. 31
1
3.2.5 Uip 的主程序循环 ........................................................................................ 33 3.2.6 主要的处理函数 uip_process()..................................................................... 35 3.2.7 再来分析 UIP_UDP_SEND_CONN,主要处理 UDP 报文的发送:............... 38 3.2.8 接下来,分析 UIP_POLL_REQUEST .............................................................. 39 3.2.9 对定时器期满的处理流程 UIP_TIMER ......................................................... 40 3.2.10 对 UIP_UDP_TIMER 的处理流程 ............................................................... 41 3.2.11 原始套接字和原始线程............................................................................ 41

uip协议栈下载

uip协议栈下载

竭诚为您提供优质文档/双击可除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。

TCPIP协议规范及UIP处理流程精编

TCPIP协议规范及UIP处理流程精编

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客户端工作流程

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中文文档第七 uIP编译时配置选项 [

UIP中文文档第七 uIP编译时配置选项 [
复制代码
1. #define UIP_APPCALL smtp_appcall TCP/IP 事件的应答函数名称. 2. typedef smtp_state uip_tcp_appstate_t 存储在 uip_conn 中的应用状态类
型. 3. typedef int uip_udp_appstate_t 存储在 uip_conn 中的应用状态类型 4. #define UIP_LITTLE_ENDIAN 3412 5. #define UIP_BIG_ENDIAN 1234
16. #define UIP_REASSEMBLY 打开 IP 包重组. uip 支持碎片 IP 包的重组.此项特性需要多余的 RAM 来盛放重组缓冲区.重组代码大小约为 700 字节.重组缓冲区的大小与 uip_buf 的大小(由 UIP_BUFSIZE 配置)相同. 注意: IP 包重组并未经过严格测试. 定义于 uipopt.h 的 156 行.
复制代码 针对应用的配置: UIP 应用是使用单个应用函数数实现的.只要 TCP/IP 事件发生,uIP 就会调用这个函数.这个 函数的名字必须在编译时使用 UIP_APPCALL 注册到 uIP. uIP 应用可以在 uip_conn 结构中保存应用状态.这是通过利用 typedef uip_tcp_appstate_t 和 uip_udp_appstate_t 指定应用的类型实现的.包含此定义的文件必须被包含在 uipopt.h 文件
1. #define UIP_UDP UDP 支持是否编译 2. #define UIP_UDP_CHECKSUMS. 是否使用 UDP 校验和. 3. #define UIP_UDP_CONNS 最大并发 UDP 连接数.
复制代码 TCP 配置选项:

第二章 uIP协议栈分析V1.00

第二章 uIP协议栈分析V1.00

2ቤተ መጻሕፍቲ ባይዱ 2
uIP 架构
uIP相当于一个代码库,通过一系列的函数实现与底层硬件和高层应用程序 的通讯, 对于整个系统来说它内部的协议组是透明的, 从而增加了协议的通用性。 uIP协议栈与系统底层和高层应用之间的关系如图2-1所示。
图2-1 uIP在系统中的位置 从上图可以看出, uIP 协议栈主要提供了三个函数供系统底层调用。即 uip_init(), uip_input() 和 uip_periodic() 。 其 与 应 用 程 序 的 主 要 接 口 是
本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2007,版权所有, 仅供试用。
UIP_APPCALL( )。 uip_init()是系统初始化时调用的,主要初始化协议栈的侦听端口和默认所有 连接是关闭的。 当网卡驱动收到一个输入包时,将放入全局缓冲区 uip_buf 中,包的大小由 全局变量 uip_len 约束。同时将调用 uip_input()函数,这个函数将会根据包首部 的协议处理这个包和需要时调用应用程序。当 uip_input()返回时,一个输出包同 样放在全局缓冲区 uip_buf 里,大小赋给 uip_len。如果 uip_len 是 0,则说明没 有包要发送。否则调用底层系统的发包函数将包发送到网络上。 uIP 周期计时是用于驱动所有的 uIP 内部时钟事件。当周期计时激发,每一 个 TCP 连接都会调用 uIP 函数 uip_periodic()。 类似于 uip_input()函数。 uip_periodic() 函数返回时,输出的 IP 包要放到 uip_buf 中,供底层系统查询 uip_len 的大小发 送。 由于使用 TCP/IP 的应用场景很多,因此应用程序作为单独的模块由用户实 现。uIP 协议栈提供一系列接口函数供用户程序调用,其中大部分函数是作为 C 的宏命令实现的,主要是为了速度、代码大小、效率和堆栈的使用。用户需要将 应 用 层 入 口 程 序 作 为 接 口 提 供 给 uIP 协 议 栈 , 并 将 这 个 函 数 定 义 为 宏 UIP_APPCALL()。这样,uIP 在接受到底层传来的数据包后,在需要送到上层应 用程序处理的地方,调用 UIP_APPCALL( )。在不用修改协议栈的情况下可以适 配不同的应用程序。

uip简介

uip简介

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实例——精选推荐

uip实例——精选推荐

uIP移植笔记2007-07-20 16:44:02| 分类:tcp&#47;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任何端口的信息,但必须指定本地端口,即要绑定。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
{ ①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) 如果连接不处于关闭状态 {
case UIP_ESTABLISHED: (1)接收到远方主机的 FIN 请求:
① uip_flags |= UIP_CLOSE;关闭 TCP 连接 ②如果接收到的数据包中还包含有数据则 uip_flags |= UIP_NEWDATA; ③调用 UIP_APPCALL()处理刚关闭的连接和新接收到数据; ④发送 TCP_FIN +TCP_ACK,关闭连接; (2)如果接收到的数据状态为 UIP_NEWDATA | UIP_ACKDATA 则: ①调用 UIP_APPCALL();处理接收到的包; ② appsend: (1)如果(uip_flags & UIP_ABORT)终止连接则
①tcpstateflags = UIP_CLOSED;将连接置为关闭状态 ②uip_flags = UIP_CLOSE;调用 UIP_APPCALL();通知应用程序连接已经断开;
case UIP_FIN_WAIT_1: (1)此时本机已经关闭连接等待对方关闭连接,如果接收到数据并不处理,仅仅将接收到数 据包数目加一; (2)如果接收到 FIN 请求:
reset: (1)接收到的是 RST 断开连接包,则直接丢包,返回; (2)设置 RST+ACK 标志,填充适当的 TCP 帧头; (3)goto tcp_send_noconn;发送 TCP 数据包;
found_listen: (1)从链接列表中找出一个空链接或剩余生存时间最短的连接; (2)将找到的链接列表根据接收到的 TCP 数据包进行初始化; (3)设置 TCP 状态为 UIP_SYN_RCVD; (4)向下执行,发送 ACK tcp_send_synack: (1)设置 ACK 标志 (2)向下执行 tcp_send_syn: (1)设置 SYN 标志 (2)填充 TCP 选项中最大报文段长度 MSS (3) goto tcp_send;
———————————————————————————————————————
tcp_input: (1)检查 TCP 校验和,若正确向下继续,若错误则丢弃此包直接返回; (2)在 TCP 连接列表 uip_conns 中轮询,检查接收到的 TCP 数据包是否已经建立连接(通过逐 个比较源端口、目的端口和源 IP 是否与链接列表中的相同)。
(3)检查 IP 帧头中的 IP 版本及 IP 头长度是否符合要求: ①不符合:goto drop;丢弃此包 ②符合继续向下执行
(4)检查目的 IP 地址是否为本机地址: ①不是,goto drop;丢弃此包 ②是,向下继续执行
(5)if(BUF->proto == UIP_PROTO_TCP)IP 上层协议是否为 TCP 协议 ①是,goto tcp_input;进入 TCP 数据处理模块 ②不是,继续向下执行
——————————————————————————————————————— udp_input: (1)根据要求校验 UDP 数据 (2)在 UDP 连接列表中寻找接收到的数据包是否属于列表中的连接,若是则 goto udp_found;
如果不是则 goto drop;
udp_found: (1)接收到数据数设置 uip_flags = UIP_NEWDATA; 将 uip_sappdata ,uip_appdata 指向接收 到的 UDP 包的数据部分。 (2)调用 UIP_UDP_APPCALL();使应用程序处理接收到的数据; (3)继续向下执行 udp_send: (1)如果 uip_slen == 0 表明没有数据要发送,则直接 goto drop; (2)计算 UDP 数据包长度,填充 UDP、IP 帧头中的数据长度及相关选项; (3)根据要求计算校验和; (4)goto ip_send_nolen;发送 UDP 数据包;
case UIP_SYN_RCVD: goto tcp_send_synack;重新发送先前发送的 SYN+ACK
case UIP_SYN_SENT: goto tcp_send_syn;重发 SYN 请求连接
case UIP_ESTABLISHED: uip_flags = UIP_REXMIT; UIP_APPCALL(); 调用上层应用程序,通知重新生成数据重发 goto apprexmit;进入重发阶段
found: (1)若接收到的是 RST 数据包,则将本连接状态置为 UIP_CLOSED,uip_flags = UIP_ABORT;, 调用 UIP_APPCALL()通知应用程序处理连接断开请求。然后丢弃此包,直接返回; (2)检查接收到的数据包中的数据编号是否为自己等在等待的数据编号,若不是则 goto tcp_send_ack;发送自己期望的数据编号的数据,即请求重传。若是则继续向下; (3)检查接收到的数据包中是否包含 ACK,
case UIP_FIN_WAIT_1: case UIP_CLOSING:
case UIP_LAST_ACK: goto tcp_send_finack;重发 FIN+ACK 关闭连接 } else if(tcpstateflags) == UIP_ESTABLISHED) 处于稳定连接状态且上次发送的数据
case UIP_SYN_SENT: (1)如果接收到 ACK 且为 SYN+ACK 则:
①检查 TCP 扩展选项,如果有扩展选项从中取出 MSS 信息; ②tcpstateflags = UIP_ESTABLISHED;进入 ESTABLISHED 状态 ③设置接收编号,uip_flags = UIP_CONNECTED | UIP_NEWDATA;调用 UIP_APPCALL()处 理刚建立的连接和新接收到数据; ④goto appsend; (2)没有接收到 ACK 且为 SYN+ACK 则: ①uip_flags = UIP_ABORT;终止连接调用 UIP_APPCALL(); ②tcpstateflags = UIP_CLOSED;关闭 TCP 连接 ③goto reset;
①tcpstateflags = UIP_CLOSED;关闭 TCP 连接; ②发送 RST+ACK 关闭连接; (2)如果(uip_flags & UIP_CLOSE)正常关闭连接则: ①tcpstateflags = UIP_FIN_WAIT_1;进入等待关闭状态 ②发送 FIN+ACK 告知对方关闭连接; (3)如果 uip_slen > 0 有数据要发送则设置发送数据的长度
若找到 goto found; 没有找到则检查接收到的 TCP 数据包中是否含有 SYN 请求建立连接标志:
若没有则 goto reset;发送 RST+ACK 断开连接; 若有则检查 uip_listenports 监听列表,若 TCP 数据包目的端口在监听列表中则 goto found_listen;若不在监听列表中则向下执行,进入 reset;发送 RST+ACK 断开连接;
(6)if(BUF->proto == UIP_PROTO_UDP)IP 上层协ቤተ መጻሕፍቲ ባይዱ是否为 UDP 协议 ①是,goto udp_input;进入 UDP 数据处理模块 ②不是,继续向下执行
(7)if(BUF->proto != UIP_PROTO_ICMP) 不是 TCP 不是 UDP 也不是 ICMP 协议
apprexmit: (1)如果(uip_slen > 0 && uip_connr->len > 0)则发送 PSH_ACK 数据包; (2)如果(uip_flags & UIP_NEWDATA)仅仅是发送 ACK,没有数据要发送则发送对接收
到数据的 ACK;
(3) goto drop;
case UIP_LAST_ACK: (1)如果 uip_flags & UIP_ACKDATA 接收到对本机发送的 FIN 的 ACK 确认则:
若是则: ①更新发送数据序列的编号,使之可以发送后续数据; ②计算 RTT 时间,重新设置 RTT 时间; ③uip_flags = UIP_ACKDATA;表明接收到 ACK ④uip_connr->len = 0;表明等待 ACK 的数据长度为 0,即可以发送其它数据 ⑤继续向下;
若不是:继续向下;
①goto drop;本机只处理 UDP、TCP、ICMP 数据包,其它包都将丢弃
(8)运行到此处,表明接收到的是 ICMP 数据包,继续向下执行; ——————————————————————————————————————— icmp_input:
此处为 ICMP 数据包处理部分,比较简单不做详解。 此部分仅仅接收 ECHO 命令,若接收到别的命令,则将数据包丢弃。若接收到的是 ECHO 命令则返回包含 ECHO_REPLY 的 ICMP 数据包给远方主机,主要是用来响应 ping 命令。
接收到正确的 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; } }
相关文档
最新文档