色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux Internet Domain應用編程簡介

嵌入式應用研究院 ? 來源:TLPI編程筆記 ? 2023-05-26 14:05 ? 次閱讀

Internet domain socket

Internet domain 流 socket 是基于 TCP 的,它們提供了可靠的雙向字節流通信信道。

Internet domain 數據報 socket 是基于 UDP 的:

UNIX domain 數據報 socket 是可靠的,但是 UDP socket 則不是可靠的,數據報可能會丟失,重復,亂序

在一個 UNIX domain 數據報 socket 上發送數據會在接收 socket 的數據隊列為滿時阻塞,與之不同的是,使用 UDP 時如果進入的數據報會使接收者的隊列溢出,那么數據報就會靜默地被丟棄

網絡字節序

2 字節和 4 字節整數的大端和小端字節序:

4ae9ef3a-fb84-11ed-90ce-dac502259ad0.png

網絡字節序采用大端。

INADDR_ANY 和 INADDR_LOOPBACK 是主機字節序,在將它們存儲進 socket 地址結構中之前需要將這些值轉換成網絡字節序。

主機序和網絡字節序之間的轉換:

#include 

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

數據表示

readLine() 從文件描述符 fd 引用的文件中讀取字節直到碰到換行符為止。

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 地址存儲于結構體 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 地址,它們都是網絡字節序,分別是 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 結構

IPv6 socket API 中新引入了一個通用的 sockaddr_storage 結構,這個結構的空間足以容納任意類型的 socket:

#define __ss_aligntype uint32_t
struct sockaddr_storage{
    sa_family ss_family;
    __ss_aligntype __ss_slign;
    char __ss_padding[SS_PADSIZE];
};

主機和服務轉換函數概述

主機名和連接在網絡上的一個系統的符號標識符,服務名是端口號的符號表示。主機地址和端口的表示有下列兩種:

主機地址和端口的表示有兩種方法:

主機地址可以表示為一個二進制值或一個符號主機名或展現格式(IPv4 點分十進制,IPv6 是十六進制字符串)

端口號可以表示為一個二進制值或一個符號服務名

inet_pton() 和 inet_ntop() 函數

#include 

int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

p 表示展現 presentation ,n 表示 網絡 network

inet_pton() 將 src 包含的展現字符串轉換成網絡字節序的二進制 IP 地址,af 被指定為 AF_INET 或者 AF_INET6

inet_ntop() 執行逆向轉換, size 被指定為緩沖器的大小,如果 size 太小,那么 inet_ntop() 會返回 NULL 并將 errno 設置成 ENOSPC

緩沖器大小可以使用下面兩個宏指定:

#include 

#define INET_ADDRSTRLEN      16
#define INET6_ADDRSTRLEN     46

數據報 socket 客戶端/服務器示例

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);
}

域名系統(DNS)

DNS 出現以前,主機名和 IP 地址之間的映射關系是在一個手工維護的本地文件 /etc/hosts 中進行定義的:

127.0.0.1    localhost
::1   ip6-localhost ip6-loopback

gethostbyname() 或者 getaddrinfo() 通過搜索這個文件并找出與規范主機名或其中一個別名匹配的記錄來獲取一個 IP 地址。

DNS 設計:

4af6fd74-fb84-11ed-90ce-dac502259ad0.png

將主機名組織在一個層級名空間中,每個節點有一個標簽,該標簽最多能包含 63 個字符,層級的根是一個無名的節點,稱為 "匿名節點"

一個節點的域名由該節點到根節點的路徑中所有節點的名字連接而成,各個名字之間使用 . 分隔

完全限定域名,如 www.kernel.org. ,標識出了層級中的一臺主機,區分一個完全限定域名的方法是看名字是否以.結尾,但是在很多情況下這個點會被省略

沒有一個組織或系統會管理整個層級,相反,存在一個 DNS 服務器層級,每臺服務器管理樹的一個分支(區域)

當一個程序調用 getaddrinfo() 來解析一個域名時,getaddrinfo() 會使用一組庫函數來與各地的 DNS 服務器通信,如果這個服務器無法提供所需要的信息,那么它就會與位于層級中的其他 DNS 服務器進行通信以便獲取信息,這個過程可能花費很多時間,DNS 采用了緩存技術以節省時間

遞歸和迭代的解析請求

DNS 解析請求可以分為:遞歸和迭代。

遞歸請求:請求者要求服務器處理整個解析任務,包括在必要時候與其它 DNS 服務器進行通信任務。當位于本地主機上的一個應用程序調用 getaddrinfo() 時,該函數會與本地 DNS 服務器發起一個遞歸請求,如果本地 DNS 服務器自己沒有相關信息來完成解析,那么它就會迭代地解析這個域名。

迭代解析:假設要解析 www.otago.ac.nz,首先與每個 DNS 服務器都知道的一小組根名字服務器中的一個進行通信,根名字服務器會告訴本 DNS 服務器到其中一臺 nz DNS 服務器上查詢,然后本地 DNS 服務器會在 nz 服務器上查詢名字 www.otago.ac.nz,并收到一個到 ac.nz 服務器上查詢的響應,之后本地 DNS 服務器會在 ac.nz 服務器上查詢名字 www.otago.ac.nz 并告知查詢 otago.ac.nz 服務器,最后本地 DNS 服務器會在 otago.ac.nz 服務器上查詢 www.otago.ac.nz 并獲取所需的 IP 地址。

向 gethostbyname() 傳遞一個不完整的域名,那么解析器在解析之前會嘗試補齊。域名補全規則在 /etc/resolv.conf 中定義,默認情況下,至少會使用本機的域名來補全,例如,登錄機器 oghma.otago.ac.nz 并輸入 ssh octavo 得到的 DNS 查詢將會以 octavo.otago.ac.nz 作為其名字。

頂級域

緊跟在匿名根節點下面的節點稱為頂級域(TLD),TLD 分為兩類:通用的和國家的。

/etc/services 文件

端口號和服務名記錄在 /etc/services 中,getaddrinfo() 和 getnameinfo() 會使用這個文件中的信息在服務名和端口號之間進行轉換。

獨立于協議的主機和服務轉換

getaddrinfo() 將主機和服務名轉換成 IP 地址和端口號,它作為過時的 gethostbyname() 和 getservername() 接替者。

getnameinfo() 是 getaddrinfo() 的逆函數,將一個 socket 地址結構轉換成包含對應主機和服務名的字符串,是過時的 gethostbyaddr() 和 getserverbyport() 的等價物。

getaddrinfo() 函數

#include 
#include 
#include 

int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints,struct addrinfo **res);

給定一個主機名和服務器名,getaddrinfo() 返回一個 socket 地址結構列表,每個結構都包含一個地址和端口號

成功時返回0,失敗時返回非 0

host 包含一個主機名或者一個以 IPv4 點分十進制標記或者 IPv6 十六進制字符串標記的數值地址字符串

service 包含一個服務名或一個十進制端口號

hints 指向一個 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;
};

res 返回一個結構列表而不是單個結構,因為與在 host、 service、 hints、 指定的標準對應的主機和服務組合可能有多個。例如,查詢多個網絡接口的主機時可能返回多個地址結構,此外,如果將 hints.ai_sockettype 指定 為0,那么可能返回兩個結構:一個用于 SOCK_DGRAM socket 和 SOCKET_STREAM socket,前提是給定的 service 同時對 TCP 和 UDP 可用

4b02f386-fb84-11ed-90ce-dac502259ad0.png

hints 參數

hints 參數為如何選擇 getaddrinfo() 返回的 socket 地址結構指定了更多標準。當用作 hints 參數時只能設置 addrinfo 結構的 ai_flags、ai_family、ai_socktype、ai_protocol 字段,其他字段未使用,并將根據具體情況初始化為 0 或者 NULL。

hints.ai_family 返回的 socket 地址結構的域,取值可以是 AF_INET 或者 AF_INET6。如果需要獲取所有種類 socket 地址,那么可以將這個字段設置為 AF_UNSPEC。

hints.ai_socktype 字段指定了使用返回的 socket 地址結構的 socket 類型。如果將這個字段指定為 SOCK_DGRAM,那么查詢將會在 UDP 服務上執行,如果指定了 SOCK_STREAM,那么將返回一個 TCP 服務查詢,如果將其指定為 0,那么任意類型的 socket 都是可接受的。

hints.ai_protocol 字段為返回的地址結構選擇了 socket 協議,這個字段設置為 0,表示調用者接受任何協議。

hints.ai_flags 字段是一個位掩碼,它會改變 getaddrinfo() 的行為,取值為:

AI_ADDRCONFIG:在本地系統上至少配置一個 IPv4 地址時返回 IPv4 地址(不是 IPv4 回環地址),在本地系統上至少配置一個 IPv6 地址時返回 IPv6 地址(不是 IPv6 回環地址)

AI_ALL:參見 AI_V4MAPPED

AI_CANONNAME:如果 host 不為 NULL,返回一個指向 null 結尾的字符串,該字符串包含了主機的規范名,這個指針會在通過 result 返回的第一個 addrinfo 結構中 ai_canoname 字段指向的緩沖器中返回

AI_NUMERICHOST:強制將 host 解釋成一個數值地址字符串,這個常量用于在不必要解析名字時防止進行名字解析,因為名字解析可能會花費比較長的時間

AI_NUMERICSERV:將 service 解釋成一個數值端口號,這個標記用于防止調用任意的名字解析服務,因為當 service 為一個數值字符串時這種調用是沒有必要的

AI_PASSIVE:返回一個適合進行被動式打開(即一個監聽 socket)的 socket 地址結構,此時,host 應該是 NULL,通過 result 返回的 socket 地址結構 IP 地址部分將會包含一耳光通配 IP 地址(INADDR_ANY 或者 IN6ADDR_ANY_INIT)。如果沒有設置這個標記,那么通過 res 返回的地址結構中的 IP 地址將會被設置成回環 IP 地址(INADDR_LOOPBACK 或者 IN6ADDR_LOOPBACK_INIT)

AI_V4MAPPED:如果在 hints 的 ai_family 中指定了 AF_INET6,那么在沒有找到匹配的 IPv6 地址時應該在 res 返回 IPv4 映射的 IPv6 地址。如果同時指定了AI_ALL 和 AI_V4MAPPED,那么 res 中同時返回 IPv6 和 IPv4 地址,IPv4 地址會被返回成 IPv4 映射的 IPv6 地址

釋放 addrinfo 列表 freeaddrinfo()

#include 
#include 
#include 

void freeaddrinfo(struct addrinfo *res);

getaddrinfo() 函數會動態地為 res 引用的所有結構分配內存,其結果是調用者必須要在不需要使用這些結構時釋放它們,使用 freeaddrinfo() 來執行釋放的任務

錯誤診斷 gai_strerror()

getaddrinfo() 在發生錯誤時返回下面的一個錯誤碼:

4b0e01b8-fb84-11ed-90ce-dac502259ad0.png

#include 
#include 
#include 

const char *gai_strerror(int errcode);

gai_strerror() 返回一個描述錯誤的字符串

getnameinfo() 函數

getnameinfo() 是 getaddrinfo() 的逆函數。給定一個 socket 地址結構它會返回一個包含對應的主機和服務名的字符串或者無法解析名字時返回一個等價的數值。

#include 
#include 

int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,char *host, socklen_t hostlen,char *service, socklen_t servlen, int flags);

addr 指向待轉換的 socket 地址結構,長度為 addrlen

得到的主機和服務名是以 null 結尾的字符串,它們會被存儲在 host 和 service 指向的緩沖器中,調用者必須要為這些緩沖器分配空間并將它們的大小傳入 hostlen 和 servlen ,NI_MAXHOST 指出了返回的主機名字符串的最大字節數,其取值為 1025,NI_MAXSERV 指出了返回服務名字符串的最大字節數,其取值為 32

如果不想獲取主機名,可以將 host 指定為 NULL 并且將 hostlen 指定為 0,如果不想獲取服務名,可以將 service 指定為 NULL 并且將 servlen 指定為 0,但是 host 和 service 中至少有一個必須非 NULL

flags 是一個掩碼,控制著 getnameinfo() 的行為,取值為:

NI_DGRAM:默認情況下,getnameinfo() 返回 TCP 服務對應的名字,NI_DGRAM 標記強制返回 UDP 服務的名字

NI_NAMEREQD:默認情況下,如果無法解析主機名,那么在 host 中返回一個數值地址字符串,如果指定了 NI_NAMEREQD,就會返回一個錯誤 EAI_NONAME

NI_NOFQDN:在默認情況下會返回主機的完全限定域名,指定 NI_NOFQDN 標記會導致當主機位于局域網中時只返回名字的第一部分(即主機名)

NI_NUMERICHOST:強制在 host 中返回一個數值地址字符串,這個標記在需要避免可能耗時較長的 DNS 服務器調用時是比較有用的

NI_NUMERICSERV:強制在 service 中返回一個十進制端口號字符串,這個標記在知道端口號不對應于服務器名時,如它是一個由內核分配給 socket 的臨時端口號,以及需要避免不必要的搜索 /etc/service 的低效性時是比較有用的

流式 socket 客戶端/服務器示例

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;
}

過時的主機和服務轉換 API

inet_aton() 和 inet_ntoa()

#include 
#include 
#include 

int inet_aton(const char *str, struct in_addr *addr);

inet_aton() 將 str 指向的點分十進制字符串轉換成一個網絡字節序的 IPv4 地址,轉換得到的地址將會返回 addr 指向的結構

成功時返回 1,str 無效時返回 0

str 字符串的數值部分無需是十進制的,它可以是八進制的,也可以是十六進制的

#include 
#include 
#include 

char *inet_ntoa(struct in_addr in);

inet_ntoa() 返回一個指向包含用點分十進制標記法標記的地址的字符串指針

inet_ntoa() 返回的字符串是靜態分配的,因此會被后續調用所覆蓋

gethostbyname() 和 gethostbyaddr()

#include 
extern int h_errno;

struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);

gethostbyname() 解析由 name 給出的主機名,并返回一個指向靜態分配的包含了主機名相關信息的 hostent 結構的指針

gethostbyaddr() 執行 gethostbyname() 的逆操作,給定一個二進制 IP 地址,它會返回一個包含與配置了該地址的主機相關的信息的 hostent 結構

發生錯誤時,或者無法解析一個名字時,gethostbyname() 和 gethostbyaddr() 都會返回 NULL,并設置全局變量 h_errno。這個變量類似于 errno,herror() 和 hstrerror() 類似于 perror() 和 strerror()

#include 
extern int h_errno;

void herror(const char *s);
const char *hstrerror(int err);

hostent 結構:

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 返回主機的官方名字,是一個以 null 結尾的字符串

h_aliases 指向一個指針數組,數組中的指針指向以 null 結尾的包含了主機名的別名的字符串

h_addr_list 是一個指針數組,數組中的指針指向這個主機的 IP 地址結構,這個列表由 in_addr 和 in6_addr 結構構成,通過 h_addrtype 字段可以確定這些結構的類型,其取值為 AF_INET 或 AF_INET6,h_length 字段可以確定這些結構的長度

getservbyname() 和 getservbyport()

#include 

struct servent *getservbyname(const char *name, const char *proto);

getservbyname() 和 getservbyport() 都是從 /etc/services 文件中獲取記錄,現在已經被 getaddrinfo() 和 getnameinfo() 取代

getservbyname() 查詢服務名或者其中一個別名與 name 匹配以及協議與 proto 匹配的記錄,proto 可以是 TCP 或者 UDP,或者設置為 NULL

如果找到了一個匹配的記錄,那么 getservbyname() 會返回一個指向靜態分配的結構指針:

struct servent {
   char  *s_name;    /* official service name */
   char **s_aliases;   /* alias list */
   int   s_port;    /* port number */
   char  *s_proto;    /* protocol to use */
};

一般調用 getservbyname() 只是為了獲取端口號,即 s_port 字段

#include 

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

getservbyport() 執行 getservbyname() 的逆操作,它返回一個 servent 記錄,該記錄包含了 /etc/services 文件中端口號與 port 匹配的記錄相關的信息

UNIX 與 Internet domain socket 比較

編寫只使用 Internet domain socket 的應用程序即可以運行在同一主機上,也可以運行在網絡中的不同主機上。

UNIX domain socket 只能用于同一系統上的應用程序間通信,使用 UNIX domain socket 的幾個原因:

在一些實現上,UNIX domain socket 速度要比 Internet domain socket 快

可以使用目錄權限來控制對 UNIX domain socket 的訪問,這樣只有運行于指定的用戶或組 ID 下的應用程序才能夠連接到一個監聽流 socket 或向一個數據報 socket 發送一個數據報。





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 緩沖器
    +關注

    關注

    6

    文章

    1921

    瀏覽量

    45473
  • Linux系統
    +關注

    關注

    4

    文章

    593

    瀏覽量

    27392
  • DNS
    DNS
    +關注

    關注

    0

    文章

    218

    瀏覽量

    19828
  • TCP通信
    +關注

    關注

    0

    文章

    146

    瀏覽量

    4221

原文標題:Linux Internet Domain應用編程

文章出處:【微信號:嵌入式應用研究院,微信公眾號:嵌入式應用研究院】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    [推薦]linux下的c語言編程簡介

    第一章本章將簡要介紹一下什么是Linux,C語言的特點,程序開發的預備知識,Linux下C語言開發的環境,程序設計的特點和原則以及編碼風格等。通過本章的學習,可以對在Linux下使用C語言編程
    發表于 04-29 13:50

    Linux網絡編程教材

    /Linux 模型.....172.1 UNIX/Linux 基本結構.......172.2 輸入和輸出..192.2.1 UNIX/Linux 文件系統簡介192.2.2 流和標準
    發表于 01-20 16:49

    精通嵌入式Linux編程

    ....................................................................... 26第 2 章 LINUX 高級程序設計簡介
    發表于 11-06 14:57

    Voice over Internet Protocol

    Voice over Internet Protocol  VoIP簡介 英文版 Voice over Internet Protocol (VoIP) is a technology
    發表于 04-23 18:02 ?21次下載

    Time Domain Reflectometry Theo

    The most general approach to evaluating the time domain responseof any electromagnetic system
    發表于 07-11 17:09 ?5次下載

    EMC宣布收購Data Domain

    EMC宣布收購Data Domain EMC公司宣布,已購得Data Domain的多數股權。預計今年7月底完成Data Domain的收購程序后,EMC便將以Data Domain
    發表于 07-28 07:46 ?890次閱讀

    Internet簡介

    Internet簡介 Internet的定義 Internet的定義(1)?Internet是全球最大的、開放的、由眾多網絡互聯而成的計
    發表于 01-27 11:24 ?1102次閱讀

    什么是Domain Name

    什么是Domain Name 英文縮寫: Domain Name 中文譯名: 域名 分  類: IP與多媒體 解
    發表于 02-22 17:38 ?1430次閱讀

    ATM網絡和Internet融合技術簡介

    ATM網絡和Internet融合技術簡介 在數據信息爆炸式增長的同時,作為信息傳輸媒介的計算機網絡發展也可謂一日千里。以計算機為主開
    發表于 04-06 16:16 ?1316次閱讀

    LINUX網絡編程

    linux開發編程教程資料——LINUX網絡編程,感興趣的小伙伴們可以看一看。
    發表于 08-23 16:23 ?0次下載

    Linux網絡編程

    linux開發編程教程資料——Linux網絡編程,感興趣的小伙伴們可以看一看。
    發表于 08-23 16:23 ?0次下載

    關于LinuxInternet安全漏洞與防范措施詳解

    LINUX是一種當今世界上廣為流行的免費操作系統,它與UNIX完全兼容,但以其開放性的平臺,吸引著無數高等院校的學生和科研機構的人員紛紛把它作為學習和研究的對象。這些編程高手在不斷完善LINUX版本中網絡安全功能。下面介紹
    發表于 07-16 09:20 ?941次閱讀

    Linux教程之Linux命令、編程器、Shell編程、實例大全pdf免費下載

    的實用程序。全書分上、中、下3篇,共20章,內容涵蓋了Linux簡介、Red Hat Linux基礎知識、系統管理與設置、用戶和用戶組管理、磁盤管理、文件和目錄管理、備份與壓縮、網絡管理、正則表達式、vim編輯器、cmacs、g
    發表于 01-08 14:55 ?20次下載
    <b class='flag-5'>Linux</b>教程之<b class='flag-5'>Linux</b>命令、<b class='flag-5'>編程</b>器、Shell<b class='flag-5'>編程</b>、實例大全pdf免費下載

    IRQ domain支持幾種映射方式

    IRQ domain IRQ domain用于將硬件的中斷號,轉換成Linux系統中的中斷號(virtual irq, virq),來張圖: 每個中斷控制器都對應一個IRQ Domain
    的頭像 發表于 09-28 15:21 ?701次閱讀
    IRQ <b class='flag-5'>domain</b>支持幾種映射方式

    Linux應用編程的基本概念

    Linux應用編程涉及到在Linux環境下開發和運行應用程序的一系列概念。以下是一些涵蓋Linux應用編程的基本概念。
    的頭像 發表于 10-24 17:19 ?222次閱讀
    主站蜘蛛池模板: 国产a在线不卡| 亚洲欧美中文字幕先锋| 美女露出乳胸扒开尿口| 黑人操日本女人| 国产精品久久久久久免费字体 | 妹妹好色网| 久久伦理影院| 久久是热这里只有精品| 久久精品中文字幕免费| 久久精品天天爽夜夜爽| 久久精品国产免费播放| 久久国产露脸老熟女熟69| 久久精品国产首叶| 巨大乳hdbbw| 芒果视频看片在线观看| 蜜臀AV精品久久无码99| 女人色极品影院| 人与人特黄一级| 视频成人app永久在线观看| 无限资源在线看影院免费观看| 无码AV毛片色欲欧洲美洲| 学校捏奶揉下面污文h| 亚洲欧美成人无码久久久| 一扒二脱三插片在线观看| 一攻多受h嗯啊巨肉bl巨污| 中文字幕亚洲无线码高清不卡| 2020精品极品国产色在线| FREEHDXXXX学生妹| 妇少水多18P蜜泬17P亚洲乱| 国产精品美女久久久网站动漫| 国产在线一区二区AV视频| 久久精品一区二区免费看| 免费完整版观看| 日日夜夜撸 在线影院| 午夜一级毛片看看| 一二三四在线高清中文版免费观看电影 | 国内精品久久久久久久试看| 久久精品国产亚洲AV忘忧草蜜臀 | 很很射影院| 免费播放美女一级毛片| 日产久久视频|