demo

#define PORT 12345                          //通信端口设置
//udp数据结构
static struct udp_socket s;
//uip的ip地址
static uip_ipaddr_t addr;static struct uip_ds6_notification n;
//定义变量i
static uint8_t i=0;
//设置发送时间间隔
#define SEND_INTERVAL        (10 * CLOCK_SECOND)
//定义周期事件定时器,发送事件定时器
static struct etimer periodic_timer, send_timer;/*---------------------------------------------------------------------------*/
//声明unicast_example_process 进程
PROCESS(unicast_example_process, "Link local unicast example process");
//系统初始化启动unicast_example_process进程
AUTOSTART_PROCESSES(&unicast_example_process);/*---------------------------------------------------------------------------*/
//路由回调函数,处理路由事件
static void
route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *ipaddr,int numroutes)
{if(event == UIP_DS6_NOTIFICATION_DEFRT_ADD) {leds_off(LEDS_ALL);printf("Got a RPL route\n");}
}/*---------------------------------------------------------------------------*/
//接收函数
static void
receiver(struct udp_socket *c,void *ptr,const uip_ipaddr_t *sender_addr,uint16_t sender_port,const uip_ipaddr_t *receiver_addr,uint16_t receiver_port,const uint8_t *data,uint16_t datalen)
{printf("Data received on port %d from port %d with length %d, '%s'\n",receiver_port, sender_port, datalen, data);
}
/*---------------------------------------------------------------------------*/
//unicast_example_process进程实现
PROCESS_THREAD(unicast_example_process, ev, data)
{//定义变量保存ipv6的地址uip_ip6addr_t ip6addr;//定义变量保存ipv4的地址uip_ip4addr_t ip4addr;//进程开始PROCESS_BEGIN();
#if 0/* Create a linkl-local multicast addresses. */uip_ip6addr(&addr, 0xff02, 0, 0, 0, 0, 0, 0x1337, 0x0001);/* Join local group. */if(uip_ds6_maddr_add(&addr) == NULL) {printf("Error: could not join local multicast group.\n");}
#endif//调用回调函数leds_on(LEDS_ALL);//调用回调函数uip_ds6_notification_add(&n, route_callback);/* Register UDP socket callback *///注册udp接收回调函数udp_socket_register(&s, NULL, receiver);/* Bind UDP socket to local port *///端口绑定udp_socket_bind(&s, PORT);/* Connect UDP socket to remote port *///连接服务器udp_socket_connect(&s, NULL, PORT);while(1) {/* Set up two timers, one for keeping track of the send interval,which is periodic, and one for setting up a randomized send timewithin that interval. */etimer_set(&periodic_timer, SEND_INTERVAL);//etimer_set(&send_timer, (random_rand() % SEND_INTERVAL));PROCESS_WAIT_UNTIL(etimer_expired(&periodic_timer));uip_ipaddr(&ip4addr, 192,168,18,86);ip64_addr_4to6(&ip4addr, &ip6addr);printf("Sending unicast %d\n",i);i++;//发送数据udp_socket_sendto(&s,&i, 1,&ip6addr, PORT);//PROCESS_WAIT_UNTIL(etimer_expired(&periodic_timer));}PROCESS_END();
}

1、static struct udp_socket s;

这里是定义了uip内的一个UDP结构体,深入udp_socket;

struct udp_socket {udp_socket_input_callback_t input_callback;void *ptr;struct process *p;struct uip_udp_conn *udp_conn;};

这里又涉及到结构体内第一个 callback 函数,为了弄清input_callback函数,这里有必要去了解dup_socket_input_callback_t 这个的定义

typedef void (* udp_socket_input_callback_t)(struct udp_socket *c,void *ptr,const uip_ipaddr_t *source_addr,uint16_t source_port,const uip_ipaddr_t *dest_addr,uint16_t dest_port,const uint8_t *data,uint16_t datalen);
/*typedef定义一种新类型 udp_socket_input_callback_t,并定义这种类型为指向某种函数的指针,
这种函数以后面的8个数据为参数并返回void类型。后面就可以像使用int,char一样使用udp_socket_input_callback_t了*/

返回上一层,那么input_callback变量就是指向一个函数A,函数A返回值为void,形参为上面的8个参数;

继续udp_socket的研究,第二行定义了一个指针ptr,可以指向任意类型的数据,先不讨论指向的是哪,继续下一行;

struct process *p :这里p指向进程结构体,保存进程信息,这也是一个链表保存;

struct uip_udp_conn *udp_conn :这里需要了解uip_udp_conn,给出的注释是Representation of a uIP UDP connection.具体看代码:

struct uip_udp_conn {uip_ipaddr_t ripaddr;   /**< The IP address of the remote peer.这个uip_ipaddr_t在配置使用ipv4还是ipv6时,就定了这个地址类型 * /uint16_t lport;        /**< The local port number in network byte order. */uint16_t rport;        /**< The remote port number in network byte order. */uint8_t  ttl;          /**< Default time-to-live. *//** The application state. */uip_udp_appstate_t appstate;
};

对于上面代码中的uip_udp_appstate_t 类型,进行查看,给出的定义是

typedef struct tcpip_uipstate uip_udp_appstate_t;

也就是说,uip_udp_appstate_t 是一个tcpip_uipstate类型的结构体变量

而继续研究tcpip_uipstate结构体

struct tcpip_uipstate { struct process *p; void *state; };

这里又一个process ,指向进程结构体,保存进程信息,那么这个进程会是什么进程呢??? 同时定义了一个空类型指针;
以上,结构体 udp_socket分析完毕,这里遗留了一个问题,,udp_socket中 *p指向的process进程,在udp_conn中,也有一个指针指向该porcess。如下图

2、static uip_ipaddr_t addr;

uip地址变量

3、static struct uip_ds6_notification n;

这里定义一个静态结构体n,类型为uip_ds6_notification;
这里研究uip_ds6_notification这个结构体类型

  struct uip_ds6_notification {struct uip_ds6_notification *next;uip_ds6_notification_callback callback;
};

同样,一个next指针,然后这里又一个callback,callback指向一个函数(指向下文定义的route_callback())

1 typedef void (* uip_ds6_notification_callback)(int event,
2                            uip_ipaddr_t *route,
3                            uip_ipaddr_t *nexthop,
4                            int num_routes);

这里回调函数有四个参数,对于该回调,深入一层代码

 1 static void2 call_route_callback(int event, uip_ipaddr_t *route,3             uip_ipaddr_t *nexthop)4 {5   int num;6   struct uip_ds6_notification *n;7   for(n = list_head(notificationlist);8       n != NULL;9       n = list_item_next(n)) {10     if(event == UIP_DS6_NOTIFICATION_DEFRT_ADD ||
11        event == UIP_DS6_NOTIFICATION_DEFRT_RM) {12       num = list_length(defaultrouterlist);
13     } else {14       num = num_routes;
15     }
16     n->callback(event, route, nexthop, num);
17   }
18 }

给出的注释 大致意思是当有路由加入或者退出时,上层可以通过注册回调函数,然后uip_ds6_route(uip_ds6_notification定义在uip_ds6_route.h内)就会通知上层发生了路由改变事件。这里还不是很理解回调函数,看了一个大佬对于回调的通俗解释——比如QQ聊天窗口的发送按钮,预先绑定某个函数OnSendClicked,你点击了发送按钮,函数就会被调用,这就是回调函数。

4、接下来看route_callback()

static void
route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *ipaddr,int numroutes)
{if(event == UIP_DS6_NOTIFICATION_DEFRT_ADD) {leds_off(LEDS_ALL);printf("Got a RPL route\n");}
}

这个函数功能很简单,只是这个节点连接上一个路由节点后,灯暗,并且打印调试信息;

不过,这个函数的参数与之前的结构体n的成员相同,为什么???;接着向下看;

5、receiver()

static void
receiver(struct udp_socket *c,void *ptr,const uip_ipaddr_t *sender_addr,uint16_t sender_port,const uip_ipaddr_t *receiver_addr,uint16_t receiver_port,const uint8_t *data,uint16_t datalen)
{printf("Data received on port %d from port %d with length %d, '%s'\n",receiver_port, sender_port, datalen, data);
}

这两个函数都很温柔,至少看起来功能明了,继续看下去;

PROCESS BEGIN()之后

6、uip_ds6_notification_add(&n, route_callback)

static struct uip_ds6_notification n;

route_callback,就是上面的函数;

这里看看uip_ds6_notification_add()

1 void
2 uip_ds6_notification_add(struct uip_ds6_notification *n,
3              uip_ds6_notification_callback c)
4 {5   if(n != NULL && c != NULL) {6     n->callback = c;
7     list_add(notificationlist, n);
8   }
9 }

来分析这个函数吧,如果结构体n存在并且c存在,这里

到了这里,就可以理解了 前面的结构体n

struct uip_ds6_notification {struct uip_ds6_notification *next;uip_ds6_notification_callback callback;
};

里面的callback,后面会被赋予一个执行一个动作的函数,这里也就能理解4后面的疑问了,因为route_callback会被赋予给n->callback;

接着分析 list_add(notificationlist, n)

这里重点是要弄清楚notification 的定义
查看nitificationlist的定义,有以下代码:

LIST(notificationlist);

既然都看到这里了,硬着头皮继续去看LIST()的定义吧

#define LIST(name) \static void *LIST_CONCAT(name,_list) = NULL; \static list_t name = (list_t)&LIST_CONCAT(name,_list)

晕(((φ(◎ロ◎;)φ)))

#define LIST_CONCAT2(s1, s2) s1##s2
#define LIST_CONCAT(s1, s2) LIST_CONCAT2(s1, s2)

这个s1##s2,实在不理解,没办法 有道了一下“concat”是合并数组的意思,这s1##s2,我实在没有找到注释,那么暂且理解为合并s1与s2.
继续晕
那么这里把LIST(notification)替换掉,应该是如下所示的代码了

static void *LIST_CONCAT(notificationlist,_list) = NULL;
static list_t notificationlist = (list_t)&LIST_CONCAT(notificationlist,_list);

这里的list_t是一个链表类型指针 ,声明如下:

typedef void ** list_t;

ok,还有不理解的地方,但是大体意思明白了,就是利用了一个链表,其实从函数名就比较能直观的了解函数功能。
ok,uip_ds6_notification_add(&n, route_callback)这个函数算是看明白了,当接入一个路由时,底层需要通知上层,有一个节点加入啦,同时,上层还要执行之前定义的route_callback()函数。

7、udp_socket_register(&s, NULL, receiver);

int
udp_socket_register(struct udp_socket *c,void *ptr,udp_socket_input_callback_t input_callback)
{init();if(c == NULL) {return -1;}c->ptr = ptr;c->input_callback = input_callback;c->p = PROCESS_CURRENT();PROCESS_CONTEXT_BEGIN(&udp_socket_process);c->udp_conn = udp_new(NULL, 0, c);PROCESS_CONTEXT_END();if(c->udp_conn == NULL) {return -1;}return 1;
}

当udp_socket或者udp_conn == NULL时,返回-1;
c-p = PROCESS_CURRENT();
这里看原文注释

/*** Get a pointer to the currently running process.** This macro get a pointer to the currently running* process. Typically, this macro is used to post an event to the* current process with process_post().** \hideinitializer*/#define PROCESS_CURRENT() process_current
CCIF extern struct process *process_current;

get a pointer to the currently running process.
前面已经提过,这里udp_socket中的p 指向的是进程信息,以保护进程;毕竟一个udp_socket来临,加入,进程可能打乱,有点进栈出栈以保护进程的意思哈!来看下一句

PROCESS_CONTEXT_BEGIN(&udp_socket_process);

/*** Switch context to another process** This function switch context to the specified process and executes* the code as if run by that process. Typical use of this function is* to switch context in services, called by other processes. Each* PROCESS_CONTEXT_BEGIN() must be followed by the* PROCESS_CONTEXT_END() macro to end the context switch.** Example:\codePROCESS_CONTEXT_BEGIN(&test_process);etimer_set(&timer, CLOCK_SECOND);PROCESS_CONTEXT_END(&test_process);\endcode** \param p    The process to use as context** \sa PROCESS_CONTEXT_END()* \sa PROCESS_CURRENT()*/#define PROCESS_CONTEXT_BEGIN(p) {\
struct process *tmp_current = PROCESS_CURRENT();\
process_current = p

这里注释就很明了了,进出栈以交换进程,不妨理解成中断,开始进行udp_socket_process;
下一句:

c->udp_conn = udp_new(NULL, 0, c);

这就比较好理解了,上面画有udp_conn的图解,这里是加入了一个udp_conn;

PROCESS_CONTEXT_END();

这个比较好理解了,就是进程切换回去,为了提高专业性,贴上代码

#define PROCESS_CONTEXT_END(p) process_current = tmp_current; }

不得不说,这里的编程风格真是我辈楷模,上面两个宏定义,实在厉害!
总结来看udp_socket_register(&s, NULL, receiver);
无非是进程中断,来执行udp_socket的注册,有进程切换操作,也有一个回调函数,上面的分析里面没有具体提及,因为在分析uip_ds6_notification_add(&n, route_callback)时,已经分析了,这里无非是,当接收到udp_socket时,执行这个receiver()函数;

8、udp_socket_bind(&s, PORT);

这个函数功能比较好理解,进行端口绑定

int
udp_socket_bind(struct udp_socket *c,uint16_t local_port)
{if(c == NULL || c->udp_conn == NULL) {return -1;}udp_bind(c->udp_conn, UIP_HTONS(local_port));return 1;
}

为了分析这个函数,这里需要查看udp_bind()和UIP_HTONS();
先看udp_bind()

udp_bind()

/*** Bind a UDP connection to a local port.** This function binds a UDP connection to a specified local port.** When a connection is created with udp_new(), it gets a local port* number assigned automatically. If the application needs to bind the* connection to a specified local port, this function should be used.** \note The port number must be provided in network byte order so a* conversion with UIP_HTONS() usually is necessary.** \param conn A pointer to the UDP connection that is to be bound.* \param port The port number in network byte order to which to bind* the connection.*/
#define udp_bind(conn, port) uip_udp_bind(conn, port)

。。。
其实功能好理解,但是还想继续到底层看
那就,看一看

/*** Bind a UDP connection to a local port.** \param conn A pointer to the uip_udp_conn structure for the* connection.** \param port The local port number, in network byte order.** \hideinitializer*/
#define uip_udp_bind(conn, port) (conn)->lport = port

可以,这个函数抽丝剥茧之后,就是很简单一句话。

UIP_HTONS();

/*** Convert 16-bit quantity from host byte order to network byte order.** This macro is primarily used for converting constants from host* byte order to network byte order. For converting variables to* network byte order, use the uip_htons() function instead.** \hideinitializer*/
#ifndef UIP_HTONS
#   if UIP_BYTE_ORDER == UIP_BIG_ENDIAN
#      define UIP_HTONS(n) (n)
#      define UIP_HTONL(n) (n)
#   else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */
#      define UIP_HTONS(n) (uint16_t)((((uint16_t) (n)) << 8) | (((uint16_t) (n)) >> 8))
#      define UIP_HTONL(n) (((uint32_t)UIP_HTONS(n) << 16) | UIP_HTONS((uint32_t)(n) >> 16))
#   endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */
#else
#error "UIP_HTONS already defined!"
#endif /* UIP_HTONS */

这个就比较好理解了,其实加个函数,就是为了port值满足一下uint16_t这个类型;

果然分析这么一大段,其实就一个东西,绑定一下端口。

9、udp_socket_connect(&s, NULL, PORT)

emmmm,先看代码

int
udp_socket_connect(struct udp_socket *c,uip_ipaddr_t *remote_addr,uint16_t remote_port)
{if(c == NULL || c->udp_conn == NULL) {return -1;}if(remote_addr != NULL) {uip_ipaddr_copy(&c->udp_conn->ripaddr, remote_addr);}c->udp_conn->rport = UIP_HTONS(remote_port);return 1;
}

这里居然没有一个回调函数,哈哈哈哈,应该只要看一个函数就够理解这个东西了,uip_ipaddr_copy()

uip_ipaddr_copy()

/*** Copy an IP address from one place to another.** Copies an IP address from one place to another.** Example:\codeuip_ipaddr_t ipaddr1, ipaddr2;uip_ipaddr(&ipaddr1, 192,16,1,2);uip_ipaddr_copy(&ipaddr2, &ipaddr1);\endcode** \param dest The destination for the copy.* \param src The source from where to copy.** \hideinitializer*/
#ifndef uip_ipaddr_copy
#define uip_ipaddr_copy(dest, src) (*(dest) = *(src))
#endif
#ifndef uip_ip4addr_copy
#define uip_ip4addr_copy(dest, src) (*((uip_ip4addr_t *)dest) = *((uip_ip4addr_t *)src))
#endif
#ifndef uip_ip6addr_copy
#define uip_ip6addr_copy(dest, src) (*((uip_ip6addr_t *)dest) = *((uip_ip6addr_t *)src))
#endif

这里其实是个宏定义,我就说这里怎么没个回调函数呢,果然adam的代码就是让人惊奇!
这里有个坑,这里remote_addr是NULL的,也就是说还不能连接上远程服务器,撑死叫做连接远程服务器端口;
不过这个函数功能的确是连接远程服务器的

通过从6~~~~~9这几个函数,udp已经半连接上了

10、uip_ipaddr(&ip4addr, 192,168,0,114)

/*** Construct an IP address from four bytes.** This function constructs an IP address of the type that uIP handles* internally from four bytes. The function is handy for specifying IP* addresses to use with e.g. the uip_connect() function.** Example:\codeuip_ipaddr_t ipaddr;struct uip_conn *c;uip_ipaddr(&ipaddr, 192,168,1,2);c = uip_connect(&ipaddr, UIP_HTONS(80));\endcode** \param addr A pointer to a uip_ipaddr_t variable that will be* filled in with the IP address.** \param addr0 The first octet of the IP address.* \param addr1 The second octet of the IP address.* \param addr2 The third octet of the IP address.* \param addr3 The forth octet of the IP address.** \hideinitializer*/
#define uip_ipaddr(addr, addr0,addr1,addr2,addr3) do {  \(addr)->u8[0] = addr0;                              \(addr)->u8[1] = addr1;                              \(addr)->u8[2] = addr2;                              \(addr)->u8[3] = addr3;                              \} while(0)
/** \brief 16 bit 802.15.4 address */
typedef struct uip_802154_shortaddr {uint8_t addr[2];
} uip_802154_shortaddr;
/** \brief 64 bit 802.15.4 address */
typedef struct uip_802154_longaddr {uint8_t addr[8];
} uip_802154_longaddr;/** \brief 802.11 address */
typedef struct uip_80211_addr {uint8_t addr[6];
} uip_80211_addr;/** \brief 802.3 address */
typedef struct uip_eth_addr {uint8_t addr[6];
} uip_eth_addr;

没什么好说 的 规范IP格式,包括下面的ip64_addr_4to6(&ip4addr, &ip6addr)

ip64_addr_4to6(&ip4addr, &ip6addr)

这里想看看addr转换方式

int
ip64_addr_4to6(const uip_ip4addr_t *ipv4addr,uip_ip6addr_t *ipv6addr)
{/* This function converts an IPv4 addresses into an IPv6addresses. It returns 0 if it failed to convert the address andnon-zero if it could successfully convert the address. *//* The IPv4 address is encoded as an IPv6-encoded IPv4 address inthe ::ffff:0000/24 prefix.*/ipv6addr->u8[0] = 0;ipv6addr->u8[1] = 0;ipv6addr->u8[2] = 0;ipv6addr->u8[3] = 0;ipv6addr->u8[4] = 0;ipv6addr->u8[5] = 0;ipv6addr->u8[6] = 0;ipv6addr->u8[7] = 0;ipv6addr->u8[8] = 0;ipv6addr->u8[9] = 0;ipv6addr->u8[10] = 0xff;ipv6addr->u8[11] = 0xff;ipv6addr->u8[12] = ipv4addr->u8[0];ipv6addr->u8[13] = ipv4addr->u8[1];ipv6addr->u8[14] = ipv4addr->u8[2];ipv6addr->u8[15] = ipv4addr->u8[3];printf("ip64_addr_4to6: IPv6-encoded IPv4 address %d.%d.%d.%d\n",ipv4addr->u8[0], ipv4addr->u8[1],ipv4addr->u8[2], ipv4addr->u8[3]);/* Conversion succeeded, we return non-zero. */return 1;
}

研究一下这里的ipv4转换为ipv6的规则
ipv4是32个字节;ipv6是128个字节
由于ipv4点分成了四段,因此每段8字节;
根据代码;

前十段都是0000h;地十一段是0ffffh;后面还剩四段,给ipv4地址来填入,这里数值如实填入,没有十进制与16进制的转换,不太明白,估计是把数值当符号了 只有指代作用,没有数值作用。

11、udp_socket_sendto(&s,&i, 1,&ip6addr, PORT)

int
udp_socket_sendto(struct udp_socket *c,const void *data, uint16_t datalen,const uip_ipaddr_t *to,uint16_t port)
{if(c == NULL || c->udp_conn == NULL) {return -1;}if(c->udp_conn != NULL) {uip_udp_packet_sendto(c->udp_conn, data, datalen,to, UIP_HTONS(port));return datalen;}return -1;
}

这里要看uip_udp_packet_sendto()

uip_udp_packet-sendto()

void
uip_udp_packet_sendto(struct uip_udp_conn *c, const void *data, int len,const uip_ipaddr_t *toaddr, uint16_t toport)
{uip_ipaddr_t curaddr;uint16_t curport;if(toaddr != NULL) {/* Save current IP addr/port. */uip_ipaddr_copy(&curaddr, &c->ripaddr);curport = c->rport;/* Load new IP addr/port */uip_ipaddr_copy(&c->ripaddr, toaddr);c->rport = toport;uip_udp_packet_send(c, data, len);/* Restore old IP addr/port */uip_ipaddr_copy(&c->ripaddr, &curaddr);c->rport = curport;}
}

这里需要去看其中的uip_udp_packet_send()

/*---------------------------------------------------------------------------*/
void
uip_udp_packet_send(struct uip_udp_conn *c, const void *data, int len)
{#if UIP_UDP     //UIP_UDP == 1if(data != NULL) {uip_udp_conn = c;uip_slen = len;memcpy(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data,len > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: len);uip_process(UIP_UDP_SEND_CONN);#if UIP_CONF_IPV6_MULTICAST       //UIP_CONF_IPV6_MUTICAST == 0/* Let the multicast engine process the datagram before we send it */if(uip_is_addr_mcast_routable(&uip_udp_conn->ripaddr)) {UIP_MCAST6.out();}
#endif /* UIP_IPV6_MULTICAST */#if NETSTACK_CONF_WITH_IPV6          //NETSTACK_CONF_WITH_IPV6 == 0tcpip_ipv6_output();
#elseif(uip_len > 0) {tcpip_output();}
#endif}uip_slen = 0;
#endif /* UIP_UDP */
}

这里面又有几个函数需要分析

 1、memcpy();

memcpy指的是C和C++使用的内存拷贝函数,函数原型为void *memcpy(void *destin, void *source, unsigned n);函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。

    memcpy(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data,len > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: len);

把数据拷贝到uip_buf区 即,数据缓冲区。
分别查看以下定义

/*** The uIP packet buffer.** The uip_aligned_buf array is used to hold incoming and outgoing* packets. The device driver should place incoming data into this* buffer. When sending data, the device driver should read the link* level headers and the TCP/IP headers from this buffer. The size of* the link level headers is configured by the UIP_LLH_LEN define.** \note The application data need not be placed in this buffer, so* the device driver must read it from the place pointed to by the* uip_appdata pointer as illustrated by the following example:\codevoiddevicedriver_send(void){hwsend(&uip_buf[0], UIP_LLH_LEN);if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) {hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN);} else {hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN);hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN);}}\endcode
*/typedef union {uint32_t u32[(UIP_BUFSIZE + 3) / 4];uint8_t u8[UIP_BUFSIZE];
} uip_buf_t;CCIF extern uip_buf_t uip_aligned_buf;/** Macro to access uip_aligned_buf as an array of bytes 宏以字节数组的形式访问uip_aligned_buf*/
#define uip_buf (uip_aligned_buf.u8)

2、uip_process();

uip_process(UIP_UDP_SEND_CONN);
下次解读 ,这个实在太多

uip-udp-demo分析---基于contiki相关推荐

  1. 一个udp用户数据报的数据字段为8192_基于FPGA的千兆网UDP通信分析

    千兆网UDP通信 以太网帧格式 图8‑12 以太网帧格式 表8‑5 以太网帧格式说明 UDP协议分析 为什么UDP协议在FPGA实现时很受欢迎,最主要一个原因就是简单,简答到什么地步呢?UDP协议只是 ...

  2. 基于zynq的千兆网udp项目_基于FPGA的千兆网UDP通信分析

    千兆网UDP通信 以太网帧格式 图8‑12 以太网帧格式 表8‑5 以太网帧格式说明 UDP协议分析 为什么UDP协议在FPGA实现时很受欢迎,最主要一个原因就是简单,简答到什么地步呢?UDP协议只是 ...

  3. Demo:基于 Flink SQL 构建流式应用

    摘要:上周四在 Flink 中文社区钉钉群中直播分享了<Demo:基于 Flink SQL 构建流式应用>,直播内容偏向实战演示.这篇文章是对直播内容的一个总结,并且改善了部分内容,比如除 ...

  4. 基于Contiki OS的智能led照明:LIFX

    最近接触了LED智能照明,但是大部分智能LED是使用私有协议或是类似Zigbee这样的协议进行组网和数据传输,这就造成除了每个灯节点外,还需要一个网关来连接节点和网络,包括飞利浦的HUE也是这样.Ph ...

  5. motan学习笔记 三 motan Demo 分析

    motan学习笔记 一 微博轻量级RPC框架Motan motan学习笔记 二 motan架构分析 motan学习笔记 三 motan Demo 分析 motan学习笔记 四 motan Demo 之 ...

  6. 温州大学计算机网络,数据通信与计算机网络(温州大学)实验四 - 运输层TCP和UDP协议分析.doc...

    温州大学 WENZHOU UNIVERSITY 实 验 报 告 学 期2016-2017学年第一学期课 程数据通信与计算机网络专 业电子信息科学与技术班 级14电科1学生姓名徐炜学 号14211111 ...

  7. qml demo分析(threadedanimation-线程动画)

    一.效果预览 使用过qml的同学都知道,使用qml做动画效果是非常简单的,再也不需要像QWidget那样,自己模拟一个动画,费时又费力,往往还达不到效果.今天我们就来分析下qml的两种动画实现方式,如 ...

  8. 红橙Darren视频笔记 ViewGroup事件分发分析 基于API27

    本节目标,通过案例,先看程序运行结果,然后跟踪源码,理解为什么会有这样的输出,继而理解view group的分发机制,感觉和证明题很像呢. 考虑以下程序的运行结果: case1: public cla ...

  9. 商品评论情感分析——基于商品评论建立的产品综合评价模型(1)

    商品评论情感分析--基于用户评论建立的产品综合评价模型(1) 1.背景 1.1问题分析 2.数据预处理 2.1删除无关数据 2.2文本去重 3.情感分析 4.LDA主题模型 4.1评论文本分词 4.2 ...

最新文章

  1. Evernote中国版、优秀的笔记软件
  2. BeanUtils组件
  3. Android Studio常用插件
  4. as与c++的反射机制对比
  5. Bash:把粘贴板上的内容拷贝的文件中。(脚本)
  6. 【转】解密Qt安装目录的结构
  7. mysql负责均衡读写分离_MySQL读写分离之负载均衡
  8. Unicode-objects must be encoded before hashing
  9. 阵列信号处理 窄带信号与包络
  10. 【转】强大的矩阵奇异值分解(SVD)及其应用
  11. Struts 2教程
  12. 【Oracle 管理员账号密码忘记的快速解决方法!十分细节!强烈建议收藏!!!】
  13. 10x 程序员工作法 - 划重点 | “自动化”主题的重点内容回顾汇总
  14. java计算年份_如何计算Java中2个日期之间的年份和年份
  15. 16种常用的数据分析方法-因子分析
  16. n个人排名,允许并列名次,共有多少种排名结果?
  17. 蓝牙最新版本6.0_连续看影视最新版本下载2021-连续看影视无广告不升级版v1.6.0 安卓版...
  18. 成功解决 git设置http代理 https代理 取消代理
  19. Ubuntu中安装Matlab2010
  20. mythtype加载出错

热门文章

  1. 人民币是升值还是贬值
  2. 18天精读掌握《费曼物理学讲义卷一》 第7天 2019.6.20
  3. CAPS发布了完全支持OpenACC的编译器了!
  4. 如何证明圆锥面积=1/3圆柱面积?
  5. CH423要如何使用,便宜的国产IO扩展芯片
  6. 广东海洋微型计算机控制技术试卷,历年真题:全国2017年4月自考02323操作系统概论考试试卷以及答案...
  7. oracle 导出数据 utl,oracle 使用 UTL_FILE 导出表数据到txt文件
  8. 我给鸿星尔克写了一个720°看鞋展厅
  9. 树莓派3b+快速编译opencv成功案例指导(保姆级教程)
  10. 谷歌cloud_参加Google Cloud专业机器学习工程师考试的20天Beta