UNP学习笔记(第十一章 名字与地址转换)
域名系统
域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射。
主机名既可以是一个简单得名字,如solaris,也可以是一个全限定域名,如solaris.unpbook.com。
资源记录
DNS中的条目称为资源记录(resource record,RR)
A记录把一个主机名映射成一个32位的IPv4的地址。
AAAA记录把一个主机名映射成一个128位的IPv6地址。
PTR记录把IP地址映射成主机名。
MX记录把一个主机指定作为主机的“邮件交换器(mail exchanger)”。
CNAME代表“cononical name”(规范名字),它的常见用法为常用的服务指派CNAME记录,例如下面名为linux的主机有以下2个CNAME记录
解析器和名字服务器
每个组织机构往往运行一个或多个名字服务器,它们通常是所谓的BIND(Berkeley Internet Name Domain)程序。
本书我们编写的应用程序通过调用成为解析器的函数库中的函数接触DNS服务器,常见的解析器函数是gethostbyname和gethostbyaddr。
下图展示了应用程序、解析器和名字服务器的一个典型关系
gethostbyname函数
查找主机名最基本的函数是gethostbyname。如果调用成功,它就返回一个指向hostent结构的指针。
#inlcude <netdb.h>
struct hostent *gethostbyname(const char *hostname); struct hostent {
char *h_name; /* official (canonical) name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
};
gethostbyname执行的是对A记录的查询。它只能返回IPv4地址
下图展示了hostent结构和它包含的信息
下面给出调用gethostbyname的简单例子
#include "unp.h" int
main(int argc, char **argv)
{
char *ptr, **pptr;
char str[INET_ADDRSTRLEN];
struct hostent *hptr; while (--argc > ) {
ptr = *++argv;
if ( (hptr = gethostbyname(ptr)) == NULL) {
err_msg("gethostbyname error for host: %s: %s",
ptr, hstrerror(h_errno));
continue;
}
printf("official hostname: %s\n", hptr->h_name); for (pptr = hptr->h_aliases; *pptr != NULL; pptr++)
printf("\talias: %s\n", *pptr); switch (hptr->h_addrtype) {
case AF_INET:
pptr = hptr->h_addr_list;
for ( ; *pptr != NULL; pptr++)
printf("\taddress: %s\n",
Inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
break; default:
err_ret("unknown address type");
break;
}
}
exit();
}
运行结果
gethostbyaddr函数
与gethostbyname的行为相反,gethostbyaddr函数试图由一个二进制的IP地址找到相应的主机名
#include <netdb.h>
struct hostent *gethostbyaddr(const char *addr,socklen_t len,int family);
//返回:若成功则为非空指针,若出错则为NULL且设置h_errno
addr参数实际上不是char *类型,而是一个指向存放IPv4地址的某个in_addr结构的指针
gethostbyaddr向一个名字服务器查询PTR记录。我们感兴趣的字段通常是规范主机名的h_name。
getservbyname和getservbyport函数
getservbyname函数用于根据给定名字查找相应服务
名字到端口号的映射关系保存在一个文件中(通常是/etc/services)
#include <netdb.h>
struct servent *getservbyname(const char *servername,const char *protoname);
servent结构如下
struct servent {
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s_port; /* port number */
char *s_proto; /* protocol to use */
};
servent结构中我们关心的主要字段是端口号,下面是本函数的典型调用
struct servent *sptr; sptr=getservbyname("domain","unp"); /* DNS using UNP */
sptr=getservbyname("ftp","tcp"); /* FTP using TCP*/
sptr=getservbyname("ftp",NULL); /* FTP using TCP*/
sptr=getservbyname("ftp","unp"); /* this call will fail */
getservbyport用于根据给定端口号和可选协议查找相应服务
#include <netdb.h>
struct servent *getservbyport(int port,const char *protoname);
port参数的值必须网络字节序。本函数的典型调用如下
struct servent *sptr; sptr=getservbyport(htons(),"unp"); /* DNS using UDP */
sptr=getservbyport(htons(),"tcp"); /* FTP using TCP*/
sptr=getservbyport(htons(),NULL); /* FTP using TCP*/
sptr=getservbyport(htons(),"unp"); /* this call will fail */
有些端口号在TCP上用于一种服务,在UDP上却用于完全不同的另一种服务
下面是使用gethostbyname和getservbyname的时间获取客户程序
#include "unp.h" int
main(int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE + ];
struct sockaddr_in servaddr;
struct in_addr **pptr;
struct in_addr *inetaddrp[];
struct in_addr inetaddr;
struct hostent *hp;
struct servent *sp; if (argc != )
err_quit("usage: daytimetcpcli1 <hostname> <service>"); if ( (hp = gethostbyname(argv[])) == NULL) {
if (inet_aton(argv[], &inetaddr) == ) {
err_quit("hostname error for %s: %s", argv[], hstrerror(h_errno));
} else {
inetaddrp[] = &inetaddr;
inetaddrp[] = NULL;
pptr = inetaddrp;
}
} else {
pptr = (struct in_addr **) hp->h_addr_list;
} if ( (sp = getservbyname(argv[], "tcp")) == NULL)
err_quit("getservbyname error for %s", argv[]); for ( ; *pptr != NULL; pptr++) {
sockfd = Socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = sp->s_port;
memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
printf("trying %s\n",
Sock_ntop((SA *) &servaddr, sizeof(servaddr))); if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) == )
break; /* success */
err_ret("connect error");
close(sockfd);
}
if (*pptr == NULL)
err_quit("unable to connect"); while ( (n = Read(sockfd, recvline, MAXLINE)) > ) {
recvline[n] = ; /* null terminate */
Fputs(recvline, stdout);
}
exit();
}
getaddrinfo函数
getaddrinfo函数能够处理名字到地址以及服务到端口的这两种转换,而且支持IPv6。
#include <netdb.h>
int getaddrinfo(const char *hostname,
const char *service,
const struct addrinfo *hint,
struct addrinfo **result);
本函数通过result指针参数返回一个指向addrinfo结构链表的指针,该结构定义如下
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
hostname参数是一个主机名或地址串(IPv4点分十进制数串或IPv6十六进制数串)
service参数是一个服务名或十进制端口号数串
hints参数可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。
例如,如果指定的服务既支持TCP也支持UDP,那么调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM,使得返回的仅仅是适用于数据报套接字的信息。
hints结构中调用者可以设置的成员有:
1.ai_flags(0个或多个或在一起的AI_xxx值)
2.ai_family(某个AF_xxx值)
3.ai_socktype(某个SOCK_xxx值)
4.ai_protocol
ai_flags成员可用的标志值及含义如下:
AI_PASSIVE 套接字将用于被动打开
AI_CONONNAME 告诉getaddrinfo函数返回主机的规范名字
AI_NUMERICHOST 防止任何类型的名字到地址映射,hostname参数必须是一个地址串
AI_NUMERICSERV 防止任何类型的名字到服务器映射,service参数必须是一个十进制端口号数串
AI_V4MAPPED 如果同时制定ai_family成员为AF_INET6,那么如果没有可用的AAAA记录,就返回A记录对应的IPv4映射的IPv6地址
AI_ALL 如果同时指定AI_V4MAPPED标志,那么除了返回与AAAA记录对应的IPv6地址外,还返回与A记录对应的IPv4映射的IPv6地址
AI_ADDRCONFIG 按照所在主机的配置选择返回地址类型
如果本函数返回成功,那么result参数指向的变量已被填入一个指针,它指向由其中的ai_next成员串联起来的addrinfo结构链表。可导致返回多个addrinfo结构的情形有以下两个:
1.如果与hostname参数关联的地址有多个,则每个地址都返回一个对应的结构
2.如果service参数指定的服务支持多个套接字类型,那么每个套接字类型都可能返回一个对应的结构
在addrinfo结构中返回的信息可现成用于socket调用,随后现成用于客户的connect或sendto调用,或者社和服务器的bind调用。
如:socket函数的参数就是addrinfo结构中的ai_family、ai_socktype和ad_addr成员。
下图是getaddrinfo返回信息的实例
下面查看这个函数的一些常见的输入:
1.指定hostname和service。这是TCP或UDP客户进程调用getaddrinfo的常规输入。
该调用返回后,TCP客户在一个循环中针对每个返回的IP地址,逐一调用socket和connect,直到有一个连接成功,或者所有地址尝试完毕为止。
2.典型的服务器值指定service而不指定hostname,同时在hints结构中指定AI_PASSIVE标志。返回的套接字地址结构中应含有一个值为INADDR_ANY(对于IPv4)的IP地址
3.服务器可以使用select或poll函数让服务器进程处理多个套接字。
这种情况下,服务器将遍历getaddrinfo返回的整个addrinfo结构链表,并为每个结构创建一个套接字,再使用select或poll。
gai_strerror函数
下图个出可由getaddrinfo返回的非0错误值的名字和含义。gai_strerror以这些值为它的唯一参数,返回对应的出错字符串
#include <netdb.h>
const char *gai_strerror(int error);
freeaddrinfo函数
由getaddrinfo返回的所有存储空间都是动态分配的。这些存储空间通过调用freeaddrinfo释放
#include <netdb.h>
void freeaddrinfo(struct addrinfo *ai);
下面函数是使用getaddrinfo的接口函数
host_serv函数
host_serv函数不要求调用者分配并填写一个hints结构,该结构中我们感兴趣的两个字段(地址族和套接字类型)成为这个接口函数的参数。
#include "unp.h"
struct addrinfo *host_serv(const char *host, const char *serv, int family, int socktype);
//返回:若成功则为指向addrinfo结构的指针,若出错则为NULL
下面是该函数的源代码
struct addrinfo *
host_serv(const char *host, const char *serv, int family, int socktype)
{
int n;
struct addrinfo hints, *res; bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_CANONNAME; /* always return canonical name */
hints.ai_family = family; /* AF_UNSPEC, AF_INET, AF_INET6, etc. */
hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */ if ( (n = getaddrinfo(host, serv, &hints, &res)) != )
return(NULL); return(res); /* return pointer to first on linked list */
}
tcp_connect函数
我们使用getaddrinfo编写tcp_connect函数:创建一个TCP套接字并连接到一个服务器。
#include "unp.h"
int tcp_connect(const char *host, const char *serv);
下面是该函数的源代码
#include "unp.h" int
tcp_connect(const char *host, const char *serv)
{
int sockfd, n;
struct addrinfo hints, *res, *ressave; bzero(&hints, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != )
err_quit("tcp_connect error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res; do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < )
continue; /* ignore this one */ if (connect(sockfd, res->ai_addr, res->ai_addrlen) == )
break; /* success */ Close(sockfd); /* ignore this one */
} while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno set from final connect() */
err_sys("tcp_connect error for %s, %s", host, serv); freeaddrinfo(ressave); return(sockfd);
}
使用tcp_connect重新编写时间获取客户程序
#include "unp.h" int
main(int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE + ];
socklen_t len;
struct sockaddr_storage ss; if (argc != )
err_quit("usage: daytimetcpcli <hostname/IPaddress> <service/port#>"); sockfd = Tcp_connect(argv[], argv[]); len = sizeof(ss);
Getpeername(sockfd, (SA *)&ss, &len);
printf("connected to %s\n", Sock_ntop_host((SA *)&ss, len)); while ( (n = Read(sockfd, recvline, MAXLINE)) > ) {
recvline[n] = ; /* null terminate */
Fputs(recvline, stdout);
}
exit();
}
tcp_listen函数
tcp_listen执行TCP服务器的通常步骤:创建一个TCP套接字,给它捆绑服务器的总所周知的端口,并允许接收外来的连接请求。
#include "unp.h"
int tcp_listen(const char *host, const char *serv, socklen_t *addrlenp);
//返回:若成功则为连接套接字描述符,若出错则不返回
下面是该函数的源代码
#include "unp.h" int
tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
{
int listenfd, n;
const int on = ;
struct addrinfo hints, *res, *ressave; bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != )
err_quit("tcp_listen error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res; do {
listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (listenfd < )
continue; /* error, try next one */ Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(listenfd, res->ai_addr, res->ai_addrlen) == )
break; /* success */ Close(listenfd); /* bind error, close and try next one */
} while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno from final socket() or bind() */
err_sys("tcp_listen error for %s, %s", host, serv); Listen(listenfd, LISTENQ); if (addrlenp)
*addrlenp = res->ai_addrlen; /* return size of protocol address */ freeaddrinfo(ressave); return(listenfd);
}
下面使用tcp_listen重新编写时间获取服务器程序
#include "unp.h"
#include <time.h> int
main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t len;
char buff[MAXLINE];
time_t ticks;
struct sockaddr_storage cliaddr; if (argc != )
err_quit("usage: daytimetcpsrv1 <service or port#>"); listenfd = Tcp_listen(NULL, argv[], NULL); for ( ; ; ) {
len = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *)&cliaddr, &len);
printf("connection from %s\n", Sock_ntop((SA *)&cliaddr, len)); ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff)); Close(connfd);
}
}
下面是tcp_listen的协议无关时间获取服务器程序
#include "unp.h"
#include <time.h> int
main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t len, addrlen;
char buff[MAXLINE];
time_t ticks;
struct sockaddr_storage cliaddr; if (argc == )
listenfd = Tcp_listen(NULL, argv[], &addrlen);
else if (argc == )
listenfd = Tcp_listen(argv[], argv[], &addrlen);
else
err_quit("usage: daytimetcpsrv2 [ <host> ] <service or port>"); for ( ; ; ) {
len = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *)&cliaddr, &len);
printf("connection from %s\n", Sock_ntop((SA *)&cliaddr, len)); ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff)); Close(connfd);
}
}
udp_client函数
使用getaddrinfo来编写udp_client函数。
本函数创建一个为连接的UDP套接字,并返回3项数据。
1.返回值是该套接字的描述符
2.把目的IP地址和端口存放在saptr指向的套接字地址结构中,用于稍后调用sendto
3.这个套接字地址结构的大小在lenp指向的变量中返回
#include "unp.h"
int udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenp);
//返回:若成功则为未连接套接字描述符,若出错则不返回
下面是该函数的源代码
#include "unp.h" int
udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenp)
{
int sockfd, n;
struct addrinfo hints, *res, *ressave; bzero(&hints, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != )
err_quit("udp_client error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res; do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd >= )
break; /* success */
} while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno set from final socket() */
err_sys("udp_client error for %s, %s", host, serv); *saptr = Malloc(res->ai_addrlen);
memcpy(*saptr, res->ai_addr, res->ai_addrlen);
*lenp = res->ai_addrlen; freeaddrinfo(ressave); return(sockfd);
}
协议无关时间获取客户程序
#include "unp.h" int
main(int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE + ];
socklen_t salen;
struct sockaddr *sa; if (argc != )
err_quit("usage: daytimeudpcli1 <hostname/IPaddress> <service/port#>"); sockfd = Udp_client(argv[], argv[], (void **) &sa, &salen); printf("sending to %s\n", Sock_ntop_host(sa, salen)); Sendto(sockfd, "", , , sa, salen); /* send 1-byte datagram */ n = Recvfrom(sockfd, recvline, MAXLINE, , NULL, NULL);
recvline[n] = '\0'; /* null terminate */
Fputs(recvline, stdout); exit();
}
udp_connect函数
udp_connect函数创建一个已连接UDP套接字
#include "unp.h"
int udp_connect(const char *host, const char *serv);
//若成功则为已连接套接字描述符,若出错则不返回
下面是该函数的源代码
#include "unp.h" int
udp_connect(const char *host, const char *serv)
{
int sockfd, n;
struct addrinfo hints, *res, *ressave; bzero(&hints, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != )
err_quit("udp_connect error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res; do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < )
continue; /* ignore this one */ if (connect(sockfd, res->ai_addr, res->ai_addrlen) == )
break; /* success */ Close(sockfd); /* ignore this one */
} while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno set from final connect() */
err_sys("udp_connect error for %s, %s", host, serv); freeaddrinfo(ressave); return(sockfd);
}
udp_server函数
本函数的参数与tcp_listen一样,有一个可选的hostname和一个必须的service(从而可以绑定其端口号),以及一个可选的指向某个变量的指针,用于返回套接字地址结构的大小
#include "unp.h"
int udp_server(const char *host, const char *serv, socklen_t *addrlenp);
//返回:若成功则为未连接套接字描述符,若出错则不返回
下面是该函数的源代码
#include "unp.h" int
udp_server(const char *host, const char *serv, socklen_t *addrlenp)
{
int sockfd, n;
struct addrinfo hints, *res, *ressave; bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != )
err_quit("udp_server error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res; do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < )
continue; /* error - try next one */ if (bind(sockfd, res->ai_addr, res->ai_addrlen) == )
break; /* success */ Close(sockfd); /* bind error - close and try next one */
} while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno from final socket() or bind() */
err_sys("udp_server error for %s, %s", host, serv); if (addrlenp)
*addrlenp = res->ai_addrlen; /* return size of protocol address */ freeaddrinfo(ressave); return(sockfd);
}
协议无关时间获取服务器程序
#include "unp.h"
#include <time.h> int
main(int argc, char **argv)
{
int sockfd;
ssize_t n;
char buff[MAXLINE];
time_t ticks;
socklen_t len;
struct sockaddr_storage cliaddr; if (argc == )
sockfd = Udp_server(NULL, argv[], NULL);
else if (argc == )
sockfd = Udp_server(argv[], argv[], NULL);
else
err_quit("usage: daytimeudpsrv [ <host> ] <service or port>"); for ( ; ; ) {
len = sizeof(cliaddr);
n = Recvfrom(sockfd, buff, MAXLINE, , (SA *)&cliaddr, &len);
printf("datagram from %s\n", Sock_ntop((SA *)&cliaddr, len)); ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Sendto(sockfd, buff, strlen(buff), , (SA *)&cliaddr, len);
}
}
getnameinfo函数
getnameinfo是getaddrinfo的互补函数,它以一个套接字地址为参数,返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串。
本函数以协议无关的方式提供这些信息。
#include <netdb.h>
int getnameinfo(const struct sockaddr *sockaddr,socklen_t addrlen,
char *host,socklen_t hostlen,
char *serv,socklen_t servlen,int flag);
flags参数是下面6个可指定的标志
可重入函数
gethostbyname函数不是一个可重入的函数。
关于可重入函数可以查看 http://www.cnblogs.com/runnyu/p/4643764.html
gethostbyname_r和gethostbyaddr_r函数提供了对原来两个函数的可重入版本
UNP学习笔记(第十一章 名字与地址转换)的更多相关文章
- o'Reill的SVG精髓(第二版)学习笔记——第十一章
第十一章:滤镜 11.1滤镜的工作原理 当SVG阅读器程序处理一个图形对象时,它会将对象呈现在位图输出设备上:在某一时刻,阅读器程序会把对象的描述信息转换为一组对应的像素,然后呈现在输出设备上.例如我 ...
- UNP第11章——名字与地址转换
1.域名系统 程序中只使用主机名和服务名的好处是,如果IP或端口变化,只需要改变映射关系,不需要重新编译程序. 1.1 资源记录 DNS的条目为资源记录,有用的项如下: A IPv4地址 AAAA I ...
- UNP学习笔记(第二章:传输层)
本章的焦点是传输层,包括TCP.UDP和SCTP. 绝大多数客户/服务器网络应用使用TCP或UDP.SCTP是一个较新的协议. UDP是一个简单的.不可靠的数据报协议.而TCP是一个复杂.可靠的字节流 ...
- 学习笔记 第十一章 CSS3布局基础
第11章 CSS3布局基础 [学习重点] 了解CSS2盒模型. 设计边框样式. 设计边界样式. 设计补白样式. 了解CSS3盒模型. 11.1 CSS盒模型基础 页面中所有元素基本显示形态为方形 ...
- [core java学习笔记][第十一章异常断言日志调试]
第11章 异常,断言,日志,调试 处理错误 捕获异常 使用异常机制的技巧 使用断言 日志 测试技巧 GUI程序排错技巧 使用调试器 11.1 处理错误 11.1.1异常分类 都继承自Throwable ...
- Head First Servlets & JSP 学习笔记 第十一章 —— Web应用部署
jar:java archive(java归档) war:web archive(web归档) war文件只是Web应用结构的一个快照,采用了一种更可移植的压缩形式(它实际上就是一个jar文件).建立 ...
- UNP学习笔记(第一章 简介)
环境搭建 1.下载解压unpv13e.tar.gz 2.进入目录执行 ./configurecd lib //进入lib目录make //执行make命令 3.将生成的libunp.a静态库复制到/u ...
- 《Python基础教程(第二版)》学习笔记 -> 第十一章 文件和素材
打开文件 open函数用来打开文件,语句如下: open(name[,mode[,buffering]]) open函数使用一个文件名作为唯一的强制参数,然后后返回一个文件对象.模式(mode)和缓冲 ...
- Linux学习笔记(第十一章)
文件系统及程序资源的配置ulimit: 环境变量: bash变量 alias设定变量别名 设定别名 取消别名 指令执行顺序 组合键 通配符 数据流重导向 多指令 以下命令都需用管道符链接: 截取命令: ...
随机推荐
- 【bzoj4589】Hard Nim FWT
题目描述 Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下: 1. Claris和NanoApe两个人轮流拿石子,Claris先拿. 2. 每次只能从一堆中取若干个,可将一堆全取走, ...
- js 判断对象类型
在企业级的开发中,我们常用 typeof 来判断企业 对象类型:但是 typeof 不能判断 Array 和 null 这里我们使用一个 原型上的 toString方法:请看一下代码: <scr ...
- Rust学习资源和路线
Rust学习资源和路线 来源 https://rust-lang-cn.org/article/23 学习资源 The Rust Programming Language 堪称Rust的"T ...
- oracle 修改数据 保险方法
oracle 中修改比较安全的方法:(pl/sql) 第一种方法: select * from temp where id=9 for update; 第二种方法: select t.*,rowid ...
- why switch kernel mode and user mode expensive
Because that means context switching(save context, restore context)
- dedeCMS php标签使用说明和数据库查询说明
1.{dede:php}标签想要输出信息 可以直接使用printf , echo,var_dump 之类的打印出来 赋值给@me 无效 2.结合sql语句使用方法 例:{dede:php} $ro ...
- Linux signal 那些事儿(2)【转】
转自:http://blog.chinaunix.net/uid-24774106-id-4064447.html 上一篇博文,基本算是给glibc的signal函数翻了个身.现在glibc的sign ...
- MySql视图笔记(转载)
1. 视图的定义 视图就是从一个或多个表中,导出来的表,是一个虚拟存在的表.视图就像一个窗口(数据展示的窗口),通过这个窗口,可以看到系统专门提供的数据(也可以查看到数据表的全部数据),使 ...
- (二)docker的部署安装,配置,基础命令
一.docker 的安装部署 这里不过多介绍,下面这两个linux发型版 安装可以参考 ubuntu的 docker-ce安装 centos7的 docker-ce安装 二.docker配置文件 重要 ...
- LeetCode OJ-- First Missing Positive
https://oj.leetcode.com/problems/first-missing-positive/ 给一列数,找出缺失的第一个正数.要求时间复杂度 O(n) 第一步遍历一遍,找出最大的数 ...