1.域名系统

  程序中只使用主机名和服务名的好处是,如果IP或端口变化,只需要改变映射关系,不需要重新编译程序。

1.1 资源记录

  DNS的条目为资源记录,有用的项如下:

  1. A IPv4地址
  2. AAAA IPv6地址
  3. CNAME 规范名字
  4. 如:
  5. ftp.unpbook.com CNAME linux.unpbook.com
  6. www.unpbook.com CNAME linux.unpbook.com

1.2 解析器和名字服务器

  程序通过调用解析器库函数,调用DNS服务。常用的函数为 gethostbyname, gethostbyaddr。

  与解析器相关的配置文件和库函数关系如下

2.1 gethostbyname

  1. struct hostent *gethostbyname(const char *name);
  2.  
  3. struct hostent {
  4. char *h_name; /* official name of host */
  5. char **h_aliases; /* alias list */
  6. int h_addrtype; /* host address type */
  7. int h_length; /* length of address */
  8. char **h_addr_list; /* list of addresses */
  9. }

  该函数只能返回资源记录的 A ,也就是IPv4。

  若要考虑IPv6,应使用 getaddrinfo.

  h_name 就是 CNAME(规范名字),如 ftp.unpbook.com 的规范名字为 linux.unpbook.com

  h_addrtype 只有为 IPv4,才有用

  h_addr_list 指向一个数组,数组元素为 IP地址(网络字节序,不是点分十进制)。

  可以传入点分十进制,或域名调用 gethostbyname

  gethostbyname 调用错误,不会设置errno,而是设置 h_errno,使用 hstrerror(h_errno) 得到错误描述的字符串。

  1. int
  2. main(int argc, char **argv)
  3. {
  4. char *ptr, **pptr;
  5. char str[INET_ADDRSTRLEN];
  6. struct hostent *hptr;
  7.  
  8. while (--argc > 0) {
  9. ptr = *++argv;
  10. if ( (hptr = gethostbyname(ptr)) == NULL) {
  11. err_msg("gethostbyname error for host: %s: %s",
  12. ptr, hstrerror(h_errno));
  13. continue;
  14. }
  15. printf("official hostname: %s\n", hptr->h_name);
  16.  
  17. for (pptr = hptr->h_aliases; *pptr != NULL; pptr++)
  18. printf("\talias: %s\n", *pptr);
  19.  
  20. switch (hptr->h_addrtype) {
  21. case AF_INET:
  22. pptr = hptr->h_addr_list;
  23. for ( ; *pptr != NULL; pptr++)
  24. printf("\taddress: %s\n",
  25. Inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
  26. break;
  27.  
  28. default:
  29. err_ret("unknown address type");
  30. break;
  31. }
  32. }
  33. exit(0);
  34. }

2.2 gethostbyaddr

  1. struct hostent *gethostbyaddr(const void *addr,
  2. socklen_t len, int type);

  输入网络字节序IP地址,查找主机CNAME。

  type 为 AF_INET

  返回 hostent ,通常我们感兴趣的只有 h_name。

2.3 可重入函数

  gethostbyname 和 getbostbyaddr 是不可重入的,原因是使用 static hostent host 。

  不可重入带来问题通常在 多线程和信号。

  如

  1. main()
  2. {
  3. signal(SIGALRM, sig_alrm);
  4. hptr = gethostbyname(...);
  5. }
  6.  
  7. void
  8. sig_alrm(int sig)
  9. {
  10. hptr= gethostbyname(...);
  11. }

  可重入可不可重入总结:

  1. gethostbyname, gethostbyaddr, getservbyname, getservbyport 都是不可重入的,原因是使用静态变量。不过有可重入版本
  2. inet_pton, inet_ntop 不可重入
  3. inet_ntoa 不可重入,不过有可重入版本。
  4. getaddrinfo, getnameinfo 可重入

  对于errno的处理,errno可能会被多线程,信号意外改变,可以如下处理

  1. void
  2. sig_alrm(int sig)
  3. {
  4. int errno_save;
  5.  
  6. errno_save = errno;
  7. if (write(...) != nbytes)
  8. fprintf(stderr, "write error = %d\n", errno);
  9. errno = errno_save;
  10. }

3.1 getserbyname

  1. struct servent *getservbyname(const char *name, const char *proto);
  2.  
  3. struct servent {
  4. char *s_name; /* official service name */
  5. char **s_aliases; /* alias list */
  6. int s_port; /* port number */
  7. char *s_proto; /* protocol to use */
  8. }

  输入服务名和协议,查询端口号。

  如果指定proto,必须保证服务器使用了对应协议,如果没有指定,因为通常服务器使用相同端口号来使用不同协议,所以也没关系。

  s_port是网络字节序

  相关配置文件 /etc/services

  典型调用:

  1. sptr = getservbyname("domain", "udp"); // DNS using UDP
  2. sptr = getservbyname("ftp" "tcp"); // FTP using TCP

3.2 getservbyport

  1. struct servent *getservbyport(int port, const char *proto);

  输入端口号和协议,查询服务名

  port必须为网络字节序

  典型调用:

  1. sptr = getservbyport(hton(53), "udp"); // DNS using UDP

4. 使用 gethostbyname 和 getserbyname

  1. int
  2. main(int argc, char **argv)
  3. {
  4. int sockfd, n;
  5. char recvline[MAXLINE + 1];
  6. struct sockaddr_in servaddr;
  7. struct in_addr **pptr;
  8. struct in_addr *inetaddrp[2];
  9. struct in_addr inetaddr;
  10. struct hostent *hp;
  11. struct servent *sp;
  12.  
  13. if (argc != 3)
  14. err_quit("usage: daytimetcpcli1 <hostname> <service>");
  15.  
  16. if ( (hp = gethostbyname(argv[1])) == NULL) {
  17. if (inet_aton(argv[1], &inetaddr) == 0) {
  18. err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));
  19. } else {
  20. inetaddrp[0] = &inetaddr;
  21. inetaddrp[1] = NULL;
  22. pptr = inetaddrp;
  23. }
  24. } else {
  25. pptr = (struct in_addr **) hp->h_addr_list;
  26. }
  27.  
  28. if ( (sp = getservbyname(argv[2], "tcp")) == NULL)
  29. err_quit("getservbyname error for %s", argv[2]);
  30.  
  31. for ( ; *pptr != NULL; pptr++) {
  32. sockfd = Socket(AF_INET, SOCK_STREAM, 0);
  33.  
  34. bzero(&servaddr, sizeof(servaddr));
  35. servaddr.sin_family = AF_INET;
  36. servaddr.sin_port = sp->s_port;
  37. memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
  38. printf("trying %s\n",
  39. Sock_ntop((SA *) &servaddr, sizeof(servaddr)));
  40.  
  41. if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) == 0)
  42. break; /* success */
  43. err_ret("connect error");
  44. close(sockfd);
  45. }
  46. if (*pptr == NULL)
  47. err_quit("unable to connect");
  48.  
  49. while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) {
  50. recvline[n] = 0; /* null terminate */
  51. Fputs(recvline, stdout);
  52. }
  53. exit(0);
  54. }

5.1 getaddrinfo

  1. int getaddrinfo(const char *hostname, const char *service,
  2. const struct addrinfo *hints,
  3. struct addrinfo **res);
  4.  
  5. struct addrinfo {
  6. int ai_flags;
  7. int ai_family;
  8. int ai_socktype;
  9. int ai_protocol;
  10. size_t ai_addrlen;
  11. struct sockaddr *ai_addr;
  12. char *ai_canonname;
  13. struct addrinfo *ai_next;
  14. };

  getaddrinfo支持IPv4,IPv6,并且提供名字到地址,服务到端口两种转换,返回的是一个sockaddr结构而不是地址列表,因此可以直接用于套接字库函数。

  hostname 是一个主机名或地址串(点分十进制或十六进制串)

  service 是一个服务名或十进制端口号串

  hints 可以为空,也可以是指向 struct addrinfo的指针,表示对期望返回信息的暗示。

    hints可以设置成员有

  1. int ai_flags;
  2. int ai_family;
  3. int ai_socktype;
  4. int ai_protocol;

    ai_flags 可用的标志值和含义:

  1. AI_PASSIVE 套接字将用于被动打开
  2. AI_CANONAME 给之getaddrinfo返回主机的规范名字
  3. AF_NUMERICHOST 防止任何类型的名字到地址的映射,hostname参数必须是一个地址串
  4. AF_NUMERICSERV 防止任何类型的名字到端口的映射,service参数必须是一个端口号
  5. AI_V4MAPPED 如果同时指定ai_famliy AF_INET6,那么如果没有可用的AAAA记录,就返回A记录对应的IPv4映射的IPv6地址
  6. AI_ALL 如果同时指定AI_V4MAPPED,那么除了返回AAAA记录对应的IPv6地址外,还返回A记录对应的IPv4映射的IPv6地址。
  7. AI_ADDRCONFIG 按照所在主机的配置选择返回地址类型,也就是只查找与所在主机回馈接口以外的网络接口配置的IP地址版本一致的地址。

  如果hints参数是空指针,那么本函数就假设ai_flag,ai_socktype,ai_protocol的值均为空,ai_family为AF_UNSPEC

  如果函数返回成功(0),result参数返回addrinfo结构的链表。

  举例,在没有任何暗示信息前提下,请求查找有2个IP地址的某个主机上的domain服务,那么就将返回4个addrinfo结构,

    第一个IP地址组合SOCK_STREAM套接字类型

    第一个IP地址组合SOCK_DGRAM套接字类型

    第二个IP地址组合SOCK_STREAM套接字类型

    第二个IP地址组合SOCK_DGRAM套接字类型

  且这些结构的返回顺序不确定。

  返回的addrinfo结构可用于其他套接字函数,

    socket调用使用 ai_family, ai_socktype,

    connect,bind调用使用 ai_addr, 和 ai_addrlen

  返回的res变量指向的addrinfo结构空间是动态分配的,需要使用freeaddrinfo释放。

  getaddrinfo的常见使用如下:

  (1)TCP或UDP客户进程,指定hostname和 service,返回后,TCP客户进程逐个使用返回的地址,以调用 socket , connect,直到一个地址调用成功。UDP客户进程调用sendto或connect,如果地址不工作(如收到错误信息,或者超时),则测试其他地址,直到成功。

  (2)服务进程一般只指定 service 而不指定 hostname,同时在AI_PASSIVE标志。TCP服务进程随后调用socket,bind,listen。如果想获得accept的客户地址,那么用 ai_addrlen 来malloc 地址结构。UDP服务调用socket,bind,recvfrom,如果服务器清楚自己只处理一种类型的套接字,那么应该把hints的ai_socktype设置成 SOCK_STREAM或SOCK_DGRAM。

  (3)到目前为止,我们的服务器(UDP,TCP)都只创建一个监听套接字或数据报套接字。而另一种设计是用 select或poll让服务器处理多个套接字,这种情况下服务器遍历整个addrinfo结构链表,并为每个结构创建一个套接字,再使用select或poll。

      这个技术的问题是,getaddrinfo返回多个结构的原因之一是该服务可以同时由IPv4,IPv6处理,但是这两个协议并非完全独立,如果我们为某个给定端口创建一个IPv6监听套接字,那么就没必要为同一个端口创建一个IPv4套接字,因为来自IPv4的连接将由协议栈和IPv6监听套接字自动处理。

5.2 gai_strerror

  1. const char *gai_strerror(int errcode);

  getaddrinfo的错误由gai_strerror解释。

5.3 freeaddrinfo

  1. void freeaddrinfo(struct addrinfo *res);

  getaddrinfo返回的res指向的链表是动态分配的,由freeaddrinfo释放。

  同时注意res的浅拷贝问题。

5.4 getnameinfo

  1. int getnameinfo(const struct sockaddr *sa, socklen_t salen,
  2. char *host, size_t hostlen,
  3. char *serv, size_t servlen, int flags);

  输入套接字地址,返回主机名和服务名。

  sock_ntop和getnameinfo的区别是,前者不涉及DNS,只返回一个IP地址和端口号的一个可显示版本,后者通常尝试获得主机和服务名字。

5.5 对getaddrinfo的封装

  getaddrinfo是推荐使用的,但是调用过于麻烦,所以封装常用操作

  1. struct addrinfo *
  2. host_serv(const char *host, const char *serv, int family, int socktype)
  3. {
  4. int n;
  5. struct addrinfo hints, *res;
  6.  
  7. bzero(&hints, sizeof(struct addrinfo));
  8. hints.ai_flags = AI_CANONNAME; /* always return canonical name */
  9. hints.ai_family = family; /* AF_UNSPEC, AF_INET, AF_INET6, etc. */
  10. hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */
  11.  
  12. if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
  13. return(NULL);
  14.  
  15. return(res); /* return pointer to first on linked list */
  1. int
  2. tcp_connect(const char *host, const char *serv)
  3. {
  4. int sockfd, n;
  5. struct addrinfo hints, *res, *ressave;
  6.  
  7. bzero(&hints, sizeof(struct addrinfo));
  8. hints.ai_family = AF_UNSPEC;
  9. hints.ai_socktype = SOCK_STREAM;
  10.  
  11. if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
  12. err_quit("tcp_connect error for %s, %s: %s",
  13. host, serv, gai_strerror(n));
  14. ressave = res;
  15.  
  16. do {
  17. sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  18. if (sockfd < 0)
  19. continue; /* ignore this one */
  20.  
  21. if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
  22. break; /* success */
  23.  
  24. Close(sockfd); /* ignore this one */
  25. } while ( (res = res->ai_next) != NULL);
  26.  
  27. if (res == NULL) /* errno set from final connect() */
  28. err_sys("tcp_connect error for %s, %s", host, serv);
  29.  
  30. freeaddrinfo(ressave);
  31.  
  32. return(sockfd);
  33. }
  1. int
  2. tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
  3. {
  4. int listenfd, n;
  5. const int on = 1;
  6. struct addrinfo hints, *res, *ressave;
  7.  
  8. bzero(&hints, sizeof(struct addrinfo));
  9. hints.ai_flags = AI_PASSIVE;
  10. hints.ai_family = AF_UNSPEC;
  11. hints.ai_socktype = SOCK_STREAM;
  12.  
  13. if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
  14. err_quit("tcp_listen error for %s, %s: %s",
  15. host, serv, gai_strerror(n));
  16. ressave = res;
  17.  
  18. do {
  19. listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  20. if (listenfd < 0)
  21. continue; /* error, try next one */
  22.  
  23. Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  24. if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
  25. break; /* success */
  26.  
  27. Close(listenfd); /* bind error, close and try next one */
  28. } while ( (res = res->ai_next) != NULL);
  29.  
  30. if (res == NULL) /* errno from final socket() or bind() */
  31. err_sys("tcp_listen error for %s, %s", host, serv);
  32.  
  33. Listen(listenfd, LISTENQ);
  34.  
  35. if (addrlenp)
  36. *addrlenp = res->ai_addrlen; /* return size of protocol address */
  37.  
  38. freeaddrinfo(ressave);
  39.  
  40. return(listenfd);
  41. }
  42. /* end tcp_listen */

  上面函数有一个问题,即指定的地址族为 AF_UNSPEC,即可能返回非期待的地址族套接字,如期待IPv4,返回IPv4和IPv6。

  解决方法:

    我们可以强制指定地址协议,如inet_pton

  1. inet_pton(AF_INET,"0.0.0.0", &foo); //succeeds
  2. inet_pton(AF_INET, "0::0", &foo); // fails
  3. inet_pton(AF_INET6, "0.0.0.0", &foo); // fails
  4. inet_pton(AF_INET6, "0::0", &foo); //succeeds

无连接UDP

  1. int
  2. udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenp)
  3. {
  4. int sockfd, n;
  5. struct addrinfo hints, *res, *ressave;
  6.  
  7. bzero(&hints, sizeof(struct addrinfo));
  8. hints.ai_family = AF_UNSPEC;
  9. hints.ai_socktype = SOCK_DGRAM;
  10.  
  11. if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
  12. err_quit("udp_client error for %s, %s: %s",
  13. host, serv, gai_strerror(n));
  14. ressave = res;
  15.  
  16. do {
  17. sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  18. if (sockfd >= 0)
  19. break; /* success */
  20. } while ( (res = res->ai_next) != NULL);
  21.  
  22. if (res == NULL) /* errno set from final socket() */
  23. err_sys("udp_client error for %s, %s", host, serv);
  24.  
  25. *saptr = Malloc(res->ai_addrlen);
  26. memcpy(*saptr, res->ai_addr, res->ai_addrlen);
  27. *lenp = res->ai_addrlen;
  28.  
  29. freeaddrinfo(ressave);
  30.  
  31. return(sockfd);
  32. }
  33. /* end udp_client */

有连接UDP

  1. int
  2. udp_connect(const char *host, const char *serv)
  3. {
  4. int sockfd, n;
  5. struct addrinfo hints, *res, *ressave;
  6.  
  7. bzero(&hints, sizeof(struct addrinfo));
  8. hints.ai_family = AF_UNSPEC;
  9. hints.ai_socktype = SOCK_DGRAM;
  10.  
  11. if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
  12. err_quit("udp_connect error for %s, %s: %s",
  13. host, serv, gai_strerror(n));
  14. ressave = res;
  15.  
  16. do {
  17. sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  18. if (sockfd < 0)
  19. continue; /* ignore this one */
  20.  
  21. if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
  22. break; /* success */
  23.  
  24. Close(sockfd); /* ignore this one */
  25. } while ( (res = res->ai_next) != NULL);
  26.  
  27. if (res == NULL) /* errno set from final connect() */
  28. err_sys("udp_connect error for %s, %s", host, serv);
  29.  
  30. freeaddrinfo(ressave);
  31.  
  32. return(sockfd);
  33. }
  34. /* end udp_connect */
  1. int
  2. udp_server(const char *host, const char *serv, socklen_t *addrlenp)
  3. {
  4. int sockfd, n;
  5. struct addrinfo hints, *res, *ressave;
  6.  
  7. bzero(&hints, sizeof(struct addrinfo));
  8. hints.ai_flags = AI_PASSIVE;
  9. hints.ai_family = AF_UNSPEC;
  10. hints.ai_socktype = SOCK_DGRAM;
  11.  
  12. if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
  13. err_quit("udp_server error for %s, %s: %s",
  14. host, serv, gai_strerror(n));
  15. ressave = res;
  16.  
  17. do {
  18. sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  19. if (sockfd < 0)
  20. continue; /* error - try next one */
  21.  
  22. if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
  23. break; /* success */
  24.  
  25. Close(sockfd); /* bind error - close and try next one */
  26. } while ( (res = res->ai_next) != NULL);
  27.  
  28. if (res == NULL) /* errno from final socket() or bind() */
  29. err_sys("udp_server error for %s, %s", host, serv);
  30.  
  31. if (addrlenp)
  32. *addrlenp = res->ai_addrlen; /* return size of protocol address */
  33.  
  34. freeaddrinfo(ressave);
  35.  
  36. return(sockfd);
  37. }
  38. /* end udp_server */

  UDP套接字是不需要设置SO_REUSEADDR,因为UDP是没有WAIT_TIME类似的状态物。

UNP第11章——名字与地址转换的更多相关文章

  1. 《Unix 网络编程》11:名字和地址转换

    名字和地址转换 系列文章导航:<Unix 网络编程>笔记 域名系统 简介 域名系统主要用于主机名字和 IP 地址之间的映射.主机名可以是: 简单名字,如:centos01 全限定域名(FQ ...

  2. UNP学习笔记(第十一章 名字与地址转换)

    域名系统 域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射. 主机名既可以是一个简单得名字,如solaris,也可以是一个全限定域名,如solaris.un ...

  3. UNP总结 Chapter 11 名字与地址转换

    本章讲述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在服务器名字和端口 ...

  4. UNIX网络编程读书笔记:名字与地址转换

    概述 在名字和数值地址间进行转换的函数: gethostbyname和gethostbyaddr:在主机名字与IPv4地址之间进行转换.仅仅支持IPv4. getservbyname和getservb ...

  5. UNP学习第九章 基本名字与地址转换

    之前都用数值地址来表示主机(206.6.226.33),用数值端口号来标识服务器. 然而,我们应该使用名字而不是数值:名字比较容易记,数值地址可以改变但名字保持不变. 随着往IPv6上转移,数值地址变 ...

  6. UNIX网络编程——名字与地址转换(gethostbyname,gethostbyaddr,getservbyname,getservbyport,getaddrinfo,getnameinfo函数)

    名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IPv4地址之间进行转换.getservbyname和getservbyport在服务器名字和端口号之 ...

  7. 第11章 拾遗1:网络地址转换(NAT)和端口映射

    1. 网络地址转换(NAT) 1.1 NAT的应用场景 (1)应用场景:允许将私有IP地址映射到公网地址,以减缓IP地址空间的消耗 ①需要连接Internet,但主机没有公网IP地址 ②更换了一个新的 ...

  8. Linux就这个范儿 第11章 独霸网络的蜘蛛神功

    Linux就这个范儿 第11章  独霸网络的蜘蛛神功  第11章 应用层 (Application):网络服务与最终用户的一个接口.协议有:HTTP FTP TFTP SMTP SNMP DNS表示层 ...

  9. 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)

    0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...

随机推荐

  1. MySQL 8 新特性之Clone Plugin

    Clone Plugin是MySQL 8.0.17引入的一个重大特性,为什么要实现这个特性呢?个人感觉,主要还是为Group Replication服务.在Group Replication中,添加一 ...

  2. day18 Pyhton学习 匿名函数

    匿名函数 别称: lambda表达式 函数,没有名字 def wahaha(n):#wahaha return n**2 print(wahaha.__name__) qqxing = lambda ...

  3. spring boot:用rocketmq发送延时消息用来取消订单(spring boot 2.3.3)

    一,为什么要用延时消息来取消订单? 1,为什么要取消订单 在电商的下单过程中,需要在生成订单时扣减库存, 但有可能发生这种情况:用户下了单,临时改变主意不再支付, 则订单不能无限期的保留,因为还要把占 ...

  4. spring boot:thymeleaf给fragment传递参数的方法(spring boot 2.3.3)

    一,thymeleaf如何给fragment传递参数? 1,如果是全局的参数,可以用interceptor中传递 非全局参数,可以从controller中传递 2,引用片断时也可以传递参数 说明:刘宏 ...

  5. go内建方法 new和make区别

    package mainimport ( "fmt" "reflect")func main() { // make函数 //makeSlice() // 创建 ...

  6. linux查看登录用户

    [root@localhost ~]# w 11:01:06 up 3 days, 12:40, 1 user, load average: 0.00, 0.01, 0.05 USER TTY FRO ...

  7. Spring 缓存抽象

    Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术:并支 ...

  8. linux mount 挂载提示 mount: you must specify the filesystem type

    解决方法: mkfs.ext3 /dev/vdv mount -t ext3 /dev/vdv /usr1

  9. Spark如何删除无效rdd checkpoint

    spark可以使用checkpoint来作为检查点,将rdd的数据写入hdfs文件,也可以利用本地缓存子系统. 当我们使用checkpoint将rdd保存到hdfs文件时,如果任务的临时文件长时间不删 ...

  10. 正则表达式小应用之对xml格式字符串每个字段加双引号

    通过Python对接口进行自动化后需要把xml格式的报文放到LoadRunner上进行压力测试,在pyCharm控制台打印出报文后,把报文放到LoadRunner中做了格式调整后,每个字段需要添加双引 ...