uip-udp-demo分析—基于contiki

uip-udp-demo分析—基于contikidemo definePORT12 通信端口设置 udp 数据结构 staticstruct sockets uip 的 ip 地址 staticuip ipaddr taddr staticstruct

大家好,欢迎来到IT知识分享网。

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 time within 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;

 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 void 2 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内)就会通知上层发生了路由改变事件。这里还不是很理解回调函数,看了一个大佬对于回调的通俗解释——比如聊天窗口的发送按钮,预先绑定某个函数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)

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) s1s2 #define LIST_CONCAT(s1, s2) LIST_CONCAT2(s1, s2) 

这个s1s2,实在不理解,没办法 有道了一下“concat”是合并数组的意思,这s1s2,我实在没有找到注释,那么暂且理解为合并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; 

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; 

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: \code PROCESS_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 

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()

/ * 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: \code uip_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: \code uip_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__shortaddr { 
    uint8_t addr[2]; } uip__shortaddr; / \brief 64 bit 802.15.4 address */ typedef struct uip__longaddr { 
    uint8_t addr[8]; } uip__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 IPv6 addresses. It returns 0 if it failed to convert the address and non-zero if it could successfully convert the address. */ /* The IPv4 address is encoded as an IPv6-encoded IPv4 address in the ::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 == 1 if(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 == 0 tcpip_ipv6_output(); #else if(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: \code void devicedriver_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();

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/152273.html

(0)
上一篇 2025-03-09 15:00
下一篇 2025-03-09 15:05

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信