日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Unix/Linux编程:Internet domain socket

發(fā)布時(shí)間:2023/12/18 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unix/Linux编程:Internet domain socket 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • Internet domain socket
  • 網(wǎng)絡(luò)字節(jié)序
  • 數(shù)據(jù)表示
  • 套接字地址結(jié)構(gòu)
    • IPv4 socket 地址:struct sockaddr_in
    • IPv6 socket 地址:struct sockaddr_in6
    • sockaddr_storage 結(jié)構(gòu)(新的通用套接字)
    • sockaddr結(jié)構(gòu)(老的通用套接字、很少用)
  • 主機(jī)和服務(wù)轉(zhuǎn)換函數(shù)
    • 概述
    • 準(zhǔn)備
      • 域名系統(tǒng)
      • /etc/services 文件
    • inet_pton()和 inet_ntop()函數(shù)
    • 獨(dú)立于協(xié)議的主機(jī)和服務(wù)轉(zhuǎn)換
  • 過時(shí)的主機(jī)和服務(wù)轉(zhuǎn)換 API
    • inet_aton()和 inet_ntoa()函數(shù)
    • gethostbyname()和 gethostbyaddr()函數(shù)
      • 理論
      • 錯(cuò)誤h_errno
      • 實(shí)踐
        • gethostbyname
        • gethostbyaddr
    • getserverbyname()和 getserverbyport()函數(shù)
      • 理論
      • 實(shí)踐
        • getservbyname
  • 值-結(jié)果參數(shù)

Internet domain socket

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

Internet domain數(shù)據(jù)報(bào)socket是預(yù)計(jì)UDP之上的。 UDP socket與之在Unix domin中的對應(yīng)實(shí)體類似,但要注意如下差別

  • Unix domin數(shù)據(jù)報(bào)socket是可靠的,而UDP socket是不可靠的
  • 在一個(gè)Unix domain數(shù)據(jù)報(bào)socket上發(fā)送數(shù)據(jù)會在接受socket的數(shù)據(jù)隊(duì)列為滿時(shí)阻塞。與之不同的是,使用UDP時(shí)如果進(jìn)入的數(shù)據(jù)報(bào)會使接受者的隊(duì)列溢出,那么數(shù)據(jù)報(bào)被靜默丟棄

網(wǎng)絡(luò)字節(jié)序

IP地址和端口號是整數(shù)值。在將這些值在網(wǎng)絡(luò)中傳遞時(shí)碰到的一個(gè)問題是不同的硬件結(jié)構(gòu)會以不同的順序來存儲一個(gè)多字節(jié)整數(shù)的字節(jié)。存儲整數(shù)時(shí)先存儲(即在最小內(nèi)存地址處)最高有效位的被稱為大端,那些先存儲最低有效位的被稱為小端。一些硬件結(jié)構(gòu)可以在這兩種格式之間切換。在特定主機(jī)上使用的字節(jié)序被稱為主機(jī)字節(jié)序

由于端口號和IP地址必須在網(wǎng)絡(luò)中的所有字節(jié)之間傳遞并且需要被它們所理解,因此必須要使用一個(gè)標(biāo)準(zhǔn)的字節(jié)序,這種字節(jié)序被稱為網(wǎng)絡(luò)字節(jié)序(它是大端的)

有時(shí)候可能會直接使用 IP 地址和端口號的整數(shù)常量形式,如可能會選擇將端口號硬編碼進(jìn)程序中,或者將端口號作為一個(gè)命令行參數(shù)傳遞給程序,或者在指定一個(gè) IPv4 地址時(shí)使用諸如INADDR_ANY 和 INADDR_LOOPBACK 之類的常量。這些值在 C 中是按照主機(jī)的規(guī)則來表示的,因此它們是主機(jī)字節(jié)序的,在將它們存儲進(jìn)socket 地址結(jié)構(gòu)中之前需要將這些值轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序

htons()、htonl()、ntohs()以及 ntohl()函數(shù)被定義(通常為宏)用來在主機(jī)和網(wǎng)絡(luò)字節(jié)序之間轉(zhuǎn)換整數(shù)。

#include <netinet/in.h>// 返回一個(gè)32位的網(wǎng)絡(luò)字節(jié)序,h表示主機(jī)uint32_t htonl (uint32_t __hostlong)// 返回一個(gè)16位的網(wǎng)絡(luò)字節(jié)序,h表示主機(jī)uint16_t htons (uint16_t __hostshort)// 返回一個(gè)32位的主機(jī)字節(jié)序,n表示網(wǎng)絡(luò) uint32_t ntohl (uint32_t __netlong) // 返回一個(gè)16位的主機(jī)字節(jié)序,n表示網(wǎng)絡(luò) uint16_t ntohs (uint16_t __netshort)

總結(jié)

網(wǎng)絡(luò)中用的是字節(jié)而不是字符串,我們常說的字節(jié)序指的是二進(jìn)制值

主機(jī)有不同的存儲字節(jié)方式(主機(jī)字節(jié)序):大端模式和小端模式;在不同的主機(jī)通信時(shí),為了避免轉(zhuǎn)換和錯(cuò)誤,我們引入了網(wǎng)絡(luò)字節(jié)序。這樣我們就只需要關(guān)注主機(jī)字節(jié)序和網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換了.


其他用于處理字節(jié)的函數(shù):

#include <string.h>/* * 功能:把s中指定數(shù)組的字節(jié)置0 */ void bzero (void *__s, size_t __n) /* * 功能:src復(fù)制到_dest */ void bcopy (const void *__src, void *__dest, size_t __n) /* * 功能:比較字節(jié)s1和s2。相等返回0,否則非0 */ int bcmp (const void *__s1, const void *__s2, size_t __n)

數(shù)據(jù)表示

在編寫網(wǎng)絡(luò)程序時(shí)需要清除不同的計(jì)算機(jī)架構(gòu)中會使用不同的規(guī)則表示各種數(shù)據(jù)類型。比如整數(shù)類型可以以大端或小端的形式存儲。此外,還存在其他的差別,如 C long 數(shù)據(jù)類型在一些系統(tǒng)中可能是 32 位的,但在其他系統(tǒng)上可能是 64 位的。當(dāng)考慮結(jié)構(gòu)中,問題就更加復(fù)雜了,因?yàn)椴煌膶?shí)現(xiàn)采用了不同的規(guī)則來將一個(gè)結(jié)構(gòu)中的字段對齊到主機(jī)系統(tǒng)的地址邊界,從而使得字段之間的填充字節(jié)數(shù)量是不同的。

由于在數(shù)據(jù)表現(xiàn)上存在這些差異,因此在網(wǎng)絡(luò)的異構(gòu)系統(tǒng)之間交換數(shù)據(jù)的應(yīng)用程序必須要采用一些公共編碼來編碼數(shù)據(jù)。發(fā)送者必須要根據(jù)這些規(guī)則來對數(shù)據(jù)進(jìn)行編碼,而接收者則必須要遵循同樣的規(guī)則對數(shù)據(jù)進(jìn)行解碼。將數(shù)據(jù)變成一個(gè)標(biāo)準(zhǔn)格式以便在網(wǎng)絡(luò)上傳輸?shù)倪^程被稱為信號編集

然而,一種比信號編集更簡單的方法通常會被采用:將所有傳輸?shù)臄?shù)據(jù)編碼成文本形式,其中數(shù)據(jù)項(xiàng)之間使用特定的字符來分隔開,這個(gè)特定的字符通常是換行符。

如果將在一個(gè)流 socket 上傳輸?shù)臄?shù)據(jù)編碼成使用換行符分隔的文本,那么定義一個(gè)諸如readLine()之類的函數(shù)將是比較便捷的。

readLine()函數(shù)從文件描述符參數(shù) fd 引用的文件中讀取字節(jié)直到碰到換行符為止。輸入字節(jié)序列將會返回在 buffer 指向的位置處,其中 buffer 指向的內(nèi)存區(qū)域至少為 n 字節(jié)。返回的字符串總是以 null 結(jié)尾,因此實(shí)際上至多有(n–1)個(gè)字節(jié)會返回。在成功時(shí),readLine()會返回放入 buffer 的數(shù)據(jù)的字節(jié)數(shù),結(jié)尾的 null 字節(jié)不會計(jì)算在內(nèi)。

ssize_t readLine(int fd, void *buffer, size_t n) {ssize_t numRead; /* # of bytes fetched by last read() */size_t totRead; /* Total bytes read so far */char *buf;char ch;if (n <= 0 || buffer == NULL) {errno = EINVAL;return -1;}buf = buffer; /* No pointer arithmetic on "void *" */totRead = 0;for (;;) {numRead = read(fd, &ch, 1);if (numRead == -1) {if (errno == EINTR) /* Interrupted --> restart read() */continue;elsereturn -1; /* Some other error */} else if (numRead == 0) { /* EOF */if (totRead == 0) /* No bytes read; return 0 */return 0;else /* Some bytes read; add '\0' */break;} else { /* 'numRead' must be 1 if we get here */if (totRead < n - 1) { /* Discard > (n - 1) bytes */totRead++;*buf++ = ch;}if (ch == '\n')break;}}*buf = '\0';return totRead; }

如果在遇到換行符之前讀取的字節(jié)數(shù)大于或等于(n–1),那么 readLine()函數(shù)會丟棄多余的字節(jié)(包括換行符)。如果在前面的(n–1)字節(jié)中讀取了換行符,那么在返回的字符串中就會包含這個(gè)換行符。(因此可以通過檢查在返回的 buffer 中結(jié)尾 null 字節(jié)前是否是一個(gè)換行符來確定是否有字節(jié)被丟棄了。)

套接字地址結(jié)構(gòu)

Internet domain socket地址由兩種:IPv4和IPv6

IPv4 socket 地址:struct sockaddr_in

一個(gè) IPv4 socket 地址會被存儲在一個(gè) sockaddr_in 結(jié)構(gòu)中,該結(jié)構(gòu)在<netinet/in.h>中進(jìn)行定義,具體如下:

#include <netinet/in.h>typedef uint16_t in_port_t;typedef uint32_t in_addr_t;struct in_addr /* IPv4 4-byte address */{ in_addr_t s_addr; /* unsigned 32-bit integer */};struct sockaddr_in /* IPv4 socket address */{sa_family sin_family; /* Address family(AF_INET) */in_port_t sin_port; /* Port number. */struct in_addr sin_addr; /* Internet address. */unsigned char sin_zero[x]; /* Pad to size of `struct sockaddr'. */};
  • sin_family 字段用來標(biāo)識socket domain,其值總是為AF_INET
  • sin_port 和 sin_addr 字段是端口號和 IP 地址,它們都是網(wǎng)絡(luò)字節(jié)序
  • in_port_t 和in_addr_t t 數(shù)據(jù)類型是無符號整型,其長度分別為 16 位和 32 位。
  • sin_sero在綁定一個(gè)非通配的IPv4地址時(shí),必須為0
  • 套接字地址結(jié)構(gòu)僅在給定主機(jī)上使用:雖然結(jié)構(gòu)中的IP地址、端口號等可以用在不同主機(jī)之間的通信中,但是結(jié)構(gòu)本身并不在主機(jī)之間傳遞

IPv6 socket 地址:struct sockaddr_in6

與 IPv4 地址一樣,一個(gè) IPv6 socket 地址包含一個(gè) IP 地址和一個(gè)端口號,它們之間的差別在于 IPv6 地址是 128 位而不是 32 位的。一個(gè) IPv6 socket 地址會被存儲在一個(gè) sockaddr_in6結(jié)構(gòu)中,該結(jié)構(gòu)在<netinet/in.h>中進(jìn)行定義,具體如下。

struct in6_addr // IPv6 address structure{union{uint8_t __u6_addr8[16]; // 16 bytes == 128bits #if defined __USE_MISC || defined __USE_GNUuint16_t __u6_addr16[8]; uint32_t __u6_addr32[4]; #endif} __in6_u;};struct sockaddr_in6 /* IPv6 socket address */{sa_family sin_family; /* Address family(AF_INET) */in_port_t sin6_port; /* Port number. */uint32_t sin6_flowinfo; /* IPv6 flow information */struct in6_addr sin6_addr; /* IPv6 address */uint32_t sin6_scope_id; /* IPv6 scope-id */};
  • sockaddr_in6 結(jié)構(gòu)中的所有字段都是以網(wǎng)絡(luò)字節(jié)序存儲的
  • sin_family 字段會被設(shè)置成 AF_INET6
  • sin6_port 和 sin6_addr 字段分別是端口號和 IP地址。

IPv6 和 IPv4 一樣也有通配和回環(huán)地址,但它們的用法要更加復(fù)雜一些,因?yàn)?IPv6 地址是存儲在數(shù)組中的(并沒有使用標(biāo)量類型),下面將會使用 IPv6 通配地址(0::0)來說明這一點(diǎn)。系統(tǒng)定義了常量 IN6ADDR_ANY_INIT 來表示這個(gè)地址,具體如下

#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }

在變量聲明的初始化器中可以使用IN6ADDR_ANY_INIT ,但無法在一個(gè)賦值語句中的右邊使用這個(gè)常量。因?yàn)镃語法不允許在賦值語句中使用一個(gè)結(jié)構(gòu)化的常量。取而代之的做法是必須要使用一個(gè)預(yù)先定義的變量in6addr_any,C庫會按照下面的方式對該變量進(jìn)行初始化:

const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;

因此可以向如下這樣使用通配地址來初始化一個(gè)IPv6 socket地址:

struct sockaddr_in6 addr;memset(&addr, 0, sizeof(struct sockaddr_in6));addr.sin6_family = AF_INET6;addr.sin6_addr = in6addr_any;addr.sin6_port = htons(SOME_PORT_NUM);

IPv6 環(huán)回地址(::1)的對應(yīng)常量和變量是 IN6ADDR_LOOPBACK_INIT 和 in6addr _loopback

如果 IPv4 和 IPv6 共存于一臺主機(jī)上,那么它們將共享同一個(gè)端口號空間。這意味著如果一個(gè)應(yīng)用程序?qū)⒁粋€(gè) IPv6 socket 綁定到了 TCP 端口 2000 上(使用 IPv6 通配地址),那么 IPv4 TCP socket 將無法綁定到同一個(gè)端口上。(TCP/IP 實(shí)現(xiàn)確保位于其他主機(jī)上的socket 能夠與這個(gè) socket 進(jìn)行通信,不管那些主機(jī)運(yùn)行的是 IPv4 還是 IPv6。

sockaddr_storage 結(jié)構(gòu)(新的通用套接字)

在IPv6 socket API中新引入了一個(gè)通用的sockaddr_storage結(jié)構(gòu),這個(gè)結(jié)構(gòu)的空間足以存儲任意類型的socket地址(即可以將任意類型的socket地址結(jié)構(gòu)強(qiáng)制轉(zhuǎn)換并存儲在這個(gè)結(jié)構(gòu)中)。特別地,這個(gè)結(jié)構(gòu)允許透明地存儲 IPv4 或 IPv6 socket 地址,從而刪除了代碼中的 IP 版本依賴性。sockaddr_storage 結(jié)構(gòu)在 Linux 上的定義如下所示。

struct sockaddr_storage{__SOCKADDR_COMMON (ss_); /* Address family, etc. */char __ss_padding[_SS_PADSIZE];__ss_aligntype __ss_align; /* Force desired alignment. */};

sockaddr結(jié)構(gòu)(老的通用套接字、很少用)

#include <sys/socket.h> struct sockaddr{__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */char sa_data[14]; /* Address data. */};

從應(yīng)用開發(fā)的角度來看,這個(gè)結(jié)構(gòu)的唯一用途就是對指向特定于協(xié)議的套接字地址結(jié)構(gòu)指針指向類型轉(zhuǎn)換

用于將主機(jī)名(如 www.kernel.org)和服務(wù)名(如 http)轉(zhuǎn)換成對應(yīng)的數(shù)字形式的函數(shù)。這些函數(shù)一般會返回用網(wǎng)絡(luò)字節(jié)序表示的整數(shù),并且可以直接將這些整數(shù)復(fù)制進(jìn)一個(gè) socket 地址結(jié)構(gòu)的相關(guān)字段中。

主機(jī)和服務(wù)轉(zhuǎn)換函數(shù)

概述

計(jì)算機(jī)以二進(jìn)制形式來表示IP地址和端口號,但是名字比數(shù)字更容易記憶,而且可以在名字不變的情況下隨意改變底層的數(shù)字值

主機(jī)名是連接在網(wǎng)絡(luò)上的一個(gè)系統(tǒng)的符號標(biāo)識符。服務(wù)名是端口號的符號標(biāo)識。

主機(jī)地址和端口的標(biāo)識有如下兩種方法:

  • 主機(jī)地址可以標(biāo)識為一個(gè)二進(jìn)制值或者一個(gè)符號主機(jī)名展現(xiàn)格式(IPv4 是點(diǎn)分十進(jìn)制,IPv6 是十六進(jìn)制字符串)
  • 端口號可以表示為一個(gè)二進(jìn)制值或一個(gè)符號服務(wù)名

在二進(jìn)制和人類可讀的形式之間轉(zhuǎn)換IPv4的地址(已過時(shí))

inet_aton、inet_addr、inet_ntoa函數(shù)將一個(gè) IPv4 地址在點(diǎn)分十進(jìn)制表示形式(127.0.0.1)和32位的網(wǎng)絡(luò)字節(jié)序二進(jìn)制值之間轉(zhuǎn)換表示形式之間進(jìn)行轉(zhuǎn)換,僅適用于IPv4地址。現(xiàn)在它們已經(jīng)被廢棄了。請避免在程序中使用它們

在二進(jìn)制和人類可讀的形式之間轉(zhuǎn)換 IPv4 和 IPv6 地址

inet_pton()和 inet_ntop()函數(shù)將二進(jìn)制 IPv4 和 IPv6 地址轉(zhuǎn)換成展現(xiàn)格式—即以點(diǎn)分十進(jìn)制表示或十六進(jìn)制字符串表示,或?qū)⒄宫F(xiàn)格式轉(zhuǎn)換成二進(jìn)制 IPv4 和 IPv6 地址。

由于人類對名字的處理能力要比對數(shù)字的處理能力強(qiáng),因此通常偶爾才會在程序中使用這些函數(shù)。inet_ntop()的一個(gè)用途是產(chǎn)生IP地址的一個(gè)可打印的表示形式以便記錄日志。在有些情況下,最好使用這個(gè)函數(shù)而不是將一個(gè)IP地址解析成主機(jī)名,其原因如下:

  • 將一個(gè)IP地址解析成主機(jī)名可能需要向一臺服務(wù)器發(fā)送一個(gè)耗時(shí)比較長的請求
  • 在一些場景中,可能并不存在一個(gè) DNS(PTR)記錄將 IP 地址映射到對應(yīng)的主機(jī)名上

主機(jī)和服務(wù)名與二進(jìn)制形式之間的轉(zhuǎn)換(已過時(shí))

gethostbyname()函數(shù)返回與主機(jī)名對應(yīng)的二進(jìn)制 IP 地址,getservbyname()函數(shù)返回與服務(wù)名對應(yīng)的端口號。對應(yīng)的逆向轉(zhuǎn)換是由 gethostbyaddr()和 getservbyport()來完成的。現(xiàn)在它們已經(jīng)過時(shí)了,新代碼應(yīng)該使用 getaddrinfo()和getnameinfo()函數(shù)來完成此類轉(zhuǎn)換

主機(jī)和服務(wù)名與二進(jìn)制形式之間的轉(zhuǎn)換(現(xiàn)代的)

getaddrinfo()函數(shù)是 gethostbyname()和 getservbyname()兩個(gè)函數(shù)的現(xiàn)代繼任者。給定一個(gè)主機(jī)名和一個(gè)服務(wù)名,getaddrinfo()會返回一組包含對應(yīng)的二進(jìn)制IP地址和端口號的結(jié)構(gòu)。與gethostbyname()不同,getaddrinfo()會透明地處理 IPv4 和 IPv6 地址。因此使用這個(gè)函數(shù)可以編寫不依賴于 IP 版本的程序。所有新代碼都應(yīng)該使用 getaddrinfo()來將主機(jī)名和服務(wù)名轉(zhuǎn)換成二進(jìn)制表示。

getnameinfo()函數(shù)執(zhí)行逆向轉(zhuǎn)換,即將一個(gè) IP 地址和端口號轉(zhuǎn)換成對應(yīng)的主機(jī)名和服務(wù)名。

使用 getaddrinfo()和 getnameinfo()還可以在二進(jìn)制 IP 地址與其展現(xiàn)格式之間進(jìn)行轉(zhuǎn)換。

準(zhǔn)備

域名系統(tǒng)

在DNS出現(xiàn)之前,主機(jī)名和IP地址之間的映射關(guān)系是在一個(gè)手工維護(hù)的本地文件/ect/hosts中定義的,該文件包含了形如下面的記錄。

gethostbyname()函數(shù)(被 getaddrinfo()取代的函數(shù))通過搜索這個(gè)文件并找出與規(guī)范主機(jī)名(即主機(jī)的官方或主要名稱)或其中一個(gè)別名(可選的,以空格分隔)匹配的記錄來獲取一個(gè)IP 地址。

然而,/etc/hosts 模式的擴(kuò)展性交叉,并且隨著網(wǎng)絡(luò)中主機(jī)數(shù)量的增長(如因特網(wǎng)中存在著數(shù)以億計(jì)的主機(jī)),這種方式已經(jīng)變得不太可行了。
DNS被設(shè)計(jì)用來解決這個(gè)問題。

當(dāng)一個(gè)程序調(diào)用 getaddrinfo()來解析(即獲取 IP 地址)一個(gè)域名時(shí),getaddrinfo()會使用一組庫函數(shù)(resolver庫)來與本地的DNS服務(wù)器通信。如果這個(gè)服務(wù)器無法提供所需的信息,那么它就會與位于層級中的其他DNS服務(wù)器進(jìn)行通信以便獲取信息。有時(shí)候,這個(gè)解析過程可能會花費(fèi)很多時(shí)間,DNS服務(wù)器采用了緩存技術(shù)來避免在查詢常見域名時(shí)所發(fā)送的不必要的通信

  • DSN解析請求可以分為兩類:遞歸和等待,在一個(gè)遞歸請求中,請求者要求服務(wù)器處理整個(gè)解析任務(wù),包括在必要的時(shí)候與其他DNS服務(wù)器進(jìn)行通信。當(dāng)位于本地主機(jī)上的一個(gè)應(yīng)用程序調(diào)用getaddrinfo()時(shí),該函數(shù)會向本地DNS服務(wù)器發(fā)起一個(gè)遞歸請求。如果本地DNS服務(wù)器自己并沒有相關(guān)信息來進(jìn)行解析,那么它就會迭代地解析這個(gè)域名。
  • 如果向 gethostbyname()傳遞了一個(gè)不完整的域名,那么解析器在解析之前會嘗試補(bǔ)全。域名補(bǔ)全的規(guī)則是在/etc/resolv.conf 中定義的(參見 resolv.conf(5)手冊)。在默認(rèn)情況下,解析器至少會使用本機(jī)的域名來補(bǔ)全。例如,如果登錄機(jī)器 oghma.otago.ac.nz 并輸入了命令 ssh octavo,得到的 DNS 查詢將會以 octavo.otago.ac.nz 作為其名字。

/etc/services 文件

眾所周知的端口號是由 IANA 集中注冊的,其中每個(gè)端口都有一個(gè)對應(yīng)的服務(wù)名。由于服務(wù)器是集中管理并且不會像IP地址那樣頻繁編號,因此沒有必要采用DNS服務(wù)來管理它們。相反,端口號的服務(wù)名會記錄在/ect/serivces中。getaddrinfo()和getnameinfo()函數(shù)會使用這個(gè)文件中的信息在服務(wù)名和端口號之間進(jìn)行轉(zhuǎn)換

inet_pton()和 inet_ntop()函數(shù)

inet_pton、inet_ntop函數(shù)用于:在 IPv4 和 IPv6 地址的網(wǎng)絡(luò)字節(jié)序二進(jìn)制形式和點(diǎn)分十進(jìn)制表示法或十六進(jìn)制字符串表示法之間進(jìn)行轉(zhuǎn)換

/* * 參數(shù): family: AF_INET或者AF_INET6。否則函數(shù)返回一個(gè)錯(cuò)誤,并將error置EAFNOSUPPORT * strptr:點(diǎn)分十進(jìn)制指針。如果指向的字符串不是有效的表達(dá)式函數(shù)返回一個(gè)0 * addrptr: 該指針指向的空間存放轉(zhuǎn)換出來的二進(jìn)制結(jié)果。 * 返回值:成功1,如果輸入不是有效的表達(dá)式則0,出錯(cuò)返回-1 */ int inet_pton (int family, const char *__restrict strptr,void *__restrict addrptr)/* * 參數(shù): family: AF_INET或者AF_INET6。否則函數(shù)返回一個(gè)錯(cuò)誤,并將error置EAFNOSUPPORT * strptr: 指向函數(shù)的結(jié)果。必須不為空(為目標(biāo)存儲單位分配內(nèi)存并指定其大小) * __len: 目標(biāo)出錯(cuò)單元的大小(包括結(jié)尾的空字符),以免溢出。與之有關(guān)的宏定義 * #define INET_ADDRSTRLEN 16#define INET6_ADDRSTRLEN 46如果__len太小,該函數(shù)返回一個(gè)控制在,并將error置ENOSPC * * 返回值:成功返回指向結(jié)果的指針,出錯(cuò)為null */ const char *inet_ntop (int family, const void *__restrict addrptr,char *__restrict strptr, socklen_t __len)

p表示presentation(展現(xiàn))表達(dá)式[ASCI字符串],n表示number數(shù)值[二進(jìn)制值]

展現(xiàn)形式是人類可讀的字符串,如:

  • 204.152.189.116(IPv4 點(diǎn)分十進(jìn)制地址);
  • ::1(IPv6 冒號分隔的十六進(jìn)制地址);
  • ::FFFF:204.152.189.116(IPv4 映射的 IPv6 地址)。

inet_pton()函數(shù)將 src_str 中包含的展現(xiàn)字符串轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序的二進(jìn)制 IP 地址。domain 參數(shù)應(yīng)該被指定為 AF_INET 或 AF_INET6。轉(zhuǎn)換得到的地址會被放在 addrptr 指向的結(jié)構(gòu)中,它應(yīng)該根據(jù)在 domain 參數(shù)中指定的值指向一個(gè) in_addr 或 in6_addr 結(jié)構(gòu)。

inet_ntop()函數(shù)執(zhí)行逆向轉(zhuǎn)換。同樣,domain 應(yīng)該被指定為 AF_INET 或 AF_INET6,addrptr 應(yīng)該指向一個(gè)待轉(zhuǎn)換的 in_addr 或 in6_addr 結(jié)構(gòu)。得到的以 null 結(jié)尾的字符串會被放置在 dst_str 指向的緩沖器中。len 參數(shù)必須被指定為這個(gè)緩沖器的大小。inet_ntop()在成功時(shí)會返回 dst_str。如果 len 的值太小了,那么 inet_ntop()會返回 NULL 并將 errno設(shè)置成 ENOSPC。

要正確計(jì)算 dst_str 指向的緩沖器的大小可以使用在<netinet/in.h>中定義的兩個(gè)常量。這些常量標(biāo)識出了 IPv4 和 IPv6 地址的展現(xiàn)字符串的最大長度(包括結(jié)尾的 null 字節(jié))。

#define INET_ADDRSTRLEN 16 #define INET6_ADDRSTRLEN 46

使用示例:

char str_v4[INET_ADDRSTRLEN];struct sockaddr_in addr;inet_ntop(AF_INET, &addr.sin_addr, str_v4, sizeof(str_v4));char str_v6[INET6_ADDRSTRLEN];struct sockaddr_in6 addr6;inet_ntop(AF_INET6, &addr6.sin6_addr, str_v6, sizeof(str_v6)); #include <stdlib.h> #include <stdio.h> #include <getopt.h> #include <zconf.h> #include <sys/socket.h> #include <rpc/types.h> #include <arpa/inet.h> #include <memory.h> #define DEST_IP "127.0.0.1" int main(int argc, char *argv[]) {int sockfd, result;socklen_t len;struct sockaddr_in servaddr;struct sockaddr_in sa;len = sizeof(sa);sockfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(struct sockaddr_in));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(8000);inet_pton(AF_INET, DEST_IP, &servaddr.sin_addr);result = bind(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr));if(result < 0){perror("bind");close(sockfd);exit(1);}bzero(&sa, sizeof(struct sockaddr_in));if (getsockname(sockfd, (struct sockaddr *)&sa, &len) == -1) {perror("getsockname error");exit(0);}if(sa.sin_family == AF_INET){char str_v4[INET_ADDRSTRLEN];inet_ntop(AF_INET, &sa.sin_addr, str_v4, sizeof(str_v4));printf("s_addr = %u ==> %s, sin_port = %d ==> %hu \n",sa.sin_addr.s_addr, str_v4, sa.sin_port, ntohs(sa.sin_port));// s_addr = 16777343 ==> 127.0.0.1, sin_port = 16415 ==> 8000 }else{char str_v6[INET6_ADDRSTRLEN];inet_ntop(AF_INET6, &sa.sin_addr, str_v6, sizeof(str_v6));printf("s_addr = %u ==> %s, sin_port = %d ==> %hu \n",sa.sin_addr.s_addr, str_v6, sa.sin_port, ntohs(sa.sin_port));}close(sockfd);exit(0); }

自己實(shí)現(xiàn)inet_pton、inet_ntop的IPv4

#include <stdlib.h> #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include<arpa/inet.h> #include <errno.h>int Inet_pton(int family, const char*strptr, void *addrptr){if(family == AF_INET){struct in_addr inAddr;if (inet_aton(strptr, &inAddr)){memcpy(addrptr, &inAddr, sizeof(inAddr));return 1;}return 0;}return -1; }const char * Inet_ntop(int family, const void * addrptr, char *strptr, size_t len){const u_char * p = (const u_char*)addrptr;if (family == AF_INET){char temp[INET_ADDRSTRLEN];snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);if (strlen(temp) >= len){errno = ENOSPC;return NULL;}strcpy(strptr, temp);return strptr;}return NULL; } int main(int argc, char **argv){uint32_t inAddr;const char *p = "127.0.0.1";Inet_pton(AF_INET, p, &inAddr);printf("Inet_pton(%s) = (%u)\n", p, inAddr); // 16777343char strptr[INET_ADDRSTRLEN];const char * n = Inet_ntop(AF_INET, &inAddr, strptr, sizeof(strptr));printf("Inet_ntop(%u) = (%s, %s)", inAddr, strptr, n); //127.0.0.1, 127.0.0.1 }

獨(dú)立于協(xié)議的主機(jī)和服務(wù)轉(zhuǎn)換

getaddrinfo()函數(shù)將主機(jī)和服務(wù)名轉(zhuǎn)換成 IP 地址和端口號,它作為過時(shí)的 gethostbyname()和 getservbyname()函數(shù)的(可重入的)接替者被定義在了 POSIX.1g 中。

getnameinfo()函數(shù)是 getaddrinfo()的逆函數(shù),它將一個(gè) socket 地址結(jié)構(gòu)(IPv4 或 IPv6)轉(zhuǎn)換成包含對應(yīng)主機(jī)和服務(wù)名的字符串。這個(gè)函數(shù)是過時(shí)的 gethostbyaddr()和 getservbyport()函數(shù)的(可重入的)等價(jià)物。

具體請參考這里

過時(shí)的主機(jī)和服務(wù)轉(zhuǎn)換 API

inet_aton()和 inet_ntoa()函數(shù)

inet_aton()和 inet_ntoa()函數(shù)將一個(gè) IPv4 地址在點(diǎn)分十進(jìn)制標(biāo)記法和二進(jìn)制形式(以網(wǎng)絡(luò)字節(jié)序)之間進(jìn)行轉(zhuǎn)換。這些函數(shù)現(xiàn)在已經(jīng)被 inet_pton()和 inet_ntop()所取代了。

/* * 功能:將__cp所指的C字符串轉(zhuǎn)換成一個(gè)32位的網(wǎng)絡(luò)字節(jié)序二進(jìn)制值,并使用指針__inp來存儲 * 如果__cp指針為空,該函數(shù)仍然對輸入的字符串指向有效性檢查,但是不存儲任何結(jié)果 * 返回值:成功1,失敗0 */ int inet_aton (const char *__cp, struct in_addr *__inp) /* *功能:將32位的網(wǎng)絡(luò)字節(jié)二進(jìn)制轉(zhuǎn)為點(diǎn)位十進(jìn)制。 * 這個(gè)函數(shù)是不可重入的 */ char * inet_ntoa (struct in_addr __in)/* * 這個(gè)函數(shù)已經(jīng)被廢棄 * 功能:將__cp所指的C字符串轉(zhuǎn)換成一個(gè)32位的網(wǎng)絡(luò)字節(jié)序二進(jìn)制值,并使用指針__inp來存儲 * 返回值:返回32位的網(wǎng)絡(luò)字節(jié)序二進(jìn)制值。失敗則返回INADDR_NONE(是一個(gè)32位均為1的值)。但是Ipv4的廣播地址位255.255.255.255. */ in_addr_t inet_addr (const char *__cp)

使用示例:

#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> int main(int aargc, char* argv[]) {struct in_addr addr1,addr2;const char *p1 = "192.168.0.74", *p2 = "211.100.21.179";ulong l1,l2;l1= inet_addr(p1), l2 = inet_addr(p2);printf("inet_addr函數(shù)已經(jīng)被廢棄 inet_addr(%s) = %u , inet_addr(%s) = %u\n", p1, inet_addr(p1), p2, inet_addr(p2));memcpy(&addr1, &l1, 4); memcpy(&addr2, &l2, 4);printf("memcpy(%lu) = %u , memcpy(%lu) = %ld\n",l1, addr1.s_addr, l2, addr2);printf("inet_ntoa(%u) = %s , inet_ntoa(%u) = %s\n", addr1.s_addr, inet_ntoa(addr1), addr2.s_addr, inet_ntoa(addr2));return 0; }

gethostbyname()和 gethostbyaddr()函數(shù)

gethostbyname()和 gethostbyaddr()函數(shù)允許在主機(jī)名和 IP 地址之間進(jìn)行轉(zhuǎn)換。現(xiàn)在這些函數(shù)已經(jīng)被 getaddrinfo()和 getnameinfo()所取代了。

理論

#include <netdb.h> struct hostent {char *h_name; /* 主機(jī)名 */char **h_aliases; /*主機(jī)別名列表,可能有多個(gè) */int h_addrtype; /* 地址類型 */int h_length; /* 地址長度 */char **h_addr_list; /* 根據(jù)網(wǎng)絡(luò)字節(jié)序列出的主機(jī)IP地址列表 */ #endif }; /* * 功能: 根據(jù)主機(jī)名獲取主機(jī)的完整信息 * 參數(shù): __name --- 目標(biāo)主機(jī)的主機(jī)名 * 返回值:非空指針——成功,空指針——出錯(cuò),同時(shí)設(shè)置h_errno * 說明: gethostbyname 先到本地的/etc/hosts配置文件中查找主機(jī),如果沒有直到,再去訪問DNS服務(wù)器 * 注意: 執(zhí)行對A記錄的查詢。所以它只能返回IPv4的地址 */ struct hostent *gethostbyname (const char *__name)/* * 功能: 根據(jù)IP地址獲取主機(jī)的完整信息 * 參數(shù): addr --- 目標(biāo)主機(jī)的IP地址 * len -- 直到addr的長度 * family -- 指定IP地址的類型,包括AF_INET、AF_INET6 * 返回值:非空指針——成功,空指針——出錯(cuò),同時(shí)設(shè)置h_errno */ struct hostent * gethostbyaddr(const char *addr, size_t len , int family);

1、返回值:
從該結(jié)構(gòu)體可以看出,不只返回 IP 地址,還會附帶其他信息,各位讀者只需關(guān)注最后一個(gè)成員 * h_addr_list。下面是對各成員的說明:

  • h_name:官方域名/主機(jī)名(Official domain name)。官方域名代表某一主頁,但實(shí)際上一些著名公司的域名并未用官方域名注冊。
  • h_aliases:別名,可以通過多個(gè)域名訪問同一主機(jī)。同一 IP 地址可以綁定多個(gè)域名,因此除了當(dāng)前域名還可以指定其他域名。
  • h_length:IP地址長度。IPv4 的長度為 4 個(gè)字節(jié),IPv6 的長度為 16 個(gè)字節(jié)。
  • h_addr_list【按照網(wǎng)絡(luò)字節(jié)序給出的主機(jī)IP地址族】:這是最重要的成員。通過該成員以整數(shù)形式保存域名對應(yīng)的 IP 地址。對于用戶較多的服務(wù)器,可能會分配多個(gè) IP 地址給同一域名,利用多個(gè)服務(wù)器進(jìn)行均衡負(fù)載。


2、出錯(cuò): 當(dāng)發(fā)生錯(cuò)誤時(shí),它不設(shè)置errno變量,設(shè)置全局整數(shù)變量h_errno

錯(cuò)誤h_errno

在發(fā)生錯(cuò)誤時(shí)(如無法解析一個(gè)名字),gethostbyname()和 gethostbyaddr()都會返回一個(gè) NULL指針并設(shè)置全局變量 h_errno。

正如其名字所表達(dá)的那樣,這個(gè)變量與 errno 類似(gethostbyname(3)手冊描述了這個(gè)變量的可取值),herror()和 hstrerror()函數(shù)類似于 perror()和 strerror()。herror()函數(shù)(在標(biāo)準(zhǔn)錯(cuò)誤上)顯示了在 str 中給出的字符串,后面跟著一個(gè)冒號(😃,然后再顯示一條與當(dāng)前位于 h_errno 中的錯(cuò)誤對應(yīng)的消息。或者可以使用 hstrerror()獲取一個(gè)指向與在 err 中指定的錯(cuò)誤值對應(yīng)的字符串的指針。

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

實(shí)踐

gethostbyname

獲取當(dāng)前主機(jī)的名字

#include <stdio.h> #include <zconf.h>#define HOSTNAME_MAX 1024 int main(int argc, char **argv){char buf[HOSTNAME_MAX + 1];if(gethostname(buf, HOSTNAME_MAX) == -1){printf("Cannot get machine hostname.");}printf("%s", buf);return 0; }

獲取域名的主機(jī)名字

#include <stdio.h> #include <stdlib.h> #include <netdb.h> #include <arpa/inet.h>int main(){int i;struct hostent *host;host = gethostbyname("www.baidu.com");if(!host){printf("Get IP address error: %s", hstrerror(h_errno));exit(0);}for(i=0; host->h_aliases[i]; i++){printf("Aliases(別名) %d: %s\n", i+1, host->h_aliases[i]);}printf("Address type(地址類型): %s\n", (host->h_addrtype==AF_INET) ? "AF_INET": "AF_INET6");for( i=0; host->h_addr_list[i]; i++){printf("IP addr(IP地址) %d: %s\n", i+1, inet_ntoa( *(struct in_addr*)host->h_addr_list[i] ) );}return 0; }

#include <stdio.h> #include <stdlib.h> #include <netdb.h> #include <arpa/inet.h>main(int argc, char **argv){char *ptr, **pptr;char str[INET_ADDRSTRLEN];struct hostent *hptr;while (--argc){ptr = *++argv;if( (hptr = gethostbyname(ptr)) == NULL){printf("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:printf("unknown address type");break;}}return 0; }

通過主機(jī)名獲取主機(jī)的完整信息

gethostbyaddr

#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <netdb.h>int main(int argc, char **argv) {struct in_addr addr;struct hostent *phost;if (inet_pton(AF_INET, argv[1], &addr) <= 0) {printf("inet_pton error:%s\n", strerror(errno));return -1;}phost = gethostbyaddr((const char*)&addr, sizeof(addr), AF_INET);if (phost == NULL) {printf("gethostbyaddr error:%s\n", strerror(h_errno));return -1;} printf("host name:%s\n", phost->h_name);return 0; }

通過IP地址獲取主機(jī)的完整信息:


解決:/etc/hosts下寫上 115.239.211.110 www.baidu.com

getserverbyname()和 getserverbyport()函數(shù)

getservbyname()和 getservbyport()函數(shù)從/etc/services 文件中獲取記錄。現(xiàn)在這些函數(shù)已經(jīng)被 getaddrinfo()和 getnameinfo()所取代了。

類似域名映射到點(diǎn)位十進(jìn)制標(biāo)記一臺主機(jī),我們也可以將一個(gè)別名映射到一個(gè)端口標(biāo)記一個(gè)服務(wù)。這個(gè)名字到端口的映射通常保存在/etc/service中。(如果端口號改變,我們所需做的所有改動就是改動文件/etc/services中的一行,而不是重新編譯應(yīng)用程序)

理論

#include <netdb.h> /* * 功能: * 參數(shù):name: 一個(gè)指向服務(wù)名的指針。 * proto: 指向協(xié)議名的指針(可選)。如果這個(gè)指針為空,getservbyname()返回第一個(gè)name與s_name或者某一個(gè)s_aliases匹配的服務(wù)條目。否則getservbyname()對name和proto都進(jìn)行匹配。 * 返回值:成功返回非空指針,失敗返回空指針 **/ struct servent * getservbyname(const char * name, const char *proto);/* * 功能: 給定端口號和可選協(xié)議后查找相應(yīng)的服務(wù) * 參數(shù):name: 一個(gè)指向服務(wù)名的端口。 * proto: 指向協(xié)議名的指針(可選)。如果這個(gè)指針為空,getservbyname()返回第一個(gè)name與s_name或者某一個(gè)s_aliases匹配的服務(wù)條目。否則getservbyname()對name和proto都進(jìn)行匹配。 * 返回值:成功返回非空指針,失敗返回空指針 **/ struct servent *getservbyport(int port, const char *protoname);

返回值:

struct servent {char *s_name; //正規(guī)的服務(wù)名char **s_aliases; // 一個(gè)以空指針結(jié)尾的可選服務(wù)名隊(duì)列int s_port;// 連接該服務(wù)時(shí)需要用到的端口號,返回的端口號是以網(wǎng)絡(luò)字節(jié)順序排列的char *s_proto;// 連接該服務(wù)時(shí)用到的協(xié)議名 };

典型調(diào)用:

struct servent *sptr; sptr = getservbyname("domain", "udp"); // DNS using UDP sptr = getservbyname("ftp", "tcp");//FTP using TCP sptr = getservbyname("ftp", NULL); //FTP using TCP sptr = getservbyname("ftp", "udp");// this call will fail 由于FTP僅支持TCP,所以第二個(gè)和第三個(gè)調(diào)用 是相同的,第四個(gè)調(diào)用將失敗。struct servent *sptr; sptr = getservbyport(htons(53), "udp"); // DNS using UDP sptr = getservbyport(htons(21), "tcp");//FTP using TCP sptr = getservbyport(htons(21), NULL);//FTP using TCP sptr = getservbyport(htons(21), "udp");// this call will fai 由于UDP中沒有使用21端口,所以第4個(gè)會失敗

Linux平臺,從/etc/services文件中讀取信息,一次讀取name(如smtp),port(如25),proto(如tcp),alias(如mail,部分服務(wù)有,部分沒有)。

$ grep -e ^ftp -e ^domain /etc/services ftp 21/tcp ftp 21/udp fsp fspd domain 53/tcp # name-domain server domain 53/udp ftp 21/sctp # FTP$ grep 514 /etc/services shell 514/tcp cmd # no passwords used syslog 514/udp

實(shí)踐

getservbyname

#include "netdb.h" #include "stdio.h" int main() {struct servent *se = NULL;int i = 0;se = getservbyname("domain", "udp");if (!se)return -1;printf("name : %s\n", se->s_name);printf("port : %d\n", ntohs(se->s_port));printf("proto : %s\n", se->s_proto);for (i = 0; se->s_aliases[i]; i++)printf("aliases : %s\n", se->s_aliases[i]);return 0;}

#include "unp.h"int main(int argc, char **argv) {int sockfd, n;char recvline[MAXLINE + 1];struct sockaddr_in servaddr;struct in_addr **pptr;struct in_addr *inetaddrp[2];struct in_addr inetaddr;struct hostent *hp;struct servent *sp;if (argc != 3)err_quit("usage: daytimetcpcli1 <hostname> <service>");if ( (hp = gethostbyname(argv[1])) == NULL) {if (inet_aton(argv[1], &inetaddr) == 0) {err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));} else {inetaddrp[0] = &inetaddr;inetaddrp[1] = NULL;pptr = inetaddrp;}} else {pptr = (struct in_addr **) hp->h_addr_list;}if ( (sp = getservbyname(argv[2], "tcp")) == NULL)err_quit("getservbyname error for %s", argv[2]);for ( ; *pptr != NULL; pptr++) {sockfd = Socket(AF_INET, SOCK_STREAM, 0);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)) == 0)break; /* success */err_ret("connect error");close(sockfd);}if (*pptr == NULL)err_quit("unable to connect");while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) {recvline[n] = 0; /* null terminate */Fputs(recvline, stdout);}exit(0); }

值-結(jié)果參數(shù)

當(dāng)往一個(gè)套接字函數(shù)傳遞一個(gè)套接字地址結(jié)構(gòu)時(shí),該結(jié)構(gòu)總是以引用形式傳遞,也就是說傳遞的是指向該結(jié)構(gòu)的一個(gè)指針。該結(jié)構(gòu)的長度也轉(zhuǎn)為一個(gè)參數(shù)來傳遞,不過起傳遞方式取決于該結(jié)構(gòu)的傳遞方向:是從進(jìn)程到內(nèi)核,還是從內(nèi)核到進(jìn)程

(1) 從進(jìn)程到內(nèi)核傳遞套接字地址結(jié)構(gòu)的函數(shù)有3個(gè):bind、connect和sendto。這些函數(shù)的一個(gè)參數(shù)是指向某個(gè)套接字地址結(jié)構(gòu)的指針。比如:

struct sockaddr_in servaddr; connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)

既然指針和指針?biāo)傅膬?nèi)容的大小都傳遞給了內(nèi)核,內(nèi)核就知道需要從進(jìn)程復(fù)制多少數(shù)據(jù)進(jìn)來

(2) 從內(nèi)核到進(jìn)程傳遞套接字地址結(jié)構(gòu)的函數(shù)有4個(gè):accept、recvfrom、getsockname、getpeername。它們其中有兩個(gè)參數(shù):一個(gè)是指向某個(gè)套接字地址結(jié)構(gòu)的指針、一個(gè)指向表示該結(jié)構(gòu)大小的整形變量的指針

#include <sys/socket.h> /* * 功能: 獲取sockfd對應(yīng)的本地地址,并且存儲于address參數(shù)指定的內(nèi)存中,地址長度由addrlen指向的變量指定: * * 如果實(shí)際長度 大于address指定的內(nèi)存區(qū)大小,那么該地址就會被階段 * * 如果實(shí)際長度 <= address指定的內(nèi)存區(qū)大小,那么該地址能夠正確獲取 * 返回值:成功返回0,失敗-1并設(shè)置error */ int getsockname (int socket, struct sockaddr *__restrict address,socklen_t *__restrict addrlen) /* * 功能: 獲取sockfd對應(yīng)的遠(yuǎn)程地址,并且存儲于address參數(shù)指定的內(nèi)存中,地址長度由addrlen指向的變量指定: * * 如果實(shí)際長度 大于address指定的內(nèi)存區(qū)大小,那么該地址就會被階段 * * 如果實(shí)際長度 <= address指定的內(nèi)存區(qū)大小,那么該地址能夠正確獲取 * 返回值:成功返回0,失敗-1并設(shè)置error */ int getpeername (int __fd, struct sockaddr *__restrict address,socklen_t *__restrict addrlen)

為什么要傳遞結(jié)構(gòu)體大小的指針呢?原因在于:

  • 當(dāng)函數(shù)被調(diào)用是,結(jié)構(gòu)大小是一個(gè),它告訴內(nèi)核該結(jié)構(gòu)的大小,這樣內(nèi)核在寫該結(jié)構(gòu)的時(shí)候不至于越界;
  • 當(dāng)函數(shù)返回時(shí),結(jié)構(gòu)大小又是一個(gè)結(jié)果,它告訴內(nèi)核在該結(jié)構(gòu)中究竟存儲了多少信息。這種類型的參數(shù)稱為值-結(jié)構(gòu)參數(shù)

總結(jié):套接字地址結(jié)構(gòu)是每個(gè)網(wǎng)絡(luò)程序的重要組成部分,我們分配它們,填寫它們,把指向它們的指針傳遞給各個(gè)套接字函數(shù)。有時(shí)我們把指向這些結(jié)構(gòu)之一的指針傳遞給一個(gè)套接字函數(shù),并由該函數(shù)填寫結(jié)構(gòu)內(nèi)容。我們總是以引用形式來傳遞這些結(jié)構(gòu),而且把該結(jié)構(gòu)的大小作為另外一個(gè)參數(shù)來傳遞。當(dāng)一個(gè)套接字函數(shù)需要填寫一個(gè)函數(shù)時(shí),該結(jié)構(gòu)的長度以引用形式傳遞,這樣它的值也可以被函數(shù)更改,我們把這樣的參數(shù)稱為值-結(jié)構(gòu)參數(shù)

總結(jié)

以上是生活随笔為你收集整理的Unix/Linux编程:Internet domain socket的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。