Internet domain socket
Internet domain 流 socket 是基于 TCP 的,它們提供了可靠的雙向字節(jié)流通信信道。
Internet domain 數(shù)據(jù)報 socket 是基于 UDP 的:
UNIX domain 數(shù)據(jù)報 socket 是可靠的,但是 UDP socket 則不是可靠的,數(shù)據(jù)報可能會丟失,重復(fù),亂序
在一個 UNIX domain 數(shù)據(jù)報 socket 上發(fā)送數(shù)據(jù)會在接收 socket 的數(shù)據(jù)隊列為滿時阻塞,與之不同的是,使用 UDP 時如果進(jìn)入的數(shù)據(jù)報會使接收者的隊列溢出,那么數(shù)據(jù)報就會靜默地被丟棄
網(wǎng)絡(luò)字節(jié)序
2 字節(jié)和 4 字節(jié)整數(shù)的大端和小端字節(jié)序:
網(wǎng)絡(luò)字節(jié)序采用大端。
INADDR_ANY 和 INADDR_LOOPBACK 是主機(jī)字節(jié)序,在將它們存儲進(jìn) socket 地址結(jié)構(gòu)中之前需要將這些值轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序。
主機(jī)序和網(wǎng)絡(luò)字節(jié)序之間的轉(zhuǎn)換:
#includeuint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);
數(shù)據(jù)表示
readLine() 從文件描述符 fd 引用的文件中讀取字節(jié)直到碰到換行符為止。
ssize_t readLine(int fd, void *buffer, size_t n) { ssize_t numRead; size_t toRead; char *buf; char ch; if(n <= 0 || buffer == NULL) ? { ? ? ? errno = EINVAL; ? ? ? return -1; ? } ? buf = buffer; ? toRead = 0; ? for (;;) ? { ? ? ? numRead = read(fd,&ch,1); ? ? ? if(numRead == -1) ? ? ? { ? ? ? ? ? if(errno == EINTR) ? ? ? ? ? ? ? continue; ? ? ? ? ? else ? ? ? ? ? ? ? return -1; ? ? ? } ? ? ? else if(numRead == 0) ? ? ? { ? ? ? ? ? if(toRead == 0) ? ? ? ? ? ? ? return 0; ? ? ? ? ? else ? ? ? ? ? ? ? break; ? ? ? } ? ? ? ? else ? ? ? { ? ? ? ? ? if(toRead < n-1) ? ? ? ? ? { ? ? ? ? ? ? ? toRead++; ? ? ? ? ? ? ? *buf++ = ch; ? ? ? ? ? } ? ? ? ? ? if(ch == ' ') ? ? ? ? ? ? ? break; ? ? ? } ? ? ? ? } ? *buf = '?'; ? return toRead; }
Internet socket 地址
Internet domain socket 地址有兩種:IPv4 和 IPv6。
IPv4 socket 地址
IPv4 地址存儲于結(jié)構(gòu)體 sockaddr_in 中:
struct in_addr { uint32_t s_addr; /* address in network byte order */ }; struct sockaddr_in{ sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; unsigned char __pad[X]; };
sin_family 總是 AF_INET
in_port_t 和 in_addr 分別是端口號和 IP 地址,它們都是網(wǎng)絡(luò)字節(jié)序,分別是 16 位和 32 位
IPv6 socket 地址
struct in6_addr{ uint8_t s6_addr[16]; }; struct sockaddr_in6{ sa_family_t sin6_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr; uint32_t sin6_scope_id; };
IPv6 的通配地址 0::0,換回地址為 ::1。
sockaddr_storage 結(jié)構(gòu)
IPv6 socket API 中新引入了一個通用的 sockaddr_storage 結(jié)構(gòu),這個結(jié)構(gòu)的空間足以容納任意類型的 socket:
#define __ss_aligntype uint32_t struct sockaddr_storage{ sa_family ss_family; __ss_aligntype __ss_slign; char __ss_padding[SS_PADSIZE]; };
主機(jī)和服務(wù)轉(zhuǎn)換函數(shù)概述
主機(jī)名和連接在網(wǎng)絡(luò)上的一個系統(tǒng)的符號標(biāo)識符,服務(wù)名是端口號的符號表示。主機(jī)地址和端口的表示有下列兩種:
主機(jī)地址和端口的表示有兩種方法:
主機(jī)地址可以表示為一個二進(jìn)制值或一個符號主機(jī)名或展現(xiàn)格式(IPv4 點分十進(jìn)制,IPv6 是十六進(jìn)制字符串)
端口號可以表示為一個二進(jìn)制值或一個符號服務(wù)名
inet_pton() 和 inet_ntop() 函數(shù)
#includeint inet_pton(int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
p 表示展現(xiàn) presentation ,n 表示 網(wǎng)絡(luò) network
inet_pton() 將 src 包含的展現(xiàn)字符串轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序的二進(jìn)制 IP 地址,af 被指定為 AF_INET 或者 AF_INET6
inet_ntop() 執(zhí)行逆向轉(zhuǎn)換, size 被指定為緩沖器的大小,如果 size 太小,那么 inet_ntop() 會返回 NULL 并將 errno 設(shè)置成 ENOSPC
緩沖器大小可以使用下面兩個宏指定:
#include#define INET_ADDRSTRLEN 16 #define INET6_ADDRSTRLEN 46
數(shù)據(jù)報 socket 客戶端/服務(wù)器示例
server
int main(int argc,char* argv[]) { struct sockaddr_in6 svaddr, claddr; int sfd, j; ssize_t numBytes; socklen_t len; char buf[BUF_SIZE]; char claddrStr[INET_ADDRSTRLEN]; sfd = socket(AF_INET6,SOCK_DGRAM,0); if(sfd ==-1) errExit("socket()"); memset(&svaddr,0,sizeof(struct sockaddr_in6)); svaddr.sin6_family = AF_INET6; svaddr.sin6_addr = in6addr_any; svaddr.sin6_port = htons(PROT_NUM); if(bind(sfd,(struct sockaddr_in6*)&svaddr,sizeof(struct sockaddr_in6)) == -1) errExit("bind()"); for (;;) { len = sizeof(struct sockaddr_in6); numBytes = recvfrom(sfd,buf,BUF_SIZE,0,(struct sockaddr*)&claddr,&len); if(numBytes == -1) errExit("recvfrom()"); if (inet_ntop(AF_INET6, &claddr.sin6_addr, claddrStr,INET6_ADDRSTRLEN) == NULL) printf("could not convert client address to string "); else printf("Sever received %ld bytes from (%s,%u) ",(long)numBytes,claddr,ntohs(claddr.sin6_port)); if(sendto(sfd,buf,numBytes,0,(struct sockaddr*)&claddr,len) != numBytes) errExit("sendto()"); } }
client
int main(int argc,char* argv[]) { struct sockaddr_in6 svaddr, claddr; int sfd, j; size_t msgLen; ssize_t numBytes; char resp[BUF_SIZE]; if(argc < 3 | strcmp(argv[1],"--help") == 0) ? { ? ? ? printf("%s host-address msg...",argv[0]); ? ? ? exit(EXIT_SUCCESS); ? } ? sfd = socket(AF_INET6,SOCK_DGRAM,0); ? if(sfd ==-1) ? ? ? errExit("socket()"); ? memset(&svaddr,0,sizeof(struct sockaddr_in6)); ? svaddr.sin6_family = AF_INET6; ? svaddr.sin6_port = htons(PROT_NUM); ? if(inet_pton(AF_INET6,argv[1],&svaddr.sin6_addr) <= 0) ? ? ? errExit("inet_pton()"); ? for (j = 2; j < argc;j++) ? { ? ? ? msgLen = strlen(argv[j]); ? ? ? if (sendto(sfd,argv[j],msgLen,0,(struct sockaddr*)&svaddr,sizeof(struct sockaddr_in6)) != msgLen) ? ? ? ? ? errExit("sendto()"); ? ? ? numBytes = recvfrom(sfd,resp,BUF_SIZE,0,NULL,NULL); ? ? ? if(numBytes == -1) ? ? ? ? ? errExit("recvfrom()"); ? ? ? printf("Respone %d : %.*s ",j-1,(int)numBytes,resp); ? } ? exit(EXIT_SUCCESS); }
域名系統(tǒng)(DNS)
DNS 出現(xiàn)以前,主機(jī)名和 IP 地址之間的映射關(guān)系是在一個手工維護(hù)的本地文件 /etc/hosts 中進(jìn)行定義的:
127.0.0.1 localhost ::1 ip6-localhost ip6-loopback
gethostbyname() 或者 getaddrinfo() 通過搜索這個文件并找出與規(guī)范主機(jī)名或其中一個別名匹配的記錄來獲取一個 IP 地址。
DNS 設(shè)計:
將主機(jī)名組織在一個層級名空間中,每個節(jié)點有一個標(biāo)簽,該標(biāo)簽最多能包含 63 個字符,層級的根是一個無名的節(jié)點,稱為 "匿名節(jié)點"
一個節(jié)點的域名由該節(jié)點到根節(jié)點的路徑中所有節(jié)點的名字連接而成,各個名字之間使用 . 分隔
完全限定域名,如 www.kernel.org. ,標(biāo)識出了層級中的一臺主機(jī),區(qū)分一個完全限定域名的方法是看名字是否以.結(jié)尾,但是在很多情況下這個點會被省略
沒有一個組織或系統(tǒng)會管理整個層級,相反,存在一個 DNS 服務(wù)器層級,每臺服務(wù)器管理樹的一個分支(區(qū)域)
當(dāng)一個程序調(diào)用 getaddrinfo() 來解析一個域名時,getaddrinfo() 會使用一組庫函數(shù)來與各地的 DNS 服務(wù)器通信,如果這個服務(wù)器無法提供所需要的信息,那么它就會與位于層級中的其他 DNS 服務(wù)器進(jìn)行通信以便獲取信息,這個過程可能花費很多時間,DNS 采用了緩存技術(shù)以節(jié)省時間
遞歸和迭代的解析請求
DNS 解析請求可以分為:遞歸和迭代。
遞歸請求:請求者要求服務(wù)器處理整個解析任務(wù),包括在必要時候與其它 DNS 服務(wù)器進(jìn)行通信任務(wù)。當(dāng)位于本地主機(jī)上的一個應(yīng)用程序調(diào)用 getaddrinfo() 時,該函數(shù)會與本地 DNS 服務(wù)器發(fā)起一個遞歸請求,如果本地 DNS 服務(wù)器自己沒有相關(guān)信息來完成解析,那么它就會迭代地解析這個域名。
迭代解析:假設(shè)要解析 www.otago.ac.nz,首先與每個 DNS 服務(wù)器都知道的一小組根名字服務(wù)器中的一個進(jìn)行通信,根名字服務(wù)器會告訴本 DNS 服務(wù)器到其中一臺 nz DNS 服務(wù)器上查詢,然后本地 DNS 服務(wù)器會在 nz 服務(wù)器上查詢名字 www.otago.ac.nz,并收到一個到 ac.nz 服務(wù)器上查詢的響應(yīng),之后本地 DNS 服務(wù)器會在 ac.nz 服務(wù)器上查詢名字 www.otago.ac.nz 并告知查詢 otago.ac.nz 服務(wù)器,最后本地 DNS 服務(wù)器會在 otago.ac.nz 服務(wù)器上查詢 www.otago.ac.nz 并獲取所需的 IP 地址。
向 gethostbyname() 傳遞一個不完整的域名,那么解析器在解析之前會嘗試補齊。域名補全規(guī)則在 /etc/resolv.conf 中定義,默認(rèn)情況下,至少會使用本機(jī)的域名來補全,例如,登錄機(jī)器 oghma.otago.ac.nz 并輸入 ssh octavo 得到的 DNS 查詢將會以 octavo.otago.ac.nz 作為其名字。
頂級域
緊跟在匿名根節(jié)點下面的節(jié)點稱為頂級域(TLD),TLD 分為兩類:通用的和國家的。
/etc/services 文件
端口號和服務(wù)名記錄在 /etc/services 中,getaddrinfo() 和 getnameinfo() 會使用這個文件中的信息在服務(wù)名和端口號之間進(jìn)行轉(zhuǎn)換。
獨立于協(xié)議的主機(jī)和服務(wù)轉(zhuǎn)換
getaddrinfo() 將主機(jī)和服務(wù)名轉(zhuǎn)換成 IP 地址和端口號,它作為過時的 gethostbyname() 和 getservername() 接替者。
getnameinfo() 是 getaddrinfo() 的逆函數(shù),將一個 socket 地址結(jié)構(gòu)轉(zhuǎn)換成包含對應(yīng)主機(jī)和服務(wù)名的字符串,是過時的 gethostbyaddr() 和 getserverbyport() 的等價物。
getaddrinfo() 函數(shù)
#include#include #include int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints,struct addrinfo **res);
給定一個主機(jī)名和服務(wù)器名,getaddrinfo() 返回一個 socket 地址結(jié)構(gòu)列表,每個結(jié)構(gòu)都包含一個地址和端口號
成功時返回0,失敗時返回非 0
host 包含一個主機(jī)名或者一個以 IPv4 點分十進(jìn)制標(biāo)記或者 IPv6 十六進(jìn)制字符串標(biāo)記的數(shù)值地址字符串
service 包含一個服務(wù)名或一個十進(jìn)制端口號
hints 指向一個 addrinfo 結(jié)構(gòu):
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; };
res 返回一個結(jié)構(gòu)列表而不是單個結(jié)構(gòu),因為與在 host、 service、 hints、 指定的標(biāo)準(zhǔn)對應(yīng)的主機(jī)和服務(wù)組合可能有多個。例如,查詢多個網(wǎng)絡(luò)接口的主機(jī)時可能返回多個地址結(jié)構(gòu),此外,如果將 hints.ai_sockettype 指定 為0,那么可能返回兩個結(jié)構(gòu):一個用于 SOCK_DGRAM socket 和 SOCKET_STREAM socket,前提是給定的 service 同時對 TCP 和 UDP 可用
hints 參數(shù)
hints 參數(shù)為如何選擇 getaddrinfo() 返回的 socket 地址結(jié)構(gòu)指定了更多標(biāo)準(zhǔn)。當(dāng)用作 hints 參數(shù)時只能設(shè)置 addrinfo 結(jié)構(gòu)的 ai_flags、ai_family、ai_socktype、ai_protocol 字段,其他字段未使用,并將根據(jù)具體情況初始化為 0 或者 NULL。
hints.ai_family 返回的 socket 地址結(jié)構(gòu)的域,取值可以是 AF_INET 或者 AF_INET6。如果需要獲取所有種類 socket 地址,那么可以將這個字段設(shè)置為 AF_UNSPEC。
hints.ai_socktype 字段指定了使用返回的 socket 地址結(jié)構(gòu)的 socket 類型。如果將這個字段指定為 SOCK_DGRAM,那么查詢將會在 UDP 服務(wù)上執(zhí)行,如果指定了 SOCK_STREAM,那么將返回一個 TCP 服務(wù)查詢,如果將其指定為 0,那么任意類型的 socket 都是可接受的。
hints.ai_protocol 字段為返回的地址結(jié)構(gòu)選擇了 socket 協(xié)議,這個字段設(shè)置為 0,表示調(diào)用者接受任何協(xié)議。
hints.ai_flags 字段是一個位掩碼,它會改變 getaddrinfo() 的行為,取值為:
AI_ADDRCONFIG:在本地系統(tǒng)上至少配置一個 IPv4 地址時返回 IPv4 地址(不是 IPv4 回環(huán)地址),在本地系統(tǒng)上至少配置一個 IPv6 地址時返回 IPv6 地址(不是 IPv6 回環(huán)地址)
AI_ALL:參見 AI_V4MAPPED
AI_CANONNAME:如果 host 不為 NULL,返回一個指向 null 結(jié)尾的字符串,該字符串包含了主機(jī)的規(guī)范名,這個指針會在通過 result 返回的第一個 addrinfo 結(jié)構(gòu)中 ai_canoname 字段指向的緩沖器中返回
AI_NUMERICHOST:強(qiáng)制將 host 解釋成一個數(shù)值地址字符串,這個常量用于在不必要解析名字時防止進(jìn)行名字解析,因為名字解析可能會花費比較長的時間
AI_NUMERICSERV:將 service 解釋成一個數(shù)值端口號,這個標(biāo)記用于防止調(diào)用任意的名字解析服務(wù),因為當(dāng) service 為一個數(shù)值字符串時這種調(diào)用是沒有必要的
AI_PASSIVE:返回一個適合進(jìn)行被動式打開(即一個監(jiān)聽 socket)的 socket 地址結(jié)構(gòu),此時,host 應(yīng)該是 NULL,通過 result 返回的 socket 地址結(jié)構(gòu) IP 地址部分將會包含一耳光通配 IP 地址(INADDR_ANY 或者 IN6ADDR_ANY_INIT)。如果沒有設(shè)置這個標(biāo)記,那么通過 res 返回的地址結(jié)構(gòu)中的 IP 地址將會被設(shè)置成回環(huán) IP 地址(INADDR_LOOPBACK 或者 IN6ADDR_LOOPBACK_INIT)
AI_V4MAPPED:如果在 hints 的 ai_family 中指定了 AF_INET6,那么在沒有找到匹配的 IPv6 地址時應(yīng)該在 res 返回 IPv4 映射的 IPv6 地址。如果同時指定了AI_ALL 和 AI_V4MAPPED,那么 res 中同時返回 IPv6 和 IPv4 地址,IPv4 地址會被返回成 IPv4 映射的 IPv6 地址
釋放 addrinfo 列表 freeaddrinfo()
#include#include #include void freeaddrinfo(struct addrinfo *res);
getaddrinfo() 函數(shù)會動態(tài)地為 res 引用的所有結(jié)構(gòu)分配內(nèi)存,其結(jié)果是調(diào)用者必須要在不需要使用這些結(jié)構(gòu)時釋放它們,使用 freeaddrinfo() 來執(zhí)行釋放的任務(wù)
錯誤診斷 gai_strerror()
getaddrinfo() 在發(fā)生錯誤時返回下面的一個錯誤碼:
#include#include #include const char *gai_strerror(int errcode);
gai_strerror() 返回一個描述錯誤的字符串
getnameinfo() 函數(shù)
getnameinfo() 是 getaddrinfo() 的逆函數(shù)。給定一個 socket 地址結(jié)構(gòu)它會返回一個包含對應(yīng)的主機(jī)和服務(wù)名的字符串或者無法解析名字時返回一個等價的數(shù)值。
#include#include int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,char *host, socklen_t hostlen,char *service, socklen_t servlen, int flags);
addr 指向待轉(zhuǎn)換的 socket 地址結(jié)構(gòu),長度為 addrlen
得到的主機(jī)和服務(wù)名是以 null 結(jié)尾的字符串,它們會被存儲在 host 和 service 指向的緩沖器中,調(diào)用者必須要為這些緩沖器分配空間并將它們的大小傳入 hostlen 和 servlen ,NI_MAXHOST 指出了返回的主機(jī)名字符串的最大字節(jié)數(shù),其取值為 1025,NI_MAXSERV 指出了返回服務(wù)名字符串的最大字節(jié)數(shù),其取值為 32
如果不想獲取主機(jī)名,可以將 host 指定為 NULL 并且將 hostlen 指定為 0,如果不想獲取服務(wù)名,可以將 service 指定為 NULL 并且將 servlen 指定為 0,但是 host 和 service 中至少有一個必須非 NULL
flags 是一個掩碼,控制著 getnameinfo() 的行為,取值為:
NI_DGRAM:默認(rèn)情況下,getnameinfo() 返回 TCP 服務(wù)對應(yīng)的名字,NI_DGRAM 標(biāo)記強(qiáng)制返回 UDP 服務(wù)的名字
NI_NAMEREQD:默認(rèn)情況下,如果無法解析主機(jī)名,那么在 host 中返回一個數(shù)值地址字符串,如果指定了 NI_NAMEREQD,就會返回一個錯誤 EAI_NONAME
NI_NOFQDN:在默認(rèn)情況下會返回主機(jī)的完全限定域名,指定 NI_NOFQDN 標(biāo)記會導(dǎo)致當(dāng)主機(jī)位于局域網(wǎng)中時只返回名字的第一部分(即主機(jī)名)
NI_NUMERICHOST:強(qiáng)制在 host 中返回一個數(shù)值地址字符串,這個標(biāo)記在需要避免可能耗時較長的 DNS 服務(wù)器調(diào)用時是比較有用的
NI_NUMERICSERV:強(qiáng)制在 service 中返回一個十進(jìn)制端口號字符串,這個標(biāo)記在知道端口號不對應(yīng)于服務(wù)器名時,如它是一個由內(nèi)核分配給 socket 的臨時端口號,以及需要避免不必要的搜索 /etc/service 的低效性時是比較有用的
流式 socket 客戶端/服務(wù)器示例
server
int main(int argc,char* argv[]) { uint32_t seqNum; char reqLenStr[INT_LEN]; char seqNumStr[INT_LEN]; struct sockaddr_storage claddr; int lfd, cfd, optval, reqLen; socklen_t addrlen; struct addrinfo hints; struct addrinfo *result, *rp; #define ADDRSTRLEN (NI_MAXHOST + NI_MAXSERV +10) char addrStr[ADDRSTRLEN]; char host[NI_MAXHOST]; char service[NI_MAXSERV]; if(argc > 1 && strcmp(argv[1],"--help") == 0) { printf("%s [init-seq-num] ",argv[0]); exit(EXIT_SUCCESS); } seqNum = (argc > 1) ? atoi(argv[1]) : 0; if(signal(SIGPIPE,SIG_IGN) == SIG_ERR) errExit("signal()"); memset(&hints,0,sizeof(struct addrinfo)); hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; if(getaddrinfo(NULL,PORT_NUM,&hints,&result) != 0) errExit("getaddrinfo()"); optval = 1; for (rp = result; rp != NULL;rp = rp->ai_next) { lfd = socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol); if(lfd == -1) continue; if(setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1) errExit("setsockopt()"); if(bind(lfd,rp->ai_addr,rp->ai_addrlen) == 0) break; close(lfd); } if(rp == NULL) errExit("could not bind socket any address"); if(listen(lfd,BACKLOG) == -1) errExit("listen()"); freeaddrinfo(result); for (;;) { addrlen = sizeof(struct sockaddr_storage); cfd = accept(lfd,(struct sockaddr*)&claddr,&addrlen); if(cfd == -1) { printf("accept "); continue; } if(getnameinfo((struct sockaddr*)&claddr,addrlen,host,NI_MAXHOST,service,NI_MAXSERV,0) == 0) snprintf(addrStr,ADDRSTRLEN,"(%s,%s)",host,service); else snprintf(addrStr,ADDRSTRLEN,"(?UNKNOWN?)"); if(readLine(cfd,reqLenStr,INT_LEN) <= 0) ? ? ? { ? ? ? ? ? close(cfd); ? ? ? ? ? continue; ? ? ? } ? ? ? snprintf(seqNumStr,INT_LEN,"%d ",seqNum); ? ? ? if(write(cfd,&seqNumStr,strlen(seqNumStr)) != strlen(seqNumStr)) ? ? ? ? ? printf("Error on write "); ? ? ? seqNum += reqLen; ? ? ? if(close(cfd) == -1) ? ? ? ? ? printf("Error on close"); ? } }
client
int main(int argc,char* argv[]) { char *reqLenStr; char seqNumStr[INT_LEN]; int cfd; ssize_t numRead; struct addrinfo hints; struct addrinfo *result, *rp; if(argc < 2 || strcmp(argv[1],"--help") == 0) ? { ? ? ? printf("%s server-host [sequence-len] ",argv[0]); ? ? ? exit(EXIT_SUCCESS); ? } ? memset(&hints,0,sizeof(struct addrinfo)); ? hints.ai_canonname = NULL; ? hints.ai_addr = NULL; ? hints.ai_next = NULL; ? hints.ai_socktype = SOCK_STREAM; ? hints.ai_family = AF_UNSPEC; ? hints.ai_flags = AI_NUMERICSERV; ? if(getaddrinfo(NULL,PORT_NUM,&hints,&result) != 0) ? ? ? errExit("getaddrinfo()"); ? for (rp = result; rp != NULL;rp = rp->ai_next) { cfd = socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol); if(cfd == -1) continue; if(connect(cfd,rp->ai_addr,rp->ai_addrlen) != -1) break; close(cfd); } if(rp == NULL) errExit("could not bind socket any address"); freeaddrinfo(result); reqLenStr = (argc > 2) ? argv[2] : "1"; if(write(cfd,reqLenStr,strlen(reqLenStr)) != strlen(reqLenStr)) errExit("write()"); if(write(cfd," ",1) != 1) errExit("write()"); numRead = readLine(cfd, seqNumStr, INT_LEN); if(numRead == -1) errExit("readLine()"); if(numRead == 0) errExit("Unexpected EOF from server"); printf("Sequence number: %s ",seqNumStr); exit(EXIT_SUCCESS); }
Internet domain socket 庫
int inetConnect(const char *host, const char *service, int type) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s; memset(&hints,0,sizeof(struct addrinfo)); hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; hints.ai_socktype = type; hints.ai_family = AF_UNSPEC; s = getaddrinfo(host, service, &hints, &result); if(s != 0) { errno = ENOSYS; return -1; } for (rp = result; rp != NULL;rp = rp->ai_next) { sfd = socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol); if(sfd == -1) continue; if(connect(sfd,rp->ai_addr,rp->ai_addrlen) != -1) break; close(sfd); } freeaddrinfo(result); return rp == NULL ? -1 : sfd; } static int inetPassiveSocket(const char* service,int type,socklen_t* addrLen,Boolean doListen,int backlog) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s, optval; memset(&hints,0,sizeof(struct addrinfo)); hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; hints.ai_socktype = type; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_PASSIVE; s = getaddrinfo(NULL, service, &hints, &result); if(s != 0) { return -1; } optval = 1; for (rp = result; rp != NULL;rp = rp->ai_next) { sfd = socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol); if(sfd == -1) continue; if(doListen) { if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1) { close(sfd); freeaddrinfo(result); return -1; } } if(bind(sfd,rp->ai_addr,rp->ai_addrlen) ==0) break; close(sfd); } if(rp != NULL && addrLen != NULL) { *addrLen = rp->ai_addrlen; } freeaddrinfo(result); return rp == NULL ? -1 : sfd; } int inetListen(const char *service, int backlog, socklen_t *addrlen) { return inetPassiveSocket(service,SOCK_STREAM,addrlen,True,backlog); } int inetBind(const char *service, int type, socklen_t *addrlen) { return inetPassiveSocket(service,type,addrlen,False,0); } char *inetAddressStr(const struct sockaddr *addr, socklen_t addrLen, char *addrStr, int addrStrLen) { char host[NI_MAXHOST], service[NI_MAXSERV]; if(getnameinfo(addr,addrLen,host,NI_MAXHOST,service,NI_MAXSERV,NI_NUMERICSERV) == 0) snprintf(addrStr,addrStrLen,("%s,%s"),host,service); else snprintf(addrStr,addrStrLen,"?UNKNOWN?"); addrStr[addrLen - 1] = '?'; return addrStr; }
過時的主機(jī)和服務(wù)轉(zhuǎn)換 API
inet_aton() 和 inet_ntoa()
#include#include #include int inet_aton(const char *str, struct in_addr *addr);
inet_aton() 將 str 指向的點分十進(jìn)制字符串轉(zhuǎn)換成一個網(wǎng)絡(luò)字節(jié)序的 IPv4 地址,轉(zhuǎn)換得到的地址將會返回 addr 指向的結(jié)構(gòu)
成功時返回 1,str 無效時返回 0
str 字符串的數(shù)值部分無需是十進(jìn)制的,它可以是八進(jìn)制的,也可以是十六進(jìn)制的
#include#include #include char *inet_ntoa(struct in_addr in);
inet_ntoa() 返回一個指向包含用點分十進(jìn)制標(biāo)記法標(biāo)記的地址的字符串指針
inet_ntoa() 返回的字符串是靜態(tài)分配的,因此會被后續(xù)調(diào)用所覆蓋
gethostbyname() 和 gethostbyaddr()
#includeextern int h_errno; struct hostent *gethostbyname(const char *name); struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);
gethostbyname() 解析由 name 給出的主機(jī)名,并返回一個指向靜態(tài)分配的包含了主機(jī)名相關(guān)信息的 hostent 結(jié)構(gòu)的指針
gethostbyaddr() 執(zhí)行 gethostbyname() 的逆操作,給定一個二進(jìn)制 IP 地址,它會返回一個包含與配置了該地址的主機(jī)相關(guān)的信息的 hostent 結(jié)構(gòu)
發(fā)生錯誤時,或者無法解析一個名字時,gethostbyname() 和 gethostbyaddr() 都會返回 NULL,并設(shè)置全局變量 h_errno。這個變量類似于 errno,herror() 和 hstrerror() 類似于 perror() 和 strerror()
#includeextern int h_errno; void herror(const char *s); const char *hstrerror(int err);
hostent 結(jié)構(gòu):
struct hostent { char *h_name; /* official 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 */ } #define h_addr h_addr_list[0] /* for backward compatibility */
h_name 返回主機(jī)的官方名字,是一個以 null 結(jié)尾的字符串
h_aliases 指向一個指針數(shù)組,數(shù)組中的指針指向以 null 結(jié)尾的包含了主機(jī)名的別名的字符串
h_addr_list 是一個指針數(shù)組,數(shù)組中的指針指向這個主機(jī)的 IP 地址結(jié)構(gòu),這個列表由 in_addr 和 in6_addr 結(jié)構(gòu)構(gòu)成,通過 h_addrtype 字段可以確定這些結(jié)構(gòu)的類型,其取值為 AF_INET 或 AF_INET6,h_length 字段可以確定這些結(jié)構(gòu)的長度
getservbyname() 和 getservbyport()
#includestruct servent *getservbyname(const char *name, const char *proto);
getservbyname() 和 getservbyport() 都是從 /etc/services 文件中獲取記錄,現(xiàn)在已經(jīng)被 getaddrinfo() 和 getnameinfo() 取代
getservbyname() 查詢服務(wù)名或者其中一個別名與 name 匹配以及協(xié)議與 proto 匹配的記錄,proto 可以是 TCP 或者 UDP,或者設(shè)置為 NULL
如果找到了一個匹配的記錄,那么 getservbyname() 會返回一個指向靜態(tài)分配的結(jié)構(gòu)指針:
struct servent { char *s_name; /* official service name */ char **s_aliases; /* alias list */ int s_port; /* port number */ char *s_proto; /* protocol to use */ };
一般調(diào)用 getservbyname() 只是為了獲取端口號,即 s_port 字段
#includestruct servent *getservbyport(int port, const char *proto);
getservbyport() 執(zhí)行 getservbyname() 的逆操作,它返回一個 servent 記錄,該記錄包含了 /etc/services 文件中端口號與 port 匹配的記錄相關(guān)的信息
UNIX 與 Internet domain socket 比較
編寫只使用 Internet domain socket 的應(yīng)用程序即可以運行在同一主機(jī)上,也可以運行在網(wǎng)絡(luò)中的不同主機(jī)上。
UNIX domain socket 只能用于同一系統(tǒng)上的應(yīng)用程序間通信,使用 UNIX domain socket 的幾個原因:
在一些實現(xiàn)上,UNIX domain socket 速度要比 Internet domain socket 快
可以使用目錄權(quán)限來控制對 UNIX domain socket 的訪問,這樣只有運行于指定的用戶或組 ID 下的應(yīng)用程序才能夠連接到一個監(jiān)聽流 socket 或向一個數(shù)據(jù)報 socket 發(fā)送一個數(shù)據(jù)報。
審核編輯:劉清
-
緩沖器
+關(guān)注
關(guān)注
6文章
2037瀏覽量
46566 -
Linux系統(tǒng)
+關(guān)注
關(guān)注
4文章
603瀏覽量
28292 -
DNS
+關(guān)注
關(guān)注
0文章
225瀏覽量
20263 -
TCP通信
+關(guān)注
關(guān)注
0文章
146瀏覽量
4459
原文標(biāo)題:Linux Internet Domain應(yīng)用編程
文章出處:【微信號:嵌入式應(yīng)用研究院,微信公眾號:嵌入式應(yīng)用研究院】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
[推薦]linux下的c語言編程簡介
Linux網(wǎng)絡(luò)編程教材
精通嵌入式Linux編程
Voice over Internet Protocol
Time Domain Reflectometry Theo
EMC宣布收購Data Domain
Internet的簡介
ATM網(wǎng)絡(luò)和Internet融合技術(shù)簡介
LINUX網(wǎng)絡(luò)編程
Linux網(wǎng)絡(luò)編程
關(guān)于Linux的Internet安全漏洞與防范措施詳解
Linux教程之Linux命令、編程器、Shell編程、實例大全pdf免費下載

IRQ domain支持幾種映射方式

評論