首页 > 其他分享 >Netlink通信机制 与 Generic netlink 数据解析

Netlink通信机制 与 Generic netlink 数据解析

时间:2022-10-31 14:26:07浏览次数:57  
标签:netlink struct Generic Netlink len nla nlmsg nlh NLMSG

一、Netlink通信机制

Netlink是linux提供的用于内核和用户态进程之间的通信方式。
但是注意虽然Netlink主要用于用户空间和内核空间的通信,但是也能用于用户空间的两个进程通信。只是进程间通信有其他很多方式,一般不用Netlink。除非需要用到Netlink的广播特性时。

那么Netlink有什么优势呢?
一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,但是Netlink可以实现双工通信。
Netlink协议基于BSD socket和AF_NETLINK地址簇(address family),使用32位的端口号寻址(以前称作PID),每个Netlink协议(或称作总线,man手册中则称之为netlink family),通常与一个或一组内核服务/组件相关联,如NETLINK_ROUTE用于获取和设置路由与链路信息、NETLINK_KOBJECT_UEVENT用于内核向用户空间的udev进程发送通知等。
netlink具有以下特点:
① 支持全双工、异步通信(当然同步也支持)
② 用户空间可使用标准的BSD socket接口(但netlink并没有屏蔽掉协议包的构造与解析过程,推荐使用libnl等第三方库)
③ 在内核空间使用专用的内核API接口
④ 支持多播(因此支持“总线”式通信,可实现消息订阅)
⑤ 在内核端可用于进程上下文与中断上下文

 

二、用户态数据结构

 1 struct msghdr 结构体
 2  
 3 struct iovec {                    /* Scatter/gather arrayitems */
 4       void *iov_base;              /*Starting address */
 5       size_t iov_len;               /* Number of bytes to transfer*/
 6   };
 7    /* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,
 8      以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度(即有多少个buff)
 9    */
10   struct msghdr {
11       void         *msg_name;       /* optional address */
12       socklen_t     msg_namelen;    /* size of address */
13       struct iovec *msg_iov;        /* scatter/gather array */
14       size_t        msg_iovlen;     /* # elements in msg_iov */
15       void         *msg_control;    /* ancillary data, see below */
16       size_t        msg_controllen; /* ancillary databuffer len */
17       int           msg_flags;      /* flags on received message */
18   };
19   /* msg_name:数据的目的地址,网络包指向sockaddr_in, netlink则指向sockaddr_nl;
20      msg_namelen: msg_name 所代表的地址长度
21      msg_iov: 指向的是缓冲区数组
22      msg_iovlen: 缓冲区数组长度
23      msg_control: 辅助数据,控制信息(发送任何的控制信息)
24      msg_controllen: 辅助信息长度
25      msg_flags: 消息标识
26   */

 

首先看一下几个重要的数据结构的关系:

 

 

 

1、struct msghdr 结构体

msghdr这个结构在socket变成中就会用到,并不算Netlink专有的,这里不在过多说明。只说明一下如何更好理解这个结构的功能。我们

知道socket消息的发送和接收函数一般有这几对:recv/send、readv/writev、recvfrom/sendto。当然还有recvmsg/sendmsg,

前面三对函数各有各的特点功能,而recvmsg/sendmsg就是要囊括前面三对的所有功能,当然还有自己特殊的用途。msghdr的前两个

成员就是为了满足recvfrom/sendto的功能,中间两个成员msg_iov和msg_iovlen则是为了满足readv/writev的功能,而最后的

msg_flags则是为了满足recv/send中flag的功能,剩下的msg_control和msg_controllen则是满足recvmsg/sendmsg特有的功能。

 

2、struct sockaddr_nl

struct sockaddr_nl为Netlink的地址,和我们通常socket编程中的sockaddr_in作用一样,他们的结构对比如下:

 

 

 struct sockaddr_nl的详细定义和描述如下:

1 struct sockaddr_nl
2 {
3     sa_family_t nl_family; /*该字段总是为AF_NETLINK */
4     unsigned short nl_pad; /* 目前未用到,填充为0*/
5     __u32 nl_pid; /* process pid */
6     __u32 nl_groups; /* multicast groups mask */
7 };

(1) nl_pid:在Netlink规范里,PID全称是Port-ID(32bits),其主要作用是用于唯一的标识一个基于netlink的socket通道。通常

  情况下nl_pid都设置为当前进程的进程号。前面我们也说过,Netlink不仅可以实现用户-内核空间的通信还可使现实用户空间两

  个进程之间,或内核空间两个进程之间的通信。该属性为0时一般指内核。

(2) nl_groups:如果用户空间的进程希望加入某个多播组,则必须执行bind()系统调用。该字段指明了调用者希望加入的多播组

  号的掩码(注意不是组号,后面我们会详细讲解这个字段)。如果该字段为0则表示调用者不希望加入任何多播组。对于每个隶属于

  Netlink协议域的协议,最多可支持32个多播组(因为nl_groups的长度为32比特),每个多播组用一个比特来表示。

 

3、struct nlmsghdr

 

 

 

Netlink的报文由消息头和消息体构成,struct nlmsghdr即为消息头。消息头定义在文件里,由结构体nlmsghdr表示:

1 struct nlmsghdr {
2     __u32        nlmsg_len;    /* Length of message including header */
3     __u16        nlmsg_type;    /* Message content */
4     __u16        nlmsg_flags;    /* Additional flags */
5     __u32        nlmsg_seq;    /* Sequence number */
6     __u32        nlmsg_pid;    /* Sending process port ID */
7 };

消息头中各成员属性的解释及说明:

(1) nlmsg_len:整个消息的长度,按字节计算。包括了Netlink消息头本身。

(2) nlmsg_type:消息的类型,即是数据还是控制消息。目前(内核版本2.6.21)Netlink仅支持四种类型的控制消息,如下:

1 #define NLMSG_NOOP        0x1    /* Nothing.        */
2 #define NLMSG_ERROR        0x2    /* Error        */
3 #define NLMSG_DONE        0x3    /* End of a dump    */
4 #define NLMSG_OVERRUN        0x4    /* Data lost        */
5 
6 #define NLMSG_MIN_TYPE        0x10    /* < 0x10: reserved control messages */

  NLMSG_NOOP  空消息,什么也不做;

  NLMSG_ERROR  指明该消息中包含一个错误;

  NLMSG_DONE  如果内核通过Netlink队列返回了多个消息,那么队列的最后一条消息的类型为NLMSG_DONE,其余所有消息的nlmsg_flags属性都被设置NLM_F_MULTI位有·效。

  NLMSG_OVERRUN  暂时没用到。
(3) nlmsg_flags:附加在消息上的额外说明信息,如上面提到的NLM_F_MULTI。

 

4、struct genlmsghdr

1 struct genlmsghdr {
2     __u8    cmd;
3     __u8    version;
4     __u16    reserved;
5 };

(1)、cmd表示消息命令,对于用户自己定义的每个子协议类型都需要定义特定的消息命令集,这里该字段表示当前消息的消息命令;

(2)、version字段表示版本控制,可以在在不破坏向后兼容性的情况下修改消息的格式,可以不使用该字段;

(3)、reserved字段保留。

5、struct nlattr

 1 /*
 2  *  <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
 3  * +---------------------+- - -+- - - - - - - - - -+- - -+
 4  * |        Header       | Pad |     Payload       | Pad |
 5  * |   (struct nlattr)   | ing |                   | ing |
 6  * +---------------------+- - -+- - - - - - - - - -+- - -+
 7  *  <-------------- nlattr->nla_len -------------->
 8  */
 9 
10 struct nlattr {
11     __u16           nla_len;
12     __u16           nla_type;
13 };
netlink的消息头后面跟着的是消息的有效载荷部分,它采用的是格式为“类型——长度——值”,简写TLV。
其中类型和长度使用属性头nlattr来表示。其中nla_len表示属性长度;
nla_type表示属性类型。

 

二、数据解析

 1  //四字节
 2  #define NLMSG_ALIGNTO    4U
 3  
 4  //用于得到不小于len且字节对齐的最小数值
 5  #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
 6  
 7  // Netlink 头部长度
 8  #define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
 9  
10  //计算消息数据len的真实消息长度(消息体 + 消息头)
11  #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
12  
13  //返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值
14  #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
15  
16  //取得struct nlmsghdr结构体消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏
17  //获取struct genlmsghdr 结构体位置
18  #define NLMSG_DATA(nlh)  ((void *)(((char *)nlh) + NLMSG_HDRLEN))
19  
20  //用于得到下一个消息的首地址, 同时len 变为剩余消息的长度
21  #define NLMSG_NEXT(nlh,len)     ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
22                    (struct nlmsghdr *)(((char *)(nlh)) + \
23                    NLMSG_ALIGN((nlh)->nlmsg_len)))
24  
25  //判断消息是否 >len
26  #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
27                 (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
28                 (nlh)->nlmsg_len <= (len))
29  
30  //用于返回payload的长度
31  #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
32  
33  
34  
35  //用于取得struct genlmsghdr结构体后面消息的数据部分的首地址
36  //获取struct nlattr 结构体位置
37  #define GENLMSG_DATA(glh)    ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
38  
39  //
40  #define GENLMSG_PAYLOAD(glh)    (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
41  
42  
43  
44  //用于取得struct nlattr结构体后面消息的数据部分的首地址
45  #define NLA_DATA(na)        ((void *)((char*)(na) + NLA_HDRLEN))
46  
47  #define NLA_PAYLOAD(len)    (len - NLA_HDRLEN)
48  
49  #define NLA_ALIGNTO        4U
50  
51  #define NLA_ALIGN(len)        (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
52  
53  #define NLA_HDRLEN        ((int) NLA_ALIGN(sizeof(struct nlattr)))
54  
55  
56  #define NLA_NEXT(nla,len)       ((len) -= NLA_ALIGN((nla)->nla_len), \
57                                        (struct nlattr*)(((char*)(nla)) + NLA_ALIGN((nla)->nla_len)))
58  
59  #define NLA_OK(nla,len)         ((len) >= (int)sizeof(struct nlattr) && \
60                                      (nla)->nla_len >= sizeof(struct nlattr) && \
61                                      (nla)->nla_len <= (len))

 

 

1、数据结构

 

 

 

 1 /* ========================================================================
 2  *         Netlink Messages and Attributes Interface (As Seen On TV)
 3  * ------------------------------------------------------------------------
 4  *                          Messages Interface
 5  * ------------------------------------------------------------------------
 6  *
 7  * Message Format:
 8  *    <--- nlmsg_total_size(payload)  --->
 9  *    <-- nlmsg_msg_size(payload) ->
10  *   +----------+- - -+-------------+- - -+-------- - -
11  *   | nlmsghdr | Pad |   Payload   | Pad | nlmsghdr
12  *   +----------+- - -+-------------+- - -+-------- - -
13  *   nlmsg_data(nlh)---^                   ^
14  *   nlmsg_next(nlh)-----------------------+
15  *
16  * Payload Format:
17  *    <---------------------- nlmsg_len(nlh) --------------------->
18  *    <------ hdrlen ------>       <- nlmsg_attrlen(nlh, hdrlen) ->
19  *   +----------------------+- - -+--------------------------------+
20  *   |     Family Header    | Pad |           Attributes           |
21  *   +----------------------+- - -+--------------------------------+
22  *   nlmsg_attrdata(nlh, hdrlen)---^
23  *
24  * Data Structures:
25  *   struct nlmsghdr            netlink message header
26  */
 1 /*
 2  *  <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
 3  * +---------------------+- - -+- - - - - - - - - -+- - -+
 4  * |        Header       | Pad |     Payload       | Pad |
 5  * |   (struct nlattr)   | ing |                   | ing |
 6  * +---------------------+- - -+- - - - - - - - - -+- - -+
 7  *  <-------------- nlattr->nla_len -------------->
 8  */
 9 
10 struct nlattr {
11     __u16           nla_len;
12     __u16           nla_type;
13 };
14 
15 /*
16  * nla_type (16 bits)
17  * +---+---+-------------------------------+
18  * | N | O | Attribute Type                |
19  * +---+---+-------------------------------+
20  * N := Carries nested attributes
21  * O := Payload stored in network byte order
22  *
23  * Note: The N and O flag are mutually exclusive.
24  */

 

(1)、nlmsg_msg_size(payload)

1 /**
2  * nlmsg_msg_size :不包括填充的网络链接消息长度
3  * @payload: 消息有效负载长度
4  */
5 static inline int nlmsg_msg_size(int payload)
6 {
7     return NLMSG_HDRLEN + payload;
8 }

(2)、nlmsg_total_size(payload)

1 /**
2  * nlmsg_total_size : 包括填充的网络链路消息长度
3  * @payload:消息有效负载长度
4  */
5 static inline int nlmsg_total_size(int payload)
6 {
7     return NLMSG_ALIGN(nlmsg_msg_size(payload));
8 }

(3)、nlmsg_data(nlh)

 1 /**
 2  * nlmsg_data : 报文有效载荷头
 3  * @nlh: netlink message header
 4  */
 5 static inline void *nlmsg_data(const struct nlmsghdr *nlh)
 6 {
 7     return (unsigned char *) nlh + NLMSG_HDRLEN;
 8 }
 9 
10 //取得struct nlmsghdr结构体消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏
11 #define NLMSG_DATA(nlh)  ((void *)(((char *)nlh) + NLMSG_HDRLEN))

(4)、nlmsg_next(nlh)

/**
 * nlmsg_next - next netlink message in message stream
 * @nlh: netlink message header
 * @remaining: 在消息流中剩余的字节数
 *
 * Returns the next netlink message in the message stream and
 * decrements remaining by the size of the current message.
 */
static inline struct nlmsghdr *nlmsg_next(const struct nlmsghdr *nlh, int *remaining)
{
    int totlen = NLMSG_ALIGN(nlh->nlmsg_len);

    *remaining -= totlen;

    return (struct nlmsghdr *) ((unsigned char *) nlh + totlen);
}


//用于得到下一个消息的首地址, 同时len 变为剩余消息的长度
#define NLMSG_NEXT(nlh,len)     ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
                  (struct nlmsghdr *)(((char *)(nlh)) + \
                  NLMSG_ALIGN((nlh)->nlmsg_len)))

(5)、nlmsg_len(nlh)

1 /**
2  * nlmsg_len - 消息有效负载长度
3  * @nlh: netlink message header
4  */
5 static inline int nlmsg_len(const struct nlmsghdr *nlh)
6 {
7     return nlh->nlmsg_len - NLMSG_HDRLEN;
8 }

(6)、nlmsg_attrdata(nlh, hdrlen)

 1 /**
 2  * nlmsg_attrdata - head of attributes data
 3  * @nlh: netlink message header
 4  * @hdrlen: length of family specific header
 5  */
 6 static inline struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen)
 7 {
 8     unsigned char *data = nlmsg_data(nlh);
 9     return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen));
10 }
11 
12 //用于取得struct genlmsghdr结构体后面消息的数据部分的首地址
13 #define GENLMSG_DATA(glh)    ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))

(7)、nlmsg_attrlen(nlh, hdrlen)

 1 /**
 2  * nlmsg_attrlen - length of attributes data
 3  * @nlh: netlink message header
 4  * @hdrlen: length of family specific header
 5  */
 6 static inline int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
 7 {
 8     return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen);
 9 }
10 
11 #define GENLMSG_PAYLOAD(glh)    (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)

(8)、其他

 1 /**
 2  * nlmsg_ok - check if the netlink message fits into the remaining bytes
 3  * @nlh: netlink message header
 4  * @remaining: number of bytes remaining in message stream
 5  */
 6 static inline int nlmsg_ok(const struct nlmsghdr *nlh, int remaining)
 7 {
 8     return (remaining >= (int) sizeof(struct nlmsghdr) &&
 9         nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
10         nlh->nlmsg_len <= remaining);
11 }
12 
13 /**
14  * nlmsg_next - next netlink message in message stream
15  * @nlh: netlink message header
16  * @remaining: number of bytes remaining in message stream
17  *
18  * Returns the next netlink message in the message stream and
19  * decrements remaining by the size of the current message.
20  */
21 static inline struct nlmsghdr *
22 nlmsg_next(const struct nlmsghdr *nlh, int *remaining)
23 {
24     int totlen = NLMSG_ALIGN(nlh->nlmsg_len);
25 
26     *remaining -= totlen;
27 
28     return (struct nlmsghdr *) ((unsigned char *) nlh + totlen);
29 }

 2、数据解析

 

 

 数据分配

 1 void *gxy_genlmsg_alloc(int *size)
 2 {
 3     unsigned char *buf;
 4     int len;
 5     /*
 6      * attribute len
 7      * attr len = (nla_hdr + pad) + (payload(user data) + pad)
 8      */
 9     len = NLA_ALIGN( NLA_HDRLEN + *size);
10     /*
11      * family msg len,
12      * but actually we have NOT custom family header
13      * family msg len = family_hdr + payload(attribute)
14      */
15     len += 0;
16     /*
17      * generic netlink msg len
18      * genlmsg len = (genlhdr + pad) + payload(family msg)
19      */
20     len += GENL_HDRLEN;
21     /*
22      * netlink msg len
23      * nlmsg len = (nlmsghdr + pad) + (payload(genlmsg) + pad)
24      */
25     len = GXY_NLMSG_SPACE(len);
26     
27     buf = malloc(len);
28     if (!buf)
29         return NULL;
30  
31     memset(buf, 0, len);
32     *size = len;
33  
34     return buf;
35 }

 

接收数据函数:

 1 /**
 2  *
 3  * @param sockfd    generic netlink socket fd
 4  * @param buf       the 'buf' is including the struct nlmsghdr,
 5  *                  struct genlmsghdr and struct nlattr
 6  * @param len       size of 'buf'
 7  * @return  >0      size of genlmsg
 8  *          <0      error occur
 9  */
10 int gxy_genlmsg_recv(int sockfd, unsigned char *buf, unsigned int len)
11 {
12     struct sockaddr_nl nladdr;
13     struct msghdr msg;
14     struct iovec iov;
15  
16     int ret;
17  
18     nladdr.nl_family = AF_NETLINK;
19     nladdr.nl_pid = getpid();
20     nladdr.nl_groups = 0;
21  
22     iov.iov_base = buf;
23     iov.iov_len = len;
24  
25     msg.msg_name = (void *)&nladdr;
26     msg.msg_namelen = sizeof(nladdr);
27     
28     msg.msg_iov = &iov;
29     msg.msg_iovlen = 1;
30     
31     ret = recvmsg(sockfd, &msg, 0);
32     ret = ret > 0 ? ret : -1;
33     
34     return ret;
35 
36 }

由gxy_genlmsg_recv()接收到数据为buf,长度为ret

struct nlmsghdr *nlh  = NULL;
struct genlmsghdr *glh = NULL;
struct nlattr *nla = NULL;

nlh  = (struct nlmsghdr *)buf;

glh  = NLMSG_DATA(nlh);

nla  = GENLMSG_DATA(nlh);

实际数据 char *temp_buf

memcpy(temp_buf,NLA_DATA(nla), nla->nla_type);

 

发送数据

 1 int gxy_genlmsg_send(int sockfd, unsigned short nlmsg_type, unsigned int nlmsg_pid,
 2         unsigned char genl_cmd, unsigned char genl_version,
 3         unsigned short nla_type, const void *nla_data, unsigned int nla_len)
 4 {
 5     struct nlmsghdr *nlh = NULL;    //netlink message header
 6     struct genlmsghdr *glh = NULL;  //generic netlink message header
 7     struct nlattr *nla = NULL;      //netlink attribute header
 8  
 9     struct sockaddr_nl nladdr;
10     unsigned char *buf = NULL;
11     int len = 0,count = 0,ret = -1;
12 
13     if ((nlmsg_type == 0) || (!nla_data) || (nla_len <= 0)){
14         return -1;
15     }
16  
17     len = nla_len;
18     
19     buf = gxy_genlmsg_alloc(&len);
20     if (!buf){
21         return -1;
22     }
23     nlh = (struct nlmsghdr *)buf;
24     nlh->nlmsg_len = len;
25     nlh->nlmsg_type = nlmsg_type;
26     nlh->nlmsg_flags = NLM_F_REQUEST;
27     nlh->nlmsg_seq = 0;
28     nlh->nlmsg_pid = nlmsg_pid;
29  
30     glh = (struct genlmsghdr *)NLMSG_DATA(nlh);
31     glh->cmd = genl_cmd;
32     glh->version = genl_version;
33  
34  
35     nla = (struct nlattr *)GENLMSG_DATA(nlh);//
36     nla->nla_type = nla_type;
37     nla->nla_len = gxy_nla_attr_size(nla_len);
38     memcpy(NLA_DATA(nla), nla_data, nla_len);
39  
40     memset(&nladdr, 0, sizeof(nladdr));
41     nladdr.nl_family = AF_NETLINK;
42 
43     do {
44  
45         ret = sendto(sockfd, &buf[count], len - count, 0,(struct sockaddr *)&nladdr, sizeof(nladdr));
46         if (ret < 0){
47             if (errno != EAGAIN){
48                 count = -1;
49                 goto out;
50             }
51         } else {
52             count += ret;
53         }
54  
55     }while (count < len);
56  
57 out:
58     free(buf);
59     buf = NULL;
60     return count;
61 }

例如要发送的数据为

char *msg_buf = "Hello world";

则:gxy_genlmsg_send(sockfd, nlmsg_type, nlmsg_pid,genl_cmd, genl_version,nla_type, msg_buf, strlen(msg_buf) + 1);

 

标签:netlink,struct,Generic,Netlink,len,nla,nlmsg,nlh,NLMSG
From: https://www.cnblogs.com/ink-white/p/16841824.html

相关文章

  • Netlink 与 struct genl_family 结构体
    一、struct 1//#include<net/genetlink.h>2#ifndef__NET_GENERIC_NETLINK_H3#define__NET_GENERIC_NETLINK_H45#include<linux/genetlink.h>......
  • 基于Mixin扩展类和GenericAPIView实现接口
    基于Mixin扩展类和GenericAPIView实现接口10.1扩展类简介(1)ListModelMixin列表视图扩展类,提供list(request,*args,**kwargs)方法快速实现列表视图,返回200状态码。该......
  • 线程安全的Generic Dictionary
     ​​System.Collections.Generic.Dictionary<,>​​只要不修改该集合,Dictionary就可以同时支持多个阅读器。即便如此,从头到尾对一个集合进行枚举本质上并不是一个线程......
  • XML Serializable Generic Dictionary
       .net2.0泛型Dictionary不支持XMLserializable. 下面是一个实现IXmlSerializable接口实现支持Serialize的泛型集合.Dictionary 。Dictionary<TKey,TValue>......
  • GenericAPIView
    https://www.bilibili.com/video/BV1z5411D7BQ?p=14&vd_source=caabcbd2a759a67e2a3de8acbaaf08ea views.pyfromsers.modelsimportBookfromrest_frameworkimpor......
  • java---泛型(Generics)
    泛型是JDK1.5以后增加的,它可以帮助我们建立类型安全的集合。什么是泛型泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参数传入,可以把“泛......
  • [Typescript] Tips: Assign local variables to default generic slots to dry up you
    YoucanDRYupyourgenericscodeMASSIVELY(andimproveperf)byassigninglocalvariablestodefaultgenericslots.Here,wemovesomecomplex'Extract'logi......
  • GenericServlet改造,ServletConfig接口
    GenericServlet(java自己编写好了)我们编写一个Servlet类直接实现Servlet接口有什么缺点?我们只需要service方法,其他方法大部分情况下是不需要使用的。代码很丑陋。适......
  • [Typescript + React] Tip: Use generics in React to make dynamic and flexible com
    YoucanusegenericsinReacttomakeincrediblydynamic,flexiblecomponents.Here,ImakeaTablecomponentwithageneric'items'type.interfaceTableProp......
  • 004-Redis 的 Generic 命令组
    1.Generic1.1copy1.1.1基本信息COPYsourcedestination[DBdestination-db][REPLACE]summary:Copyakeysince:6.2.0Thiscommandcopiesthevaluestored......