一、网络超时检测

在网络通信过程中,经常会出现不可预知的各种情况。例如网络线路突发故障、通信一方异常结束等。一旦出现上述情况,很可能长时间都不会收到数据,而且无法判断是没有数据还是数据无法到达。如果使用的是TCP协议,可以检测出来;但如果使用UDP协议的话,需要在程序中进行相关检测。所以,为避免进程在没有数据时无限制的阻塞,使用网络超时检测很有必要。


1、套接字接收超时检测

这里先介绍设置套接字选项的函数 setsockopt() 函数:

所需头文件 #include <sys/types.h>
#include <sys/socket.h>
函数原型 int setsockopt (int sockfd, int level, int optname, 
                              const void *optval, socklen_t optlen );
函数参数 sockfd:套接字描述符
level:选项所属协议层
optval:保存选项值的缓冲区
optlen:选项值的长度
函数返回值 成功:0
出错:-1,并设置 errno

下面是套接字常用选项及其说明:

LEVEL:SOL_SOCKET

选项名称 说明 数据类型
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTRUOTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int

下面利用SO_RCVTIMEO的选项实现套接字的接收超时检测:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #define N 64
  10. #define PORT 8888
  11. int main()
  12. {
  13. int sockfd;
  14. char buf[N];
  15. struct sockaddr_in seraddr;
  16. struct timeval t = {6, 0};
  17. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  18. {
  19. perror("socket error");
  20. exit(-1);
  21. }
  22. else
  23. {
  24. printf("socket successfully!\n");
  25. printf("sockfd:%d\n",sockfd);
  26. }
  27. memset(&seraddr, 0, sizeof(seraddr));
  28. seraddr.sin_family = AF_INET;
  29. seraddr.sin_port = htons(PORT);
  30. seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
  31. if(bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
  32. {
  33. perror("bind error");
  34. exit(-1);
  35. }
  36. else
  37. {
  38. printf("bind successfully!\n");
  39. printf("PORT:%d\n",PORT);
  40. }
  41. if(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) < 0)
  42. {
  43. perror("setsockopt error");
  44. exit(-1);
  45. }
  46. if(recvfrom(sockfd, buf, N, 0, NULL, NULL) < 0)
  47. {
  48. perror("fail to recvfrom");
  49. exit(-1);
  50. }
  51. else
  52. {
  53. printf("recv data: %s\n",buf);
  54. }
  55. return 0;
  56. }

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/socket/time$ ./setsockopt
  2. socket successfully!
  3. sockfd:3
  4. bind successfully!
  5. PORT:8888
  6. fail to recvfrom: Resource temporarily unavailable
  7. fs@ubuntu:~/qiang/socket/time$

可以看到,6s之内没有数据包到来,程序会从 recvfrom 函数返回,进行相应的错误处理。

注意:套接字一旦设置了超时之后,每一次发送或接收时都会检测,如果要取消超时检测,重新用setsockopt函数设置即可(把时间值指定为 0)。

2、定时器超时检测

这里利用定时器信号SIGALARM,可以在程序中创建一个闹钟。当到达目标时间后,指定的信号处理函数被执行。这样同样可以利用SIGALARM信号实现检测,下面分别介绍相关数据类型和函数。

struct sigaction 是 Linux 中用来描述信号行为的结构体类型,其定义如下:

[cpp] view plaincopy
  1. struct sigaction
  2. {
  3. void (*sa_handler) (int);
  4. void (*sa_sigaction)(int, siginfo_t *, void *);
  5. sigset_t sa_mask;
  6. int sa_flags;
  7. void (*sa_restorer) (void);
  8. }

①  sa_handler:此参数和signal()的参数handler相同,此参数主要用来对信号旧的安装函数signal()处理形式的支持;
②  sa_sigaction:新的信号安装机制,处理函数被调用的时候,不但可以得到信号编号,而且可以获悉被调用的原因以及产生问题的上下文的相关信息。
③  sa_mask:用来设置在处理该信号时暂时将sa_mask指定的信号搁置;
④  sa_restorer: 此参数没有使用;
⑤  sa_flags:用来设置信号处理的其他相关操作,下列的数值可用。可用OR 运算(|)组合:
                      ŸA_NOCLDSTOP:如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程
                      SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前,将此信号处理方式改为系统预设的方式
                      SA_RESTART:被信号中断的系统调用会自行重启
                      SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来
                      SA_SIGINFO:信号处理函数是带有三个参数的sa_sigaction。

所需头文件 #include <signal.h>
函数原型 int sigaction(int signum,  const struct sigaction *act ,
                                                       struct sigaction *oldact );
函数传入值 signum:可以指定SIGKILL和SIGSTOP以外的所有信号
act        :act 是一个结构体,里面包含信号处理函数的地址、
                  处理方式等信息;
oldact  :参数oldact 是一个传出参数,sigaction 函数调用成功后,
                  oldact 里面包含以前对 signum 信号的处理方式的信息;
函数返回值 成功:0
出错:-1

使用定时器信号检测超时的示例代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <signal.h>
  6. #include <sys/types.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #define N 64
  11. #define PORT 8888
  12. void handler(int signo)
  13. {
  14. printf("interrupted by SIGALRM\n");
  15. }
  16. int main()
  17. {
  18. int sockfd;
  19. char buf[N];
  20. struct sockaddr_in seraddr;
  21. struct sigaction act;
  22. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  23. {
  24. perror("socket error");
  25. exit(-1);
  26. }
  27. else
  28. {
  29. printf("socket successfully!\n");
  30. printf("sockfd:%d\n",sockfd);
  31. }
  32. memset(&seraddr, 0, sizeof(seraddr));
  33. seraddr.sin_family = AF_INET;
  34. seraddr.sin_port = htons(PORT);
  35. seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
  36. if(bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
  37. {
  38. perror("bind error");
  39. exit(-1);
  40. }
  41. else
  42. {
  43. printf("bind successfully!\n");
  44. printf("PORT:%d\n",PORT);
  45. }
  46. sigaction(SIGALRM, NULL, &act);
  47. act.sa_handler = handler;
  48. act.sa_flags &= ~SA_RESTART;
  49. sigaction(SIGALRM, &act, NULL);
  50. alarm(6);
  51. if(recvfrom(sockfd, buf, N, 0, NULL, NULL) < 0)
  52. {
  53. perror("fail to recvfrom");
  54. exit(-1);
  55. }
  56. printf("recv data: %s\n",buf);
  57. alarm(0);
  58. return 0;
  59. }

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/socket/time$ ./alarm
  2. socket successfully!
  3. sockfd:3
  4. bind successfully!
  5. PORT:8888
  6. interrupted by SIGALRM
  7. fail to recvfrom: Interrupted system call
  8. fs@ubuntu:~/qiang/socket/time$

二、广播

前面的网络通信中,采用的都是单播(唯一的发送方和接收方)的方式。很多时候,需要把数据同时发送给局域网中的所有主机。例如,通过广播ARP包获取目标主机的MAC地址。

1、广播地址

IP地址用来标识网络中的一台主机。IPv4 协议用一个 32 位的无符号数表示网络地址,包括网络号和主机号。子网掩码表示 IP 地址中网络和占几个字节。对于一个 C类地址来说,子网掩码为 255.255.255.0。

每个网段都有其对应的广播地址。以 C 类地址网段 192.168.1.x为例,其中最小的地址 192.168.1.0 代表该网段;而最大的地址192.168.1.255 则是该网段中的广播地址。当我们向这个地址发送数据包时,该网段中所以的主机都会接收并处理。

注意:发送广播包时,目标IP 为广播地址而目标 MAC 是 ff:ff:ff:ff:ff。

2、广播包的发送和接收

广播包的发送和接收通过UDP套接字实现。

1)广播包发送流程如下:

创建udp 套接字

指定目标地址和端口

设置套接字选项允许发送广播包

发送数据包

发送广播包的示例如下:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #define N 64
  10. #define PORT 8888
  11. int main()
  12. {
  13. int sockfd;
  14. int on = 1;
  15. char buf[N] = "This is a broadcast package!";
  16. struct sockaddr_in dstaddr;
  17. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  18. {
  19. perror("socket error");
  20. exit(-1);
  21. }
  22. else
  23. {
  24. printf("socket successfully!\n");
  25. printf("sockfd:%d\n",sockfd);
  26. }
  27. memset(&dstaddr, 0, sizeof(dstaddr));
  28. dstaddr.sin_family = AF_INET;
  29. dstaddr.sin_port = htons(PORT);
  30. dstaddr.sin_addr.s_addr = inet_addr("192.168.1.255"); // 192.168.1.x 网段的广播地址
  31. if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) //套接字默认不允许发送广播包,通过修改 SO_BROADCAST 选项使能
  32. {
  33. perror("setsockopt error");
  34. exit(-1);
  35. }
  36. while(1)
  37. {
  38. sendto(sockfd, buf, N, 0,(struct sockaddr *)&dstaddr, sizeof(dstaddr));
  39. sleep(1);
  40. }
  41. return 0;
  42. }

2)、广播包接收流程

广播包接收流程如下:

创建UDP套接字

绑定地址

接收数据包

接收包示例如下:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #define N 64
  10. #define PORT 8888
  11. int main()
  12. {
  13. int sockfd;
  14. char buf[N];
  15. struct sockaddr_in seraddr;
  16. socklen_t peerlen = sizeof(seraddr);
  17. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  18. {
  19. perror("socket error");
  20. exit(-1);
  21. }
  22. else
  23. {
  24. printf("socket successfully!\n");
  25. printf("sockfd:%d\n",sockfd);
  26. }
  27. memset(&seraddr, 0, sizeof(seraddr));
  28. seraddr.sin_family = AF_INET;
  29. seraddr.sin_port = htons(PORT);
  30. seraddr.sin_addr.s_addr = inet_addr("192.168.1.255"); //接收方绑定广播地址
  31. if(bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
  32. {
  33. perror("bind error");
  34. exit(-1);
  35. }
  36. else
  37. {
  38. printf("bind successfully!\n");
  39. printf("PORT:%d\n",PORT);
  40. }
  41. while(1)
  42. {
  43. if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&seraddr, &peerlen) < 0)
  44. {
  45. perror("fail to recvfrom");
  46. exit(-1);
  47. }
  48. else
  49. {
  50. printf("[%s:%d]",inet_ntoa(seraddr.sin_addr),ntohs(seraddr.sin_port));
  51. printf("%s\n",buf);
  52. }
  53. }
  54. return 0;
  55. }

执行结果如下

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/socket/guangbo$ ./guangbore
  2. socket successfully!
  3. sockfd:3
  4. bind successfully!
  5. PORT:8888
  6. [127.0.0.1:56195]This is a broadcast package!
  7. [127.0.0.1:56195]This is a broadcast package!
  8. [127.0.0.1:56195]This is a broadcast package!
  9. [127.0.0.1:56195]This is a broadcast package!
  10. [127.0.0.1:56195]This is a broadcast package!
  11. [127.0.0.1:56195]This is a broadcast package!
  12. [127.0.0.1:56195]This is a broadcast package!
  13. [127.0.0.1:56195]This is a broadcast package!
  14. [127.0.0.1:56195]This is a broadcast package!
  15. [127.0.0.1:56195]This is a broadcast package!
  16. [127.0.0.1:56195]This is a broadcast package!
  17. ...

3、组播

通过广播可以很方便地实现发送数据包给局域网中的所有主机。但广播同样存在一些问题,例如,频繁地发送广播包造成所以主机数据链路层都会接收并交给上层 协议处理,也容易引起局域网的网络风暴。

下面介绍一种数据包发送方式成为组播或多播。组播可以看成是单播和广播的这种。当发送组播数据包时,至于加入指定多播组的主机数据链路层才会处理,其他主机在数据链路层会直接丢掉收到的数据包。换句话说,我们可以通过组播的方式和指定的若干主机通信。

1、组播地址

IPv4 地址分为以下5类。

A类地址:最高位为0,主机号占24位,地址范围从 1.0.0.1到 126.255.255.254。

B类地址:最高两位为10,主机号占16位,地址范围从 128.0.0.1 到 191.254.255.254。

C类地址:最高3位为110,主机号占8位,地址范围从 192.0.1.1 到 223.255.254.254。

D类地址:最高4位为1110,地址范围从192.0.1.1到 223.255.254.254。

E类地址保留。

其中D类地址呗成为组播地址。每一个组播地址代表一个多播组。

2、组播包的发送和接收

组播包的发送和接收也通过UDP套接字实现。

1))组播发送流程如下:

创建UDP套接字

指定目标地址和端口

发送数据包

程序中,紧接着bind有一个setsockopt操作,它的作用是将socket加入一个组播组,因为socket要接收组播地址224.0.0.1的数据,它就必须加入该组播组。

结构体struct ip_mreq mreq是该操作的参数,下面是其定义:

[cpp] view plaincopy
  1. struct ip_mreq
  2. {
  3. struct in_addr imr_multiaddr;   // 组播组的IP地址。
  4. struct in_addr imr_interface;   // 本地某一网络设备接口的IP地址。
  5. };

一台主机上可能有多块网卡,接入多个不同的子网,imr_interface参数就是指定一个特定的设备接口,告诉协议栈只想在这个设备所在的子网中加入某个组播组。有了这两个参数,协议栈就能知道:在哪个网络设备接口上加入哪个组播组。

发送组播包的示例代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #define N 64
  10. #define PORT 8888
  11. int main()
  12. {
  13. int sockfd;
  14. char buf[N] = "This is a multicast package!";
  15. struct sockaddr_in dstaddr;
  16. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  17. {
  18. perror("socket error");
  19. exit(-1);
  20. }
  21. else
  22. {
  23. printf("socket successfully!\n");
  24. printf("sockfd:%d\n",sockfd);
  25. }
  26. memset(&dstaddr, 0, sizeof(dstaddr));
  27. dstaddr.sin_family = AF_INET;
  28. dstaddr.sin_port = htons(PORT);
  29. dstaddr.sin_addr.s_addr = inet_addr("224.10.10.1"); //绑定组播地址
  30. while(1)
  31. {
  32. sendto(sockfd, buf, N, 0,(struct sockaddr *)&dstaddr, sizeof(dstaddr));
  33. sleep(1);
  34. }
  35. return 0;
  36. }

2)组播包接收流程

组播包接收流程如下

创建UDP套接字

加入多播组

绑定地址和端口

接收数据包

组播包接收流程如下:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #define N 64
  10. #define PORT 8888
  11. int main()
  12. {
  13. int sockfd;
  14. char buf[N];
  15. struct ip_mreq mreq;
  16. struct sockaddr_in seraddr,myaddr;
  17. socklen_t peerlen = sizeof(seraddr);
  18. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  19. {
  20. perror("socket error");
  21. exit(-1);
  22. }
  23. else
  24. {
  25. printf("socket successfully!\n");
  26. printf("sockfd:%d\n",sockfd);
  27. }
  28. memset(&mreq, 0, sizeof(mreq));
  29. mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1"); //加入多播组,允许数据链路层处理指定组播包
  30. mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  31. if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
  32. {
  33. perror("fail to setsockopt");
  34. exit(-1);
  35. }
  36. memset(&seraddr, 0, sizeof(myaddr));//为套接字绑定组播地址和端口
  37. myaddr.sin_family = AF_INET;
  38. myaddr.sin_port = htons(PORT);
  39. myaddr.sin_addr.s_addr = inet_addr("224.10.10.1");
  40. if(bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1)
  41. {
  42. perror("bind error");
  43. exit(-1);
  44. }
  45. else
  46. {
  47. printf("bind successfully!\n");
  48. printf("PORT:%d\n",PORT);
  49. }
  50. while(1)
  51. {
  52. if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&seraddr, &peerlen) < 0)
  53. {
  54. perror("fail to recvfrom");
  55. exit(-1);
  56. }
  57. else
  58. {
  59. printf("[%s:%d]",inet_ntoa(seraddr.sin_addr),ntohs(seraddr.sin_port));
  60. printf("%s\n",buf);
  61. }
  62. }
  63. return 0;
  64. }

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/socket/zubo$ ./zubore
  2. socket successfully!
  3. sockfd:3
  4. bind successfully!
  5. PORT:8888
  6. [192.168.1.2:53259]This is a multicast package!
  7. [192.168.1.2:53259]This is a multicast package!
  8. [192.168.1.2:53259]This is a multicast package!
  9. [192.168.1.2:53259]This is a multicast package!
  10. [192.168.1.2:53259]This is a multicast package!
  11. [192.168.1.2:53259]This is a multicast package!
  12. [192.168.1.2:53259]This is a multicast package!
  13. [192.168.1.2:53259]This is a multicast package!
  14. [192.168.1.2:53259]This is a multicast package!
  15. ....

Linux 系统应用编程——网络编程(高级篇)相关推荐

  1. Linux操作系统下C语言网络编程(全文23475字,包含了Linux系统下所有网络编程的知识点,附程序代码)

    一.简介 如今网络应用随处可见,web.http.email 等这些都是网络应用程序,他们都有着基于相同的基本编程模型,有着相似的整体逻辑结构,并且还有着相同的编程接口.我们需要了解基本的客户端-服务 ...

  2. 【嵌入式Linux系统开发】网络编程

    文章目录 1.基本概念 1.1 OSI模型 1.2 常用命令 1.3 网络地址 1.4 IP设置项 1.5 端口 2.TCP/IP协议 2.1 整体架构概述 2.2 IP协议 2.3 IP构成 2.4 ...

  3. 入门Linux系统编程--网络编程

    文章目录 一.网络编程 1.socket服务端代码实现(无连接客户端) 6.socket服务端代码实现(连接客户端) 7.socket客户端代码实现 8.实现双方聊天 9.多方消息收发 二.往期文章 ...

  4. java 编程原理_Java网络编程 -- 网络编程基础原理

    Hello,今天记录下 Java网络编程 --> 网络编程基础原理. 一起学习,一起进步.继续沉淀,慢慢强大.希望这文章对您有帮助.若有写的不好的地方,欢迎评论给建议哈! 初写博客不久,我是杨展 ...

  5. Linux系统下网卡网络配置基础

    Ifconfig命令使LINUX核心知道软件回送和网卡这样一些网络接口,这样Linux就可以使用它们.除了上面介绍的这些用法之外,ifconfig命令用来监控和改变网络接口的状态,并且还可以带上很多命 ...

  6. linux系统下重启网络服务的两种方法

    linux系统下重启网络服务的两种方法 发布时间:2020-04-02 11:25:25 来源:亿速云 阅读:207 作者:小新 今天小编给大家分享的是linux系统下重启网络服务的两种方法,很多人都 ...

  7. linux系统下ntp网络时钟服务器(NTP服务器)的搭建和使用

    linux系统下ntp网络时钟服务器(NTP服务器)的搭建和使用 linux系统下ntp网络时钟服务器(NTP服务器)的搭建和使用 安徽京准科技开发的NTP网络时间源设备 参考 ahjzsz.com  ...

  8. Linux系统搭建NFS网络共享存储

    Linux系统搭建NFS网络共享存储 一.NFS概述: NFS是一种基于TCP/IP传输的网络文件系统协议,最初由SUN公司开发.通过NFS协议,客户机可以像访问本地目录一样访问远程服务器中的共享资源 ...

  9. Linux 系统时间与网络时间不一致 时间同步

    Linux 系统时间与网络时间不一致 时间同步 //查看时间date Tue Feb 25 20:15:18 CST 2020 //修改时间 date -s "20200225 20:16: ...

  10. 11单元-Linux系统下的网络配置

    Linux系统下的网络配置 1.IP ADDRESS -- internet protocol ADDRESS (网络进程地址) ip地址:网络位 + 主机位 ipv4 -- internet pro ...

最新文章

  1. Activity之间使用intent传递大量数据带来问题总结
  2. 7、Power Map—实例:添加二维数据表以及批注
  3. 【机器学习】FaceBook开源全网第一个时序王器--Kats。
  4. 【STM32】STM32F4系统架构
  5. C++描述杭电OJ 2015.偶数求和 ||
  6. Sort HDU5884(二分+多叉哈夫曼树)
  7. ruby 类方法与实例方法_Ruby Set相交? 实例方法
  8. 【Kafka】GroupCoordinatorNotAvailableException - The coordinator is not available
  9. Linux拷贝文件夹
  10. KVASER 与 Matlab联合使用
  11. SpringBean的生命周期
  12. openwrt PPPoE拨号如何设置-小米3硬改
  13. 实现Promise的resolve/reject/then/all/race/finally/catch方法
  14. 什么是埋点?简述埋点的操作流程
  15. RabbitMQ-Plugin configuration unchanged
  16. nslookup查询域名系统服务器,域名解析系统DNS诊断命令nslookup详解
  17. Service Mesh对比:Istio与Linkerd
  18. 基于深度摄像头的障碍物检测(realsense+opencv)
  19. OKCoin期货现货API[Python3版]
  20. Xshell的下载与安装

热门文章

  1. 开源 免费 java CMS - FreeCMS1.9 会员组管理
  2. 研究者展示黑客通过GPU绕过防毒软件查杀的方法
  3. leetcode1053. 交换一次的先前排列(贪心算法)
  4. leetcode658. 找到 K 个最接近的元素(二分法)
  5. 准确性 敏感性 特异性_如何掌握类型特异性的艺术
  6. 朴素贝叶斯实现分类_关于朴素贝叶斯分类及其实现的简短教程
  7. r怎么对两组数据统计检验_数据科学中最常用的统计检验是什么
  8. tab enter键出现
  9. SQL性能优化案例分析
  10. 爬取IMDBTOP250