Contiki包含两个通信协议栈
uIP和Rime

UIPTCP/IP协议

导言
随着Internet的成功,TCP/IP协议套件已经成为全球通信的标准。TCP/IP是一种底层协议,用于网页传输、电子邮件传输、文件传输和Internet上的对等网络。对于嵌入式系统,能够运行本机TCP/IP使系统能够直接连接到Intranet,甚至是全局Internet。具有完全TCP/IP支持的嵌入式设备将成为一流的网络公民,从而能够与网络中的其他主机充分通信。

在小型8位或16位系统中,传统的TCP/IP实现在代码大小和内存使用方面都需要太多的资源。几百千字节的代码大小和几百千字节的RAM要求使整个TCP/IP堆栈无法容纳几十千字节的RAM和不足100 KB的代码。

UIP实现设计为只有一个完整的TCP/IP堆栈所需的绝对最小特性集。它只能处理单一的网络接口,包含IP、ICMP、UDP和TCP协议。UIP是用C语言编写的。

许多其他用于小型系统的TCP/IP实现假设嵌入式设备总是与运行在工作站类计算机上的完整TCP/IP实现通信。在这种假设下,可以删除在这种情况下很少使用的某些TCP/IP机制。然而,如果嵌入式设备要与另一个同样有限的设备通信,例如在运行分布式对等服务和协议时,许多这些机制是必不可少的。UIP被设计为符合RFC的,以便让嵌入式设备充当一流的网络公民。不适合任何特定应用程序的UIPTCP/IP实现。

TCP/IP通信
完整的TCP/IP套件由许多协议组成,从低层协议(如将IP地址转换为MAC地址的ARP)到应用程序级协议(如用于传输电子邮件的SMTP)。UIP主要关注TCP和IP协议,上层协议被称为“应用程序”。底层协议通常在硬件或固件中实现,并被称为“网络设备”,由网络设备驱动程序控制。

TCP为上层协议提供可靠的字节流。它将字节流分解为适当大小的段,每个段以自己的IP数据包发送。IP包由网络设备驱动程序在网络上发送。如果目的地不在物理连接的网络上,则IP分组由位于两个网络之间的路由器转发到另一个网络。如果另一个网络的最大数据包大小小于IP数据包的大小,则该数据包被路由器分割成较小的分组。如果可能,选择TCP段的大小,以便将碎片最小化。数据包的最终接收者必须重新组装任何支离破碎的IP数据包,然后才能传递到更高的层。

TCP/IP协议栈中的协议的形式要求是由Internet工程任务组(IETF)发布的许多RFC文档中指定的。堆栈中的每个协议都在另一个RFC文档中定义,RFC 1122收集所有需求并更新以前的RFC。

RFC 1122要求可分为两类:处理主机到主机通信的需求和处理应用程序与网络堆栈之间通信的需求。第一类示例是“TCP必须能够在任何段中接收TCP选项”,第二类示例是“必须有向应用程序报告软TCP错误条件的机制”。违反第一类要求的TCP/IP实现可能无法与其他TCP/IP实现通信,甚至可能导致网络故障。违反第二类要求只会影响系统内部的通信,不会影响主机与主机之间的通信。

在UIP中,所有影响主机到主机通信的RFC需求都被实现.然而,为了减少代码大小,我们删除了应用程序和堆栈之间的某些机制,如软错误报告机制和TCP连接的动态可配置的服务类型位。由于使用这些特性的应用程序很少,因此可以在不失去通用性的情况下删除它们。

主控制回路
UIP堆栈可以作为多任务系统中的任务运行,也可以作为单任务系统中的主程序运行。在这两种情况下,主控制循环重复执行两件事情:

检查数据包是否已从网络到达。
检查是否发生了定期超时。
如果数据包已经到达,则输入处理程序函数,UIP_INPUT(),应该由主控制循环调用。输入处理程序函数永远不会阻塞,但会立即返回。当它返回时,堆栈或用于接收数据包的应用程序可能产生了一个或多个应发送的应答包。如果是这样的话,应该调用网络设备驱动程序来发送这些数据包。

周期性超时用于驱动依赖于定时器的TCP机制,例如延迟确认、重传和往返时间估计。当主控制循环推断周期性计时器应该触发时,它应该调用计时器处理函数uip_周期()。因为TCP/IP堆栈在处理计时器事件时可能执行重传,所以网络设备驱动程序应该被调用来发送可能已经产生的数据包。

特定于体系结构的功能
UIP需要为运行UIP的体系结构专门实现几个功能。这些函数应该针对特定的体系结构进行手工调整,但是一般的C实现是UIP发行版的一部分。

校验和计算
TCP和IP协议实现了包含TCP和IP数据包的数据和报头部分的校验和。由于此校验和的计算是对发送和接收的每个数据包中的所有字节进行的,因此计算校验和的函数是有效的。最常见的情况是,这意味着校验和计算必须针对运行UIP堆栈的特定体系结构进行微调。

虽然uip包含一个泛型校验和函数,但它也为这两个函数的特定于体系结构的实现保留了一个打开状态。uip_ipchksum()和uip_tcpchksum()…这些函数中的校验和计算可以用高度优化的汇编程序(而不是泛型C代码)编写。

32位算法
TCP协议使用32位序列号,作为正常协议处理的一部分,TCP实现必须进行一些32位的添加。由于32位算法在许多UIP打算使用的平台上本机不可用,因此uip将32位添加留给特定于体系结构的模块来实现,并且不使用主代码库中的任何32位算法。

虽然uip实现了一个通用的32位加法,但是支持有一个特定于体系结构的实现。UIP_add32()功能。

内存管理
在UIP的体系结构中,RAM是最稀缺的资源。由于只有几千字节的RAM可供TCP/IP堆栈使用,因此不能直接应用传统TCP/IP中使用的机制。

UIP堆栈不使用显式动态内存分配。相反,它使用一个全局缓冲区来保存数据包,并且有一个固定的表来保存连接状态。全局数据包缓冲区足够大,足以包含一个最大大小的数据包。当数据包从网络到达时,设备驱动程序将其放入全局缓冲区并调用TCP/IP堆栈。如果数据包包含数据,TCP/IP堆栈将通知相应的应用程序。由于缓冲区中的数据将被下一个传入数据包覆盖,因此应用程序要么必须立即对数据采取行动,要么将数据复制到辅助缓冲区中以便稍后处理。在应用程序处理数据之前,数据包缓冲区不会被新数据包覆盖。当应用程序处理数据时到达的数据包必须由网络设备或设备驱动程序排队。大多数单片以太网控制器都有足够大的片上缓冲区,至少可以容纳4个最大大小的以太网帧。由处理器处理的设备,如RS-232端口,可以在应用程序处理期间将传入的字节复制到单独的缓冲区中。如果缓冲区已满,则丢弃传入的数据包。这将导致性能下降,但仅当多个连接并行运行时。这是因为UIP广告的接收窗口非常小,这意味着每个连接的网络中只有一个TCP段。

在UIP中,用于传入数据包的相同的全局数据包缓冲区也用于传出数据的TCP/IP报头。如果应用程序发送动态数据,它可以使用全局数据包缓冲区中未用于报头的部分作为临时存储缓冲区。要发送数据,应用程序将指向数据的指针以及数据的长度传递给堆栈。TCP/IP报头被写入全局缓冲区,一旦产生报头,设备驱动程序就将报头和应用程序数据发送到网络上。数据没有排队等待重传。相反,如果需要重传,应用程序将不得不复制数据。

UIP的内存使用总量在很大程度上取决于要运行实现的特定设备的应用程序。内存配置决定了系统应该能够处理的通信量和同时连接的最大数量。一个设备将发送大量电子邮件,同时运行一个具有高度动态网页和多个同时客户端的Web服务器,它将比简单的Telnet服务器需要更多的RAM。只需200字节的RAM就可以运行UIP实现,但是这样的配置将提供极低的吞吐量,并且只允许少量的同时连接。

应用程序接口(API)
应用程序接口(API)定义了应用程序与TCP/IP堆栈的交互方式。TCP/IP最常用的API是BSD套接字API,它在大多数Unix系统中使用,严重影响了MicrosoftWindowsWinSock API。由于套接字API使用停止和等待语义,它需要底层多任务操作系统的支持。由于任务管理、上下文切换和任务堆栈空间分配的开销在预期的UIP目标体系结构中可能过高,因此BSD套接字接口不适合我们的目的。

uip为程序员提供了两个api:protosocket,一个类似bsd套接字的api,没有完全多线程的开销,以及一个基于“原始”事件的api,它比protosocket级别低,但使用的内存更少。

另见:
Protosocket库
原螺纹
UIP原始API
“RAW”UIPAPI使用事件驱动的接口,其中应用程序在响应某些事件时被调用。运行在UIP之上的应用程序作为一个C函数实现,UIP响应某些事件调用该函数。UIP在接收到数据、数据已成功地传递到连接的另一端、新连接已经设置或数据必须重新传输时调用应用程序。还定期对应用程序进行新数据的轮询。应用程序只提供一个回调函数,由应用程序处理将不同网络服务映射到不同端口和连接的问题。由于应用程序能够在TCP/IP堆栈接收到数据包时立即处理传入数据和连接请求,因此即使在低端系统中也可以实现低响应时间。

UIP不同于其他TCP/IP堆栈,因为它在重传时需要应用程序的帮助。其他TCP/IP堆栈将传输的数据缓冲在内存中,直到已知数据成功地传递到连接的远程端为止。如果需要重新传输数据,堆栈将在不通知应用程序的情况下处理重传。使用这种方法,数据必须在内存中缓冲,同时等待确认,即使如果必须重新传输,应用程序可能能够快速重新生成数据。

为了减少内存使用,UIP利用了应用程序可以重新生成发送的数据并允许应用程序参与重传的事实。UIP不跟踪设备驱动程序发送的数据包内容,UIP要求应用程序主动参与重传。当UIP决定重新传输一个段时,它使用一个标志集调用应用程序,该标志集指示需要重新传输。应用程序检查重传标志,并生成与以前发送的数据相同的数据。从应用程序的角度来看,执行重传与最初发送数据的方式没有什么不同。因此,可以以这样的方式编写应用程序,即将相同的代码用于发送数据和重传数据。另外,需要注意的是,即使实际的重传操作是由应用程序执行的,堆栈也有责任知道何时应该进行重传。因此,应用程序的复杂性不一定增加,因为它在重传中起着积极的作用。

应用事件
应用程序必须实现为C函数,UIP_APPCALL(),每当发生事件时,UIP都会调用。每个事件都有一个相应的测试函数,用于区分不同的事件。这些函数被实现为C宏,计算值为零或非零。请注意,某些事件可以同时发生(例如,新数据可以在确认数据的同时到达)。

连接指针
当应用程序被UIP调用时,全局变量UIP_CON设置为指向UIP_CON结构,用于当前处理的连接,称为“当前连接”。的田野UIP_CON可以使用当前连接的结构,例如区分不同的服务或检查连接到哪个IP地址。一个典型的用途是检查uip_conn->lport(本地TCP端口号),以决定连接应该提供哪些服务。例如,如果uip_conn->lport的值等于80,应用程序可能决定充当HTTP服务器;如果值为23,应用程序可能决定充当Telnet服务器。

接收数据
如果UIP测试函数UIP_newdata()为非零,连接的远程主机已发送新数据.uip_AppData指针指向实际数据。数据的大小是通过uip函数获得的。UIP_datalen()…数据不是由UIP缓冲的,而是在应用程序函数返回后被覆盖,因此应用程序必须直接对传入的数据采取行动,或者自己将传入的数据复制到缓冲区中以供以后处理。

发送数据
在发送数据时,UIP根据可用的缓冲区空间和接收方公布的当前TCP窗口调整应用程序发送的数据的长度。缓冲区空间的大小由内存配置决定。因此,从应用程序发送的所有数据都可能没有到达接收方,并且应用程序可以使用UIP_MSS()函数查看堆栈实际发送的数据。

应用程序使用UIP函数发送数据。UIP_Send()…这个UIP_Send()函数有两个参数:指向要发送的数据的指针和数据的长度。如果应用程序需要RAM空间来生成应该发送的实际数据,则可以为此使用数据包缓冲区(由uip_AppData指针指向)。

应用程序一次只能在连接上发送一个数据块,因此无法调用。UIP_Send()每次应用程序调用不止一次;只发送上次调用的数据。

重传数据
重传由周期性TCP定时器驱动。每次调用周期性计时器时,每个连接的重传定时器都会减少。如果计时器达到零,则应进行重传。由于UIP不跟踪设备驱动程序发送的数据包内容,因此UIP要求应用程序积极参与重传。当UIP决定重新传输一个段时,应用程序函数将使用uip_rexmit()标志集,指示需要重传。

应用程序必须检查uip_rexmit()标记并生成与以前发送的数据相同的数据。从应用程序的角度来看,执行重传与最初发送数据的方式没有什么不同。因此,应用程序的编写方式可以使相同的代码既用于发送数据,又用于重传数据。另外,需要注意的是,即使实际的重传操作是由应用程序执行的,堆栈也有责任知道何时应该进行重传。因此,应用程序的复杂性不一定增加,因为它在重传中起着积极的作用。

合闸连接
应用程序通过调用UIP_CLOSE()在应用程序调用过程中。这将导致连接被彻底关闭。为了指示致命错误,应用程序可能希望中止连接,并通过调用UIP_ABORT()功能。

如果连接已被远程端关闭,则测试函数UIP_CLOSE()是真的。然后,应用程序可以进行任何必要的清理。

报告错误
连接可能发生两个致命错误,要么是连接被远程主机中止,要么是连接多次重新传输最后一个数据并已中止。UIP通过调用应用程序函数来报告这一点。应用程序可以使用这两个测试函数。UIP_ABORT()和UIP_timedout()测试这些错误条件。

轮询
当连接空闲时,UIP每次周期性计时器触发时都会轮询应用程序。应用程序使用测试函数。UIP_ROUP()以检查UIP是否正在对其进行调查。

轮询事件有两个目的。第一种方法是让应用程序周期性地知道连接是空闲的,这允许应用程序关闭已经闲置太久的连接。另一个目的是让应用程序发送已经生成的新数据。应用程序只能在UIP调用时发送数据,因此轮询事件是在其他空闲连接上发送数据的唯一方法。

监听港口
UIP维护一个监听TCP端口的列表。将打开一个新端口,用于侦听UIP_LISK()功能。当连接请求到达侦听端口时,UIP创建一个新连接并调用应用程序函数。测试函数UIP_Connected()如果由于创建了新连接而调用应用程序,则为true。

应用程序可以在UIP_CON结构检查新连接连接到哪个端口。

开口连接
函数可以从UIP内部打开新的连接。UIP_CONNECT()…此函数分配一个新连接,并在连接状态下设置一个标志,该标志将在下次UIP轮询连接时打开到指定IP地址和端口的TCP连接。这个UIP_CONNECT()函数返回指向UIP_CON新连接的结构。如果没有空闲连接槽,则函数返回NULL。

struct uip_conn* uip_connect(uip_ipaddr_t  *ripaddr,uint16_t port)使用TCP连接到远程主机。此函数用于启动到指定主机上指定端口的新连接。它分配一个新的连接标识符,将连接设置为SYN_SINT状态,并将重传计时器设置为0。这将导致下一次定期处理此连接时发出TCP SYN段,通常在调用UIP_CONNECT- **注:**只有当通过将UIP_ACTIVE_OPEN定义为1配置了对Active OPEN的支持时,此函数才可用uipopt.h由于此函数要求端口号按网络字节顺序排列,所以使用UIP_HTONS()或uip_hton()是必要的。UIP_HTONS()
将16位数量从主机字节顺序转换为网络字节顺序.
此宏主要用于将常量从主机字节顺序转换为网络字节顺序。若要将变量转换为网络字节顺序,请使用uip_hton()功能代替。附:对应函数CCIF uint16_t    uip_htons (uint16_t val)Convert a 16-bit quantity from host byte order to network byte order. 将16位数量从主机字节顺序转换为网络字节顺序uip_ipaddr_t ipaddr;uip_ipaddr(&ipaddr, 192,168,1,2);uip_connect(&ipaddr, UIP_HTONS(80));- **参数:**波波远程主机的IP地址。港按网络字节顺序排列的16位端口号。- **返回:**指向新连接的UIP连接标识符的指针,如果无法分配连接,则为NULL。

功能uip_ipaddr()可用于将IP地址打包到UIP用来表示IP地址的两个元素16位数组中。

使用的两个例子如下所示。第一个示例演示如何打开当前连接的远程端的TCP端口8080的连接。如果没有足够的tcp连接槽允许打开新连接,则UIP_CONNECT()函数返回NULL,当前连接由UIP_ABORT().

void connect_example1_app(void) {if(uip_connect(uip_conn->ripaddr, HTONS(8080)) == NULL) {uip_abort();}
}

第二个示例演示如何打开到特定IP地址的新连接。在此示例中不进行错误检查。

void connect_example2(void) {uip_addr_t ipaddr;uip_ipaddr(ipaddr, 192,168,0,1);uip_connect(ipaddr, HTONS(8080));
}

实例
本节介绍了许多非常简单的UIP应用程序。UIP代码发行版包含几个更复杂的应用程序。

一个非常简单的应用程序
第一个例子展示了一个非常简单的应用程序。应用程序侦听端口1234上的传入连接。建立连接后,应用程序将回复发送给它的所有数据,即“OK”。

此应用程序的实现如下所示。应用程序由调用example1_init()的函数初始化,UIP回调函数称为example1_app()。对于这个应用程序,配置变量UIP_APPCALL应该定义为example1_app()。

void example1_init(void) {uip_listen(HTONS(1234));
}
void example1_app(void) {if(uip_newdata() || uip_rexmit()) {uip_send("ok\n", 3);}
}

初始化函数调用UIP函数。UIP_LISK()若要注册侦听端口,请执行以下操作。实际应用程序函数example1_app()使用测试函数UIP_newdata()和uip_rexmit()来确定为什么叫它。如果应用程序是因为远程端发送了数据而被调用的,它会用“ok”进行响应。如果由于网络中数据丢失而调用了应用程序函数,并且必须重新传输,那么它也会发送一个“ok”。注意,这个示例实际上显示了一个完整的UIP应用程序。应用程序不需要处理所有类型的事件,例如UIP_Connected()或UIP_timedout().

更高级的应用程序
第二个示例比前一个示例稍微高级一些,并展示了UIP_CON结构使用。

该应用程序类似于第一个应用程序,因为它侦听传入连接的端口,并通过一个“ok”响应发送给它的数据。最大的区别是这个应用程序输出了一个欢迎的“欢迎!”在建立连接时发出消息。

这个看似很小的操作更改对应用程序的实现方式产生了很大的影响。复杂性增加的原因是,如果数据在网络中丢失,应用程序必须知道要重传哪些数据。如果“欢迎!”消息丢失,应用程序必须重新发送欢迎,如果“ok”消息之一丢失,应用程序必须发送一个新的“ok”。

应用程序知道只要“欢迎!”远程主机尚未确认消息,它可能已被丢弃在网络中。但是,一旦远程主机发送回确认,应用程序就可以确定欢迎已经收到,并且知道任何丢失的数据都必须是“OK”消息。因此,应用程序可以处于两种状态之一:无论是在欢迎发送状态,还是在“欢迎!”已被发送但未被确认,或处于受欢迎状态的“欢迎!”已经被认可了。

当远程主机连接到应用程序时,应用程序发送“欢迎!”信息,并设置它的状态欢迎-发送。当确认欢迎消息时,应用程序将移动到受欢迎的状态。如果应用程序从远程主机接收到任何新数据,它将通过发送“ok”返回来响应。

如果请求应用程序重新传输最后一条消息,它将查看应用程序处于哪个状态。如果应用程序处于欢迎发送状态,则发送“欢迎!”因为它知道以前的欢迎信息还没有被确认。如果应用程序处于欢迎状态,它知道最后一条消息是“ok”消息,并发送这样一条消息。

此应用程序的实现如下所示。应用程序的此配置设置在其实现后遵循。

struct example2_state {enum {WELCOME_SENT, WELCOME_ACKED} state;
};void example2_init(void) {uip_listen(HTONS(2345));
}void example2_app(void) {struct example2_state *s;s = (struct example2_state *)uip_conn->appstate;if(uip_connected()) {s->state = WELCOME_SENT;uip_send("Welcome!\n", 9);return;} if(uip_acked() && s->state == WELCOME_SENT) {s->state = WELCOME_ACKED;}if(uip_newdata()) {uip_send("ok\n", 3);}if(uip_rexmit()) {switch(s->state) {case WELCOME_SENT:uip_send("Welcome!\n", 9);break;case WELCOME_ACKED:uip_send("ok\n", 3);break;}}
}

应用程序的配置:

#define UIP_APPCALL       example2_app
#define UIP_APPSTATE_SIZE sizeof(struct example2_state)

区分应用程序
如果系统应该运行多个应用程序,那么区分它们的一种技术是使用连接的远程端或本地端的TCP端口号。下面的示例显示了如何将上述两个示例组合到一个应用程序中。

void example3_init(void) {example1_init();example2_init();
}void example3_app(void) {switch(uip_conn->lport) {case HTONS(1234):example1_app();break;case HTONS(2345):example2_app();break;}
}

利用TCP流量控制
此示例展示了一个简单的应用程序,该应用程序连接到主机,发送文件的HTTP请求并将其下载到诸如磁盘驱动器这样的慢速设备上。这说明了如何使用UIP的流量控制功能。

void example4_init(void) {uip_ipaddr_t ipaddr;uip_ipaddr(ipaddr, 192,168,0,1);uip_connect(ipaddr, HTONS(80));
}void example4_app(void) {if(uip_connected() || uip_rexmit()) {uip_send("GET /file HTTP/1.0\r\nServer:192.186.0.1\r\n\r\n",48);return;}if(uip_newdata()) {device_enqueue(uip_appdata, uip_datalen());if(device_queue_full()) {uip_stop();}}if(uip_poll() && uip_stopped()) {if(!device_queue_full()) {uip_restart();}}
}

建立连接后,会向服务器发送HTTP请求。由于这是发送的唯一数据,应用程序知道,如果需要重新传输任何数据,则应该重新传输该请求。因此,可以像示例中所做的那样将这两个事件组合起来。

当应用程序从远程主机接收新数据时,它将使用FunctionDevice_enQueue()将该数据发送到设备。需要注意的是,此示例假定此函数将数据复制到自己的缓冲区中。uip_AppData缓冲区中的数据将被下一个传入数据包覆盖。

如果设备的队列已满,应用程序将通过调用UIP函数从远程主机停止数据UIP_STOP()…然后,应用程序可以确保它不会接收任何新的数据,直到UIP_RESTART()叫做。应用程序轮询事件用于检查设备的队列是否不再满,如果是,则用UIP_RESTART().

一个简单的Web服务器
此示例显示一个非常简单的文件服务器应用程序,该应用程序侦听两个端口并使用端口号来确定要发送的文件。如果文件被正确格式化,这个简单的应用程序可以用作带有静态页面的Web服务器。执行情况如下。

struct example5_state {char *dataptr;unsigned int dataleft;
};void example5_init(void) {uip_listen(HTONS(80));uip_listen(HTONS(81));
}void example5_app(void) {struct example5_state *s;s = (struct example5_state)uip_conn->appstate;if(uip_connected()) {switch(uip_conn->lport) {case HTONS(80):s->dataptr = data_port_80;s->dataleft = datalen_port_80;break;case HTONS(81):s->dataptr = data_port_81;s->dataleft = datalen_port_81;break;}uip_send(s->dataptr, s->dataleft);return;      }if(uip_acked()) {if(s->dataleft < uip_mss()) {uip_close();return;}s->dataptr += uip_conn->len;s->dataleft -= uip_conn->len;uip_send(s->dataptr, s->dataleft);      }
}

应用程序状态由指向应该发送的数据和要发送的数据的大小的指针组成。当远程主机连接到应用程序时,将使用本地端口号来确定要发送的文件。第一个数据块使用UIP_Send()…UIP确保实际发送的数据不超过MSS字节,即使s->dataleft可能大于MSS。

应用程序由传入的确认驱动。当数据被确认后,可以发送新的数据。如果没有更多的数据要发送,则使用UIP_CLOSE().

结构化应用程序设计
在使用UIP编写更大的程序时,能够以结构化的方式使用UIPAPI是很有用的。下面的示例提供了一个结构化的设计,它显示了自己对于编写比这里展示的前面示例更大的协议实现是有用的。该程序分为UIP事件处理函数,该函数调用七个应用程序处理函数,这些函数处理新数据、对确认的数据进行操作、发送新数据、处理连接建立或关闭事件以及处理错误。这些函数被称为newdata()、aced()、senddata()、Connected()、CLOSE()、ABOTED()和timedout(),并且需要专门为正在实现的协议编写。

UIP事件处理程序函数如下所示。

void example6_app(void) {if(uip_aborted()) {aborted();}if(uip_timedout()) {timedout();}if(uip_closed()) {closed();}if(uip_connected()) {connected();}if(uip_acked()) {acked();}if(uip_newdata()) {newdata();}if(uip_rexmit() ||uip_newdata() ||uip_acked() ||uip_connected() ||uip_poll()) {senddata();}
}

该函数首先处理可能发生的任何错误条件,方法是检查UIP_ABORT()或UIP_timedout()都是真的。如果是,则调用适当的错误函数。另外,如果连接已经关闭,则会将CLOPEN()函数调用到它处理该事件。

接下来,函数通过检查UIP_Connected()是真的。Connected()函数被调用,并应该在建立连接时执行任何需要执行的操作,例如对连接的应用程序状态进行弱化。由于数据应该被发送出去,所以调用senddata()函数来处理传出数据。

下面这个非常简单的应用程序可以作为应用程序处理程序函数外观的示例。此应用程序只需等待任何数据到达连接,并通过发送消息“HelloWorld!”来响应数据。为了说明如何开发应用程序状态机,这条消息分为两部分,首先是“Hello”部分,然后是“World!”部分。

#define STATE_WAITING 0
#define STATE_HELLO   1
#define STATE_WORLD   2struct example6_state {uint8_t state;char *textptr;int  textlen;
};static void aborted(void) {}
static void timedout(void) {}
static void closed(void) {}static void connected(void) {struct example6_state *s = (struct example6_state *)uip_conn->appstate;s->state   = STATE_WAITING;s->textlen = 0;
}static void newdata(void) {struct example6_state *s = (struct example6_state *)uip_conn->appstate;if(s->state == STATE_WAITING) {s->state   = STATE_HELLO;s->textptr = "Hello ";s->textlen = 6;}
}static void acked(void) {struct example6_state *s = (struct example6_state *)uip_conn->appstate;s->textlen -= uip_conn->len;s->textptr += uip_conn->len;if(s->textlen == 0) {switch(s->state) {case STATE_HELLO:s->state   = STATE_WORLD;s->textptr = "world!\n";s->textlen = 7;break;case STATE_WORLD:uip_close();break;}}
}static void senddata(void) {struct example6_state *s = (struct example6_state *)uip_conn->appstate;if(s->textlen > 0) {uip_send(s->textptr, s->textlen);}
}

应用程序状态由一个“state”变量、一个指向文本消息的“textptr”指针和文本消息的“textlen”长度组成。“state”变量可以是“state_Waing”,这意味着应用程序正在等待来自网络的数据,“state_Hello”,其中应用程序发送消息的“Hello”部分,或者“state_world”,其中应用程序发送“World”!留言。

应用程序不处理错误或连接关闭事件,因此中止的()、timedout()和CLOSE()函数被实现为空函数。

连接()函数在建立连接时将被调用,在本例中,将“state”变量设置为“state_贮备”,将“textlen”变量设置为零,这意味着没有要发送的消息。

当新数据从网络到达时,事件处理程序函数将调用newdata()函数。newdata()函数将检查连接是否处于“状态等待”状态,如果是,则切换到“state_Hello”状态,并在连接中注册一个6字节长的“Hello”消息。此消息稍后将由senddata()函数发送出去。

每当接收主机认可先前发送的数据时,就调用aced()函数。这个aced()函数首先通过从“textlen”变量中减去以前发送的数据的长度(从“uip_conn->len”获得)来减少要发送的数据量,并相应地调整“textptr”指针。然后检查“textlen”变量现在是否为零,这表明所有数据都已成功接收,如果是,则更改应用程序状态。如果应用程序处于“state_Hello”状态,它会将状态切换为“state_world”,并设置要发送的7字节“world!\n”消息。如果应用程序处于“state_world”状态,则关闭连接。

最后,senddata()函数负责实际发送要发送的数据。当收到新数据、确认数据、建立新连接、由于不活动而轮询连接或重新传输时,事件处理程序函数将调用该函数。函数的目的是可选地格式化要发送的数据,并调用UIP_Send()函数来实际发送数据。在这个特定的例子中,函数只需调用UIP_Send()如果要发送数据,则使用适当的参数,检查数据是否应该按照“textlen”变量的指示发送出去。

需要注意的是,senddata()函数永远不会影响应用程序状态;这应该只在aced()和newdata()函数中进行。

协议实现
TCP/IP协议套件中的协议是以分层方式设计的,每个协议执行特定的功能,并严格定义协议层之间的交互。虽然分层方法是设计协议的好方法,但并不总是实现它们的最佳方法。在UIP中,为了节省代码空间,协议实现紧密耦合。

本节详细介绍了UIP中的具体协议实现。

IP-因特网协议
当UIP处理传入的数据包时,IP层是检查数据包的第一个协议。IP层执行一些简单的检查,例如传入数据包的目标IP地址是否匹配任何本地IP地址,并验证IP报头校验和。由于没有严格要求的IP选项,而且由于它们非常少见,所以接收到的数据包中的任何IP选项都会被丢弃。

IP片段重组
IP片段重组是使用一个单独的缓冲区来实现的,该缓冲区保存要重新组装的数据包。将传入片段复制到缓冲区中的正确位置,并使用位映射来跟踪接收到的片段。因为IP片段的第一个字节是在一个8字节的边界上对齐的,所以位映射需要少量的内存。当所有片段被重新组装后,产生的IP数据包被传递到传输层。如果没有在指定的时间范围内接收到所有片段,则丢弃该数据包。

当前的实现只有一个缓冲区,用于保存要重新组装的数据包,因此不支持同时重组多个数据包。由于分段数据包是不常见的,这应该是一个合理的决定。但是,将实现扩展到支持多个缓冲区是很简单的。

广播和多播
IP具有在本地网络上广播和多播数据包的能力。这样的分组被寻址到特殊的广播和多播地址。广播在许多基于UDP的协议中被大量使用,比如MicrosoftWindows文件共享SMB协议。组播主要用于用于多媒体分发(如RTP)的协议中。TCP是一种点对点协议,不使用广播或多播数据包.UIP当前支持广播数据包以及发送多播数据包。目前不支持加入多播组(IGMP)和接收非本地多播数据包.

ICMP-因特网控制消息协议
ICMP协议用于报告软错误条件和查询主机参数。然而,它的主要用途是“ping”程序使用的回波机制。

UIP中的ICMP实现非常简单,因为它仅限于实现ICMP回送消息。对回显消息的答复是通过简单地交换传入回送请求的源和目标IP地址,并使用Echo-Reply消息类型重写ICMP报头来构造的。ICMP校验和使用标准技术进行调整(参见RFC 1624)。

由于只实现了ICMP回送消息,因此不支持路径MTU发现或ICMP重定向消息。这两种机制都不是互操作性的严格要求;它们都是性能增强机制。

传输控制协议
UIP中的TCP实现是由传入数据包和计时器事件驱动的。传入的数据包由TCP解析,如果数据包包含要传递给应用程序的数据,则通过应用程序函数调用的方式调用应用程序。如果传入数据包确认先前发送的数据,则更新连接状态并通知应用程序,允许它发送新数据。

监听连接
TCP允许连接侦听传入的连接请求。在UIP中,侦听连接由16位端口号标识,传入连接请求根据侦听连接列表进行检查。此侦听连接列表是动态的,可由系统中的应用程序更改。

滑动窗
大多数TCP实现使用滑动窗口机制发送数据。连续发送多个数据段,而不等待每个段的确认。

滑动窗口算法使用了很多32位操作,因为32位算法在大多数8位CPU上相当昂贵,所以UIP没有实现它。此外,UIP不缓冲发送的数据包,而不缓冲发送的数据包的滑动窗口实现必须由复杂的应用层支持。相反,UIP只允许在任何给定时间内不承认每个连接的一个TCP段。

需要注意的是,即使大多数TCP实现使用滑动窗口算法,TCP规范也不需要滑动窗口算法。删除滑动窗口机制不会以任何方式影响互操作性。

往返时间估计
TCP不断估计每个活动连接的当前往返时间(RTT),以便为重传超时找到合适的值。

利用TCP的周期定时器实现了UIP中的RTT估计。每次周期性计时器触发时,它都会为网络中有未确认数据的每个连接增加一个计数器。当接收到确认时,计数器的当前值被用作RTT的示例。该样本与Van Jacobson的标准TCP RTT估计函数一起计算RTT的估计值。Karn的算法被用来确保重传不会扭曲估计值。

重传
重传由周期性TCP定时器驱动。每次调用周期性计时器时,每个连接的重传定时器都会减少。如果计时器达到零,则应进行重传。

由于UIP不跟踪设备驱动程序发送的数据包内容,因此UIP要求应用程序积极参与重传。当UIP决定重新传输一个段时,它使用一个标志集调用应用程序,该标志集指示需要重新传输。应用程序检查重传标志,并生成与以前发送的数据相同的数据。从应用程序的角度来看,执行重传与最初发送数据的方式没有什么不同。因此,可以以这样的方式编写应用程序,即将相同的代码用于发送数据和重传数据。另外,需要注意的是,即使实际的重传操作是由应用程序执行的,堆栈也有责任知道何时应该进行重传。因此,应用程序的复杂性不一定增加,因为它在重传中起着积极的作用。

流量控制
TCP流量控制机制的目的是允许内存尺寸变化很大的主机之间的通信。在每个TCP段中,该段的发送方指示其可用的缓冲区空间。TCP发送方不得发送比接收方指示的缓冲区空间更多的数据。

在UIP中,应用程序不能发送比接收主机可以缓冲的数据更多的数据。应用程序发送的数据量不能超过接收主机允许发送的字节数。如果远程主机根本无法接受任何数据,则堆栈启动零窗口探测机制。

拥塞控制
拥塞控制机制限制了网络中同步TCP段的数量。用于拥塞控制的算法设计简单,只需几行代码即可实现。

由于UIP每个连接只处理一个正在运行的TCP段,因此不能进一步限制同步段的数量,因此不需要拥塞控制机制。

紧急数据
TCP的紧急数据机制提供了应用程序到应用程序的通知机制,应用程序可以使用该机制将数据流的部分标记为比正常流更紧急。紧急数据的含义由接收方来解释。

在许多TCP实现中,包括BSD实现,紧急数据特性增加了实现的复杂性,因为它需要在其他同步API中使用异步通知机制。由于UIP已经使用了基于异步事件的API,紧急数据特性的实现不会导致复杂性的增加。

性能
在用于高端系统的TCP/IP实现中,处理时间主要由校验和计算循环、复制数据包的操作和上下文交换所支配。用于高端系统的操作系统通常有多个保护域,用于保护内核数据不受用户进程和用户进程的影响。由于TCP/IP堆栈在内核中运行,因此必须在内核空间和用户进程的地址空间之间复制数据,并且在复制数据后必须执行上下文切换。通过将复制操作与校验和计算相结合,可以提高性能。由于高端系统通常有许多活动连接,分组解复用也是一种昂贵的操作.

小型嵌入式设备不具备具有多个保护域的必要处理能力,也没有运行多任务操作系统的能力。因此,不需要在TCP/IP堆栈和应用程序之间复制数据。对于基于事件的API,TCP/IP堆栈和应用程序之间没有上下文切换。

在这种有限的系统中,TCP/IP处理开销主要是将数据包从网络设备复制到主机内存,并进行校验和计算。除了校验和计算和复制之外,TCP对传入数据包的处理只需要更新几个计数器和标志,然后再将数据交给应用程序。因此,可以通过计算校验和计算和复制最大大小分组所需的CPU周期来估计TCP/IP实现的CPU开销。

延迟确认的影响
大多数TCP接收器实现延迟确认算法,以减少发送的纯确认数据包的数量。使用此算法的TCP接收器只会发送对每个其他接收段的确认。如果在特定的时间范围内没有接收到任何段,则发送确认。时间框架可以高达500毫秒,但通常是200毫秒。

只处理单个未处理TCP段的TCP发送方(如UIP)与延迟确认算法的交互效果很差。因为接收器一次只接收一个段,所以在发送确认之前它将等待多达500 ms。这意味着最大可能的吞吐量受到500 ms空闲时间的严重限制。

因此,从UIP发送数据时的最大吞吐量方程是p=s/(t+td)p=s/(t+t_d)p=s/(t+td​),其中sss是段大小,tdt_dtd​是延迟确认超时,通常在200到500 ms之间。如果段大小为1000字节,往返时间为40 ms,延迟确认超时为200 ms,则最大吞吐量将为每秒4166字节。在接收端禁用延迟确认算法后,最大吞吐量将为每秒25000字节。

但是,应当指出,由于运行UIP的小型系统不太可能有大量数据要发送,因此UIP的延迟确认器吞吐量下降不一定非常严重。这样一个系统发送的少量数据不会超过一个TCP段,因此不会受到吞吐量下降的影响。

UIP作为接收方时的最大吞吐量不受延迟确认吞吐量下降的影响。

注:
这个UIPTCP吞吐量增强器攻击模块实现了一种黑客,克服了延迟确认吞吐量下降带来的问题。

Data Structures数据结构

union    uip_ip4addr_tRepresentation of an IP address. 表示一个IP地址。
struct  uip_802154_shortaddr16 bit 802.15.4 address 16位802.15.4地址
struct  uip_802154_longaddr64 bit 802.15.4 address
struct  uip_80211_addr802.11 address More...
struct  uip_eth_addr802.3 address More...
struct  uip_connRepresentation of a uIP TCP connection. uIP TCP连接的表示。
struct  uip_udp_connRepresentation of a uIP UDP connection. 表示一个uIP UDP连接。
struct  uip_statsThe structure holding the TCP/IP statistics that are gathered if UIP_STATISTICS is set to 1. 如果UIP_STATISTICS设置为1,则收集的TCP/IP统计信息的结构。

Modules模块

Protosockets library   protosocket库

protosocket库为uIP堆栈提供了一个类似于传统BSD套接字接口的接口。

uIP hostname resolver functions  uIP主机名解析器函数

uIP DNS解析器函数用于查找主机名并将其映射到数字IP地址。

Simple-udp  简单UDP

The default Contiki UDP API is difficult to use.
默认的Contiki UDP API很难使用。

Serial Line IP (SLIP) protocol   串行线IP (SLIP)协议

The SLIP protocol is a very simple way to transmit IP packets over a serial line.
SLIP协议是在串行线路上传输IP数据包的一种非常简单的方法。

The Contiki/uIP interface  Contiki /摘要界面

TCP/IP support in Contiki is implemented using the uIP TCP/IP stack.
Contiki的TCP/IP支持是使用uIP TCP/IP栈实现的。

uIP packet forwarding   UIO数据包转发

uIP TCP吞吐量增强黑客

基本的uIP TCP实现只允许每个TCP连接在任何给定时间运行一个TCP段。

uIP configuration functions  UIP配置函数

uIP配置函数用于设置uIP中的运行时参数,如IP地址。

uIP initialization functions    UIP初始化函数

uIP初始化函数用于启动uIP。

uIP device driver functions    uIP设备驱动程序功能

这些功能由网络设备驱动程序用于与uIP交互。

uIP application functions    UIP应用函数

运行在uIP顶部的应用程序使用的函数。

uIP conversion functions    UIP转换函数

这些函数可用于在uIP使用的不同数据格式之间进行转换。

Variables used in uIP device drivers    UIP驱动设备变量

uIP有一些用于uIP的设备驱动程序的全局变量。

uIP Address Resolution Protocol    uIP地址解析协议

地址解析协议ARP用于IP地址和链路层地址(如以太网MAC地址)之间的映射。

Configuration options for uIP    uIP的配置选项

uIP is configured using the per-project configuration file “uipopt.h”.
uIP是使用每个项目的配置文件“uipop .h”来配置的。

uIP IPv6 specific features   uIP IPv6的具体功能

uIP IPv6栈为Contiki提供了新的互联网通信能力。

6LoWPAN implementation   6 lowpan实现

6lowpan是IETF的一个工作组,它定义了在IEEE 802.15.4链路上使用IPv6。

Architecture specific uIP functions    架构特定的uIP功能

体系结构特定模块中的函数实现了IP校验和和32位加法。

Files
file    uip.cThe uIP TCP/IP stack code.file uip.hHeader file for the uIP TCP/IP stack.

Defines

#define  UIP_STAT(s)The uIP TCP/IP statistics. uIP TCP/IP统计数据。
#define UIP_APPDATA_SIZEThe buffer size available for user data in the uip_buf buffer. uip_buf缓冲区中用户数据可用的缓冲区大小。

Typedefs

typedef union uip_ip4addr_t  uip_ip4addr_tRepresentation of an IP address. IP地址的表示
typedef struct uip_802154_shortaddr     uip_802154_shortaddr16 bit 802.15.4 address 16位802.15.4地址
typedef struct uip_802154_longaddr  uip_802154_longaddr64 bit 802.15.4 address 64位802.14.5地址
typedef struct uip_80211_addr   uip_80211_addr802.11 address
typedef struct uip_eth_addr     uip_eth_addr802.3 address
typedef uip_eth_addr    uip_lladdr_tEthernet address.

Functions

void uip_setipid (uint16_t id)uIP initialization function. UIP初始化功能。此函数可在启动时用于设置初始ip_id。
void    uip_add32 (uint8_t *op32, uint16_t op16)Carry out a 32-bit addition. 执行32位加法.
uint16_t    uip_chksum (uint16_t *buf, uint16_t len)Calculate the Internet checksum over a buffer. 计算缓冲区上的Internet校验和。
uint16_t    uip_ipchksum (void)Calculate the IP header checksum of the packet header in uip_buf. 计算uip_BUF中数据包报头的IP报头校验和。
uint16_t    uip_tcpchksum (void)Calculate the TCP checksum of the packet in uip_buf and uip_appdata. 计算uip_BUF和uip_AppData中数据包的TCP校验和。
void    uip_init (void)uIP initialization function. UIP初始化功能。此函数应在启动时调用,以初始化UIPTCP/IP堆栈
struct uip_udp_conn *   uip_udp_new (const uip_ipaddr_t *ripaddr,
uint16_t rport)Set up a new UDP connection. 建立一个新的UDP连接。
void    uip_unlisten (uint16_t port)Stop listening to the specified port. 停止监听指定的端口。
void    uip_listen (uint16_t port)Start listening to the specified port. 开始监听指定的端口。
void    uip_process (uint8_t flag)process the options within a hop by hop or destination option header 在逐跳或目标选项头中处理选项
uint16_t    uip_htons (uint16_t val)Convert a 16-bit quantity from host byte order to network byte order. 将16此函数主要用于将变量从主机字节顺序转换为网络字节顺序。
void    uip_send (const void *data, int len)Send data on the current connection. 发送当前连接上的数据。
uint16_t    uip_udpchksum (void)Calculate the UDP checksum of the packet in uip_buf and uip_appdata. 计算uip_BUF和uip_AppData中数据包的UDP校验和。
uint16_t    uip_icmp6chksum (void)Calculate the ICMP checksum of the packet in uip_buf. 计算uip_BUF中数据包的ICMP校验和。

Variables

void *   uip_appdataPointer to the application data in the packet buffer. 指向数据包缓冲区中的应用程序数据的指针。
uint16_t    uip_lenThe length of the packet in the uip_buf buffer. uip_BUF缓冲区中数据包的长度。
struct uip_conn *   uip_connPointer to the current TCP connection. 指向当前TCP连接的指针。
struct uip_udp_conn *   uip_udp_connThe current UDP connection. 当前的UDP连接。
uint8_t uip_acc32 [4]4-byte array used for the 32-bit sequence number calculations. 用于32位序列号计算的4字节数组。
CCIF void * uip_appdataPointer to the application data in the packet buffer. 指向数据包缓冲区中的应用程序数据的指针。
CCIF struct uip_conn *  uip_connPointer to the current TCP connection. 指向当前TCP连接的指针。
struct uip_udp_conn *   uip_udp_connThe current UDP connection. 当前的UDP连接。
CCIF uip_lladdr_t   uip_lladdrHost L2 address. 主机L2地址

Define Documentation

#define UIP_APPDATA_SIZE
uip_buf缓冲区中用户数据可用的缓冲区大小。
这个宏保存uip_buf缓冲区中用户数据的可用大小。该宏用于检查可用用户数据的界限。

Example:

 snprintf(uip_appdata, UIP_APPDATA_SIZE, "%u\n", i);
#define UIP_STAT (       s   )
The uIP TCP/IP 统计数据
这是收集uIP TCP/IP统计信息的变量。
由uip_icmp6_echo_request_input(),
uip_icmp6_error_output(), uip_nd6_na_input(),
uip_nd6_ns_input(), uip_nd6_ns_output(),
uip_nd6_ra_input(), uip_nd6_rs_output(), and uip_process().调用

Function Documentation

void uip_add32   (   uint8_t *   op32,
uint16_t    op16
)
执行32位加法。
因为并不是所有uIP的体系结构都具有本机32位算术,所以uIP使用一个外部
C函数来完成TCP协议处理中所需的32位附加。这个函数应该添加两个参数,
并将结果放在全局变量uip_acc32中。
由uip_process(), and uip_split_output().调用

Note:
op32参数指向的32位整数和uip_acc32变量中的结果是按照网络字节顺序(大端)排列的。
Parameters:
op32 指向一个4字节数组的指针,该数组以网络字节顺序表示一个32位整数。
op16 主机字节顺序的16位整数。

uint16_t uip_chksum  (   uint16_t *  buf,
uint16_t    len
)
通过缓冲区计算Internet校验和。
internet校验和是缓冲区中所有16位单词的1的补码和的1的补码。
See RFC1071.
由uip_process().调用

Parameters:
buf 指向要在其上计算校验和的缓冲区的指针。
len 要计算校验和的缓冲区的长度。
Returns:
缓冲区的Internet校验和。

uint16_t uip_htons   (   uint16_t    val )
将16位数量从主机字节顺序转换为网络字节顺序。
由hc_compress(), mac_LowpanToEthernet(), uip_chksum(),
uip_ipchksum(), and uip_udp_new().调用

此函数主要用于将变量从主机字节顺序转换为网络字节顺序。要将常量转换为网络字节顺序,请使用UIP_HTONS()宏。

uint16_t uip_icmp6chksum (   void        )
计算uip_buf中包的ICMP校验和。
由mac_translateIcmpLinkLayer(), uip_icmp6_echo_request_input(),
uip_icmp6_error_output(), uip_icmp6_send(),
uip_nd6_ns_input(), uip_nd6_ns_output(), uip_nd6_rs_output(),and uip_process().调用

Returns:
uip_buf中ICMP包的ICMP校验和

void uip_init    (   void        )
uIP initialization function.
由main().调用

在启动时应调用此函数以初始化uIP TCP/IP堆栈。

uint16_t uip_ipchksum    (   void        )   计算uip_buf中数据包报头的IP报头校验和。由 hc_inflate(), uip_ipchksum(), uip_process(), and uip_split_output().调用

IP报头校验和是IP报头的20个字节的Internet校验和。

Returns:
uip_buf缓冲区中IP报头的校验和。

void uip_listen  (   uint16_t    port    )
开始监听指定的端口。

Note:
由于此函数期望端口号按网络字节顺序排列,因此需要使用UIP_HTONS()或UIP_HTONS()进行转换。
uip_listen (UIP_HTONS (80));
Parameters:
port 网络字节顺序的16位端口号。

void uip_process (   uint8_t     flag    )
处理单跳或目标选项头中的选项

Return values:
0,: nothing to send,
1,: drop pkt
2,: 发送ICMP错误消息

void uip_send    (   const void *    data,
int     len
)
发送当前连接的数据。

此函数用于发送TCP数据的单个段。只有uIP为事件处理调用的应用程序才能发送数据。
调用此函数后实际发送出去的数据量由TCP允许的最大数据量决定。uIP将自动裁剪数据,以便只发送适当数量的数据。函数uip_mss()可用于查询uIP,以获得实际要发送的数据量。

Note:
此函数不保证发送的数据将到达目的地。如果数据在网络中丢失,将通过设置uip_rexmit()事件调用应用程序。然后应用程序必须使用此函数重新发送数据。
Parameters:
data 指向要发送的数据的指针。
len 要发送的最大数据字节数。

void uip_setipid (   uint16_t    id  )
uIP initialization function.

这个函数可以在启动时用于设置初始ip_id。

uint16_t uip_tcpchksum   (   void        )
计算uip_buf和uip_appdata中数据包的TCP校验和。
由uip_process(), uip_split_output(), and uip_tcpchksum().调用

TCP校验和是TCP段数据内容的Internet校验和,是RFC793中定义的伪头。

Returns:
uip_buf中TCP段的TCP校验和,由uip_appdata指向。

struct uip_udp_conn* uip_udp_new (   const uip_ipaddr_t *    ripaddr,
uint16_t    rport
)       [read]
建立一个新的UDP连接
由udp_new().调用

这个函数设置一个新的UDP连接。该函数将自动为新连接分配一个未使用的本地端口。但是,在调用uip_udp_new()函数之后,可以使用uip_udp_bind()调用来选择另一个端口。

Example:

uip_ipaddr_t addr;struct uip_udp_conn *c;uip_ipaddr(&addr, 192,168,2,1);c = uip_udp_new(&addr, UIP_HTONS(12345));if(c != NULL) {uip_udp_bind(c, UIP_HTONS(12344));}

Parameters:
ripaddr 远程主机的IP地址。
rport 网络字节顺序的远程端口号。
Returns:
新连接的uip_udp_conn结构,如果无法分配连接,则为NULL。

uint16_t uip_udpchksum   (   void        )
计算uip_buf和uip_appdata中数据包的UDP校验和。
由 uip_process().调用

UDP校验和是UDP段数据内容的Internet校验和,以及RFC768中定义的伪头。

Returns:
uip_buf中的UDP校验和,由uip_appdata指向。

void uip_unlisten    (   uint16_t    port    )
停止监听.

Note:
由于此函数期望端口号按网络字节顺序排列,因此需要使用UIP_HTONS()或UIP_HTONS()进行转换。
uip_unlisten (UIP_HTONS (80));
Parameters:
port 网络字节顺序的16位端口号。

Variable Documentation

void* uip_appdata
指向包缓冲区中的应用程序数据的指针。
由slipdev_send(), uip_arp_out(), uip_fw_forward(),uip_process(), uip_split_output(), and uip_tcpchksum().调用

该指针在调用应用程序时指向应用程序数据。如果应用程序希望发送数据,那么在调用uip_send()之前,应用程序可以使用这个空间来写入数据。

CCIF void* uip_appdata
指向包缓冲区中的应用程序数据的指针。

该指针在调用应用程序时指向应用程序数据。如果应用程序希望发送数据,那么在调用uip_send()之前,应用程序可以使用这个空间来写入数据。

struct uip_conn* uip_conn
指向当前TCP连接的指针。
由uip_process().调用

uip_conn指针可用于访问当前TCP连接。

CCIF struct uip_conn* uip_conn
指向当前TCP连接的指针。

uip_conn指针可用于访问当前TCP连接。

uint16_t uip_len
uip_buf缓冲区中包的长度。
由mac_ethernetToLowpan(), mac_LowpanToEthernet(),
mac_translateIcmpLinkLayer(), slipdev_send(),
tcpip_input(), uip_arp_arpin(), uip_arp_out(),uip_ds6_periodic(), uip_fw_forward(), uip_fw_output(), uip_icmp6_echo_request_input(), uip_icmp6_error_output(), uip_icmp6_send(), uip_nd6_na_input(), uip_nd6_ns_input(), uip_nd6_ns_output(), uip_nd6_ra_input(), uip_nd6_rs_output(), uip_process(), and uip_split_output().调用

全局变量uip_len在uip_buf缓冲区中保存包的长度。
当网络设备驱动程序调用uIP输入函数时,uip_len应该设置为uip_buf缓冲区中包的长度。
发送数据包时,设备驱动程序应该使用uip_len变量的内容来确定发送数据包的长度。

contiki学习笔记(十二)UIPTCP/IP协议相关推荐

  1. Python语言入门这一篇就够了-学习笔记(十二万字)

    Python语言入门这一篇就够了-学习笔记(十二万字) 友情提示:先关注收藏,再查看,12万字保姆级 Python语言从入门到精通教程. 文章目录 Python语言入门这一篇就够了-学习笔记(十二万字 ...

  2. 吴恩达《机器学习》学习笔记十二——机器学习系统

    吴恩达<机器学习>学习笔记十二--机器学习系统 一.设计机器学习系统的思想 1.快速实现+绘制学习曲线--寻找重点优化的方向 2.误差分析 3.数值估计 二.偏斜类问题(类别不均衡) 三. ...

  3. ROS学习笔记十二:使用roswtf

    ROS学习笔记十二:使用roswtf 在使用ROS过程中,roswtf工具可以为我们提供ROS系统是否正常工作的检查作用. 注意:在进行下列操作之前,请确保roscore没有运行. 检查ROS是否安装 ...

  4. Polyworks脚本开发学习笔记(十二)-输出和读取文本文件

    Polyworks脚本开发学习笔记(十二)-输出和读取文本文件 Polyworks作为一个测量工具,将测量的数据方便的导出到文本文件则是一项必须的功能.在DATA_FILE这个命令下提供了很多子命令用 ...

  5. OpenCV学习笔记(十二)——图像分割与提取

    在图像处理的过程中,经常需要从图像中将前景对象作为目标图像分割或者提取出来.例如,在视频监控中,观测到的是固定背景下的视频内容,而我们对背景本身并无兴趣,感兴趣的是背景中出现的车辆.行人或者其他对象. ...

  6. 【现代机器人学】学习笔记十二:轮式移动机器人

    目录 轮式机器人类型 全向轮式机器人 建模 单个全向轮是怎么运动的 多个全向轮是如何带动底盘运动的 运动规划和反馈控制 非完整约束轮式移动机器人 建模 独轮车 差速驱动机器人 车型机器人 非完整移动机 ...

  7. 【theano-windows】学习笔记十二——卷积神经网络

    前言 按照进度, 学习theano中的卷积操作 国际惯例, 来一波参考网址 Convolutional Neural Networks (LeNet) 卷积神经网络如何应用在彩色图像上? 卷积小知识 ...

  8. (C/C++学习笔记) 十二. 指针

    十二. 指针 ● 基本概念 变量的地址就是指针,存放指针的变量就是指针变量(因而又叫作地址变量 address variable); 这个地址编号本身就是一个无符号的整数,在32位系统下为4字节(8位 ...

  9. Vue.js 学习笔记 十二 Vue发起Ajax请求

    首先需要导入vue-resource.js,可以自己下载引入,也可以通过Nuget下载,它依赖于Vue.js. 全局使用方式: Vue.http.get(url,[options]).then(suc ...

  10. Json.Net学习笔记(十二) 协议解析

    IContractResolver接口提供了一种方法去定制序列化器如何去序列化和反序列化.Net对象为Json对象 实现IContractResolver接口,然后为Json序列化器分配一个实例对象, ...

最新文章

  1. 美国政府签署网络安全行政令 将全面加强网络安全建设
  2. Eclipse里不同的project,右键选择属性property facet里看到的list 内容是否相同
  3. [SpringSecurity]web权限方案_用户认证_查询数据库完成认证
  4. 前端学习(2311):react中处理跨域问题
  5. ERROR 1044 (42000): Access denied for user ''@'localhost' to database
  6. 开发软件不是闭卷考试
  7. python 循环 覆盖之前print内容_Python爬虫第二战---爬取500px图片
  8. 7-1 最长连续递增子序列 (20 分)
  9. css 背景色渐变 background linear-gradient
  10. STM32F407 窗口看门狗 个人笔记
  11. php对接监控摄像头源码,摄像头监控录像源代码 (详细的代码,可以直接使用,也可以拿来学习使用)...
  12. Linux常用网络指令
  13. 彻底解决联想手机数据连接不能上网问题(无需恢复出厂设置) 本文来自移动叔叔论坛 ,详细出处请参考:http://bbs.ydss.cn/thread-201115-1-1.html
  14. 手机 SMS PDU 格式参考手册
  15. 企业数据资产管理:数据资产目录应该如何规划
  16. 全球最豪华手机诺基亚Vertu
  17. java静态方法mult_学会使用函数式编程的程序员(第3部分)
  18. 对手在开拓,苹果在“堕落”,创新路上,苹果还能走多远
  19. Job 和 Event
  20. Java笔试题库之选择题汇总

热门文章

  1. 拷贝漫画检索下载爬虫
  2. 零基础学习C++系列课程(一) 持续更新中
  3. 一键还原涂鸦图片_涂鸦的图片可以复原吗
  4. android 使用ios字体大小,ios和android上的字体大小不同
  5. c语言中指数函数fabs,高一指数函数公式,高一指数函数
  6. java设计模式-设配器模式
  7. 第十二周练兵区——编程题——不计入总分
  8. BUUCTF Quoted-printable编码
  9. mongodb 建立索引提示异常:WiredTigerIndex::insert: key too large to index, failing 1483
  10. win10 android叹号,win10系统设备管理器驱动显示黄色感叹号的详细技巧