亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

網絡IPC:套接字之尋址

2024-06-28 13:27:51
字體:
來源:轉載
供稿:網友
網絡ipC:套接字之尋址

在學習用套接字做一些有意義的事情之前,需要知道如何確定一個目標通信進程。

進程的標識有兩個部分:計算機的網絡地址可以幫助標識網絡上想與之通信的計算機,而服務可以幫助標識計算機上特定的進程。

1、字節序

運行在同一臺計算機上的進程相互通信時,一般不用考慮字節的順序(字節序),字節序是一個處理器架構特性,用于指示像整數這樣的大數據類型的內部字節順序。圖16-1顯示一個32位整數內部的字節是如何排序的。

未命名

圖16-1 32位整數內部的字節序

如果處理器架構支持大端(big-endian)字節序,那么最大字節地址對應于數字最低有效字節(LSB);小端(little-endian)字節序則相反:數字最低字節對應于最小字節地址。注意,不管字節如何排序,數字最高位總是在左邊,最低位總是在右邊。

網絡協議指定了字節序,因此異構計算機系統能夠交換協議信息而不會混淆字節序。TCP/IP協議棧采用大端字節序。應用程序交換格式化數據時,字節序問題就會出現。對于TCP/IP,地址用網絡字節序表示,所以應用程序有時需要在處理器的字節序與網絡字節序之間的轉換。

對于TCP/IP應用程序,提供了四個通用函數以實施在處理器字節序和網絡字節序之間的轉換

#include <arpa/inet.h>uint32_t htonl(uint32_t hostint32);返回值:以網絡字節序表示的32位整型數uint16_t htons(uint16_t hostint16);返回值:以網絡字節序表示的16位整型數uint32_t ntohl(uint32_t netint32);返回值:以主機字節序表示的32位整型數uint16_t ntohs(uint16_t netint16);返回值:以主機字節序表示的16位整型數

h表示“主機(host)”字節序,

n表示“網絡(network)”字節序。

l表示“長(long)”整數(即4個字節),

s表示“短(short)”整數(即2個字節)。

這四個函數定義在<arpa/inet.h>中,也有比較老的系統將其定義在<netinet/in.h>中。

2、地址格式

地址標識了特定通信域中的套接字端點,地址格式與特定的通信域相關。為使不同格式地址能夠被傳入到套接字函數,地址被強制轉換成通用的地址結構sockaddr表示:

struct sockaddr {    sa_family_t    sa_family;        /* address family */    char           sa_data[];        /* variable-length address */    ......    };

套接字實現可以自由地添加額外的成員并且定義sa_data成員的大小。例如在linux中,該結構定義如下:

struct sockaddr {    sa_family_t    sa_family;        /* address family */    char           sa_data[14];    /* variable-length address */};

因特網地址定義在<netinet/in.h>中。在IPv4因特網域(AF_INET)中,套接字地址用如下結構sockaddr_in表示:

struct in_addr {    int_addr_t        s_addr;    /* IPv4 address */};struct sockaddr_in {    sa_family_t       sin_family;      /* address family */    in_port_t         sin_port;        /* port number */    struct in_addr    sin_addr;        /* IPv4 address */};

數據類型in_port_t定義為uint16_t。數據類型in_addr_t定義成uint32_t。這些整數類型在<stdint.h>中定義并指定了相應的位數。與IPv4因特網域(AF_INET)相比較,IPv6因特網域(AF_INET6)套接字地址用如下結構sockaddr_in6表示:

struct in6_addr {    uint8_t    s6_addr[16];    /* IPv6 address */};struct sockaddr_in6 {    sa_family_t        sin6_family;      /* address family */    in_port_t          sin6_port;        /* port number */    uint32_t           sin6_flowinfo;    /* traffic class and flow info */    struct in6_addr    sin6_addr;        /* IPv6 address */    uint32_t           sin6_scope_id;    /* set of interfaces for scope */};

這些是Single UNIX Specification必須的定義,每個實現可以自由地添加額外的字段。例如,在Linux中,sockaddr_in定義如下:

struct sockaddr_in {    sa_family_t       sin_family;      /* address family */    in_port_t         sin_port;        /* port number */    struct in_addr    sin_addr;        /* IPv4 address */    unsigned char     sin_zero[
8
];     /* filler */};

其中成員sin_zero為填充字段,必須全部被置為0。

注意,盡管sockaddr_in與sockaddr_in6相差比較大,它們均被強制轉換成sockaddr結構傳入到套接字例程中。

有時,需要打印出能被人而不是計算機所理解的地址格式。BSD網絡軟件中包含了函數inet_addr和inet_ntoa,用于在二進制地址格式與點分十進制字符串表示(a.b.c.d)之間相互轉換。這些函數僅用于IPv4地址,但功能相似的兩個函數inet_ntop和inet_pton支持IPv4和IPv6地址。

#include <arpa/inet.h>const char *inet_ntop(int domain, const void *restrict addr, char *restrict str, socklen_t size);返回值:若成功則返回地址字符串指針,若出錯則返回NULLint inet_pton(int domain, const char *restrict str, void *restrict addr);返回值:若成功則返回1,若格式無效則返回0,若出錯則返回-1

函數inet_ntop將網絡字節序的二進制地址轉換成文本字符串格式,inet_pton將文本字符串格式轉換成網絡字節序的二進制地址。參數domain僅支持兩個值:AF_INET和AF_INET6。

對于inet_ntop,參數size指定了用以保存文本字符串的緩沖區(str)的大小。兩個常數用于簡化工作:INET_ADDRSTRLEN定義了足夠大的空間來存放表示IPv4地址的文本字符串,INET6_ADDRSTRLEN定義了足夠大的空間來存放表示IPv6地址的文本字符串。

對于inet_pton,如果domain是AF_INET,緩沖區addr需要有足夠大的空間來存放32位地址,如果domain是AF_INET6則需要足夠大的空間來存放128位地址。

3、地址查詢

理想情況下,應用程序不需要了解套接字地址的內部結構。如果應用程序只是簡單地傳遞類似于sockaddr結構的套接字地址,并且不依賴于任何協議相關的特性,那么可以與提供相同服務的許多不同協議協作。

歷史上,BSD網絡軟件提供接口訪問各種網絡配置信息。http://www.CUOXin.com/nufangrensheng/p/3507496.html中,簡要地討論了網絡數據文件和用來訪問這種信息的函數。在本節,將更加詳細地討論一些細節,并且引入新的函數來查詢尋址信息。

這些函數返回的網絡配置信息可能存放在許多地方。它們可以保存在靜態文件中(如/etc/hosts,/etc/services等),或者可以由命名服務管理,例如DNS(Domain Name System)或者NIS(Network Information Service)。無論這些信息放在何處,這些函數同樣能夠訪問它們。

通過調用gethostent,可以找到給定計算機的主機信息。

#include <netdb.h>struct hostent *gethostent(void);返回值:若成功則返回指針,若出錯則返回NULLvoid sethostent(int stayopen);void endhostent(void);

如果主機數據文件沒有打開,gethostent會打開它。函數gethostent返回文件的下一個條目。函數sethostent會打開文件,如果文件已經被打開,那么將其回繞。函數endhostent將關閉文件。

當gethostent返回時,得到一個指向hostent結構的指針,該結構可能包含一個靜態的數據緩沖區。每次調用gethostent將會覆蓋這個緩沖區。數據結構hostent至少包含如下成員:

struct hostent {    char       *h_name;          /* name of host */    char      **h_aliases;       /* pointer to alternate host name array */    int         h_addrtype;      /* address type */    int         h_length;        /* length in bytes of address */    char      **h_addr_list;     /* pointer to array of network addresses */    ...};

返回的地址采用網絡字節序。

兩個附加的函數gethostbyname和gethostbyaddr,原來包含在hostent函數里面,現在被認為是過時的,馬上將會看到其替代函數。

能夠采用一套相似的接口來獲得網絡名字和網絡號。

#include <netdb.h>struct netent *getnetbyaddr(uint32_t net, int type);struct netent *getnetbyname(const char *name);struct netent *getnetent(void);以上三個函數的返回值:若成功則返回指針,若出錯則返回NULLvoid setnetent(int stayopen);void endnetent(void);

結構netent至少包含如下字段:

struct netent {    char       *n_name;        /* network name */    char      **n_aliases;     /* alternate network name array pointer */    int         n_addrtype;    /* address type */    uint32_t    n_net;         /* network number */    ...};

網絡號按照網絡字節序返回。地址類型是一個地址族常量(例如AF_INET)。

可以將協議名字和協議號采用以下函數映射。

#include <netdb.h>struct PRotoent *getprotobyname(const char *name);struct protoent *getprotobynumber(int proto);struct protoent *getprotoent(void);以上所有函數的返回值:若成功則返回指針,出錯則返回NULLvoid setprotoent(int stayopen);void endprotoent(void);

POSIX.1定義的結構protoent至少包含如下成員:

struct protoent {    char     *p_name;         /* protocol name */    char    **p_aliases;      /* pointer to alternate protocol name array */    int       p_proto;        /* protocol number */    ...};

服務是由地址的端口號部分表示的。每個服務由一個唯一的、熟知的端口號來提供。采用函數getservbyname可以將一個服務名字映射到一個端口號,函數getservbyport將一個端口號映射到一個服務名,或者采用函數getservent順序掃描服務數據庫。

#include <netdb.h>struct servent *getservbyname(const char *name, const char *proto);struct servent *getservbyport(int port, const char *proto);struct servent *getservent(void);以上所有函數的返回值:若成功則返回指針,出錯則返回NULLvoid setservent(int stayopen);void endservent(void);

結構servent至少包含如下成員:

struct servent {    char      *s_name;         /* service name */    char     **s_aliases;      /* pointer to alternate service name array */    int        s_port;         /* port number */    char      *s_proto;        /* name of protocol */    ...};

POSIX.1定義了若干新的函數,允許應用程序將一個主機名字和服務名字映射到一個地址,或者相反。這些函數代替老的函數gethostbyname和gethostbyaddr。

函數getaddrinfo允許將一個主機名字和服務名字映射到一個地址。

#include <sys/socket.h>#include <netdb.h>int getaddrinfo(const char *restrict host,                const char *restrict service,                const struct addrinfo *restrict hint,                struct addrinfo **restrict res);返回值:若成功則返回0,出錯則返回非0錯誤碼void freeaddrinfo(struct addrinfo *ai);

需要提供主機名字、服務名字,或者兩者都提供。如果僅僅提供一個名字,另外一個必須是個空指針。主機名字可以是一個節點名或點分十進制記法表示的主機地址。

函數getaddrinfo返回一個結構addrinfo的鏈表??梢杂胒reeaddrinfo來釋放一個或多個這種結構,這取決于用ai_next字段鏈接起來的結構有多少。

結構addrinfo的定義至少包含如下成員:

struct addrinfo {    int                  ai_flags;         /* customize behavior */    int                  ai_family;        /* address family */    int                  ai_socktype;      /* socket type */    int                  ai_protocol;      /* protocol */    socklen_t            ai_addrlen;       /* length in bytes of address */    struct sockaddr     *ai_addr;          /* address */    char                *ai_canonname;     /* canonical(與aliases相對) name of host */    struct addrinfo     *ai_next;          /* next in list */    ...};

根據某些規則,可以提供一個可選的hint來選擇地址。hint是一個用于過濾地址的模板,僅使用ai_family、ai_flags、ai_protocol和ai_socktype字段。剩余的整數字段必須設為0,并且指針字段為空。表15-6總結了在ai_flags中所用的標志,這寫標志用來指定如何處理地址和名字。

表16-5 addrinfo結構標志

未命名

如果getaddrinfo失敗,不能使用perror或strerror來生成錯誤消息。替代地,調用gai_strerror將返回的錯誤碼轉換成錯誤消息。

#include <netdb.h>const char *gai_strerror(int error);返回值:指向描述錯誤的字符串的指針

函數getnameinfo將地址轉換成主機名或者服務名。

#include <sys/socket.h>#include <netdb.h>int getnameinfo(const struct sockaddr *restrict addr,             socklen_t alen, char *restrict host,             socklen_t hostlen, char *restrict service,             socklen_t servlen, unsigned int flags);返回值:若成功則返回0,出錯則返回非0值

套接字地址(addr)被轉換成主機名或服務名。如果host非空,它指向一個長度為hostlen字節的緩沖區用于存儲返回的主機名。同樣,如果service非空,它指向一個長度為servlen字節的緩沖區用于存儲返回的服務名。

參數flags指定一些轉換的控制方式,表16-6總結了系統支持的標志。

表16-6 getnameinfo函數標志

未命名

實例

程序清單16-1說明了函數getaddrinfo的使用方法。

程序清單16-1 打印主機和服務信息

#include "apue.h"#include <netdb.h>#include <arpa/inet.h>#if defined(BSD) || defined(MACOS)#include <sys/socket.h>#include <netinet/in.h>#endifvoid print_family(struct addrinfo *aip){    printf(" family ");    switch(aip->ai_family)    {        case AF_INET:            printf("inet");            break;        case AF_INET6:            printf("inet6");            break;        case AF_UNIX:            printf("unix");            break;        case AF_UNSPEC:            printf("unspecified");            break;        default:            printf("unknown");    }}void print_type(struct addrinfo *aip){    printf(" type ");    switch(aip->ai_socktype)    {        case SOCK_STREAM:            printf("stream");            break;        case SOCK_DGRAM:            printf("datagram");            break;        case SOCK_SEQPACKET:            printf("seqpacket");            break;        case SOCK_RAW:            printf("raw");            break;        default:            printf("unknown (%d)", aip->ai_socktype);    }}voidprint_protocol(struct addrinfo *aip){    printf(" protocol ");    switch(aip->ai_protocol)    {        case 0:            printf("default");            break;        case IPPROTO_TCP:            printf("TCP");            break;        case IPPROTO_UDP:            printf("UDP");            break;        case IPPROTO_RAW:            printf("raw");            break;        default:            printf("unknown (%d)", aip->ai_protocol);    }}voidprint_flags(struct addrinfo *aip){    printf("flags");    if(aip->ai_flags == 0)    {        printf(" 0");    }    else    {        if(aip->ai_flags & AI_PASSIVE)            printf(" passive");        if(aip->ai_flags & AI_CANONNAME)            printf(" canon");        if(aip->ai_flags & AI_NUMERICHOST)            printf(" numhost");#if defined(AI_NUMERICSERV)        if(aip->ai_flags & AI_NUMERICSERV)            printf(" numserv");#endif#if defined(AI_V4MAPPED)        if(aip->ai_flags & AI_V4MAPPED)            printf(" v4mapped");#endif#if defined(AI_ALL)        if(aip->ai_flags & AI_ALL)            printf(" all");#endif    }}intmain(int argc, char *argv[]){    struct addrinfo        *ailist, *aip;    struct addrinfo         hint;    struct sockaddr_in     *sinp;    const char             *addr;    int                     err;    char                    abuf[INET_ADDRSTRLEN];    if(argc != 3)        err_quit("usage: %s nodename service", argv[0]);    hint.ai_flags = AI_CANONNAME;    hint.ai_family = 0;    hint.ai_socktype = 0;    hint.ai_protocol = 0;    hint.ai_addrlen = 0;    hint.ai_canonname = NULL;    hint.ai_addr = NULL;    hint.ai_next = NULL;    if((err = getaddrinfo(argv[1], argv[2], &hint, &ailist)) != 0)        err_quit("getaddrinfo error: %s", gai_strerror(err));    for(aip = ailist; aip != NULL; aip = aip->ai_next)    {        print_flags(aip);        print_family(aip);        print_type(aip);        print_protocol(aip);        printf("/n/thost %s", aip->ai_canonname?aip->ai_canonname:"-");        if(aip->ai_family == AF_INET)        {            sinp = (struct sockaddr_in *)aip->ai_addr;            addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET_ADDRSTRLEN);            printf(" address %s", addr?addr:"unknown");            printf(" port %d", ntohs(sinp->sin_port));        }        printf("/n");    }    exit(0);}

程序在Linux系統上運行輸出如下:

未命名

4、將套接字與地址綁定

與客戶端的套接字關聯的地址沒有太大的意義,可以讓系統選一個默認的地址。然而,對于服務器,需要給一個接收客戶端請求的套接字綁定一個眾所周知的地址。客戶端應有一種方法來發現用以連接服務器的地址,最簡單的方法就是為服務器保留一個地址并且在/etc/services或者某個名字服務(name service)中注冊。

可以用bind函數將地址綁定到一個套接字。

#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t len);返回值:若成功則返回0,出錯則返回-1

對于所能使用的地址有一些限制:

  • 在進程所運行的機器上,指定的地址必須有效,不能指定一個其他機器的地址。
  • 地址必須和創建套接字時的地址族所支持的格式相匹配。
  • 端口號必須不小于1024,除非該進程具有相應的特權(即為超級用戶)。
  • 一般只有套接字端點能夠與地址綁定,盡管有些協議允許多重綁定。

對于因特網域,如果指定IP地址為INADDR_ANY,套接字端點可以被綁定到所有的系統網絡接口。這意味著可以收到這個系統所安裝的所有網卡的數據包。

可以調用函數getsockname來發現綁定到一個套接字的地址。

#include <sys/socket.h>int getsockname(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp);返回值:若成功則返回0,出錯則返回-1

調用getsockname之前,設置alenp為一個指向整數的指針,該整數指定緩沖區sockaddr的大小。返回時,該整數會被設置成返回地址的大小。如果該地址和提供的緩沖區長度不匹配,則將其截斷而不報錯。如果當前沒有綁定到該套接字的地址,其結果沒有定義。

如果套接字已經和對方連接,調用getpeername來找到對方的地址。

#include <sys/socket.h>int getpeername(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp);返回值:若成功則返回0,若出錯則返回-1

除了返回的是對方的地址之外,函數getpeername和getsockname一樣。

本篇博文內容摘自《UNIX環境高級編程》(第2版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
www国产91| 欧美日韩精品在线观看| 亚洲成人av资源网| 欧美大片在线看免费观看| 亚洲国产精品一区二区久| 亚洲国产日韩精品在线| 色午夜这里只有精品| 日本成人在线视频网址| 中文字幕一精品亚洲无线一区| 欧美激情一区二区三区久久久| 羞羞色国产精品| 日韩亚洲欧美中文高清在线| 久久久噜久噜久久综合| 国产精品视频在线观看| 91国语精品自产拍在线观看性色| 久久婷婷国产麻豆91天堂| 日韩视频在线一区| 97精品久久久中文字幕免费| 欧美在线免费观看| 欧美激情精品久久久久久| 亚洲欧美在线一区二区| 啪一啪鲁一鲁2019在线视频| 久久久久一本一区二区青青蜜月| 大桥未久av一区二区三区| 国产精品久久久久999| 欧美精品一区二区免费| 91天堂在线视频| 精品国产一区二区三区四区在线观看| 国产久一一精品| 欧美丝袜美女中出在线| 国产精品扒开腿爽爽爽视频| 亚州成人av在线| 亚洲色图av在线| 国产欧美一区二区| 最新69国产成人精品视频免费| 亚洲香蕉av在线一区二区三区| 国产综合福利在线| 久久中文久久字幕| 亚洲精品国产精品国自产观看浪潮| 色七七影院综合| 国产精品精品久久久久久| 亚洲国产精品99久久| 中文字幕久热精品在线视频| 北条麻妃在线一区二区| 亚洲综合色av| 欧美视频在线免费看| 国产精品福利在线观看网址| 日韩欧美999| 欧美亚洲另类在线| 国产欧美在线观看| 国产偷亚洲偷欧美偷精品| 亚洲aaa激情| 日韩有码在线播放| 国产成人激情视频| 国产精品欧美激情在线播放| 成人www视频在线观看| 欧美精品久久久久久久久久| 欧美日韩国产成人高清视频| 91影院在线免费观看视频| 国产精品观看在线亚洲人成网| 在线精品国产欧美| 国产精品99蜜臀久久不卡二区| 中文字幕日韩av综合精品| 福利二区91精品bt7086| 国产精品久久久久久久久粉嫩av| 久久久久久亚洲精品中文字幕| 国产精品美女免费| 欧美人与性动交| 亚洲理论在线a中文字幕| 热re91久久精品国99热蜜臀| 成人淫片在线看| 久久在线观看视频| 欧美另类99xxxxx| 亚洲成人精品视频| 欧洲精品久久久| 久久久久久久国产精品| 综合久久五月天| 亚洲精品久久久久久久久久久久| 久久久久久久久久亚洲| 国产91精品久久久| 尤物九九久久国产精品的特点| 亚洲福利在线播放| 国模gogo一区二区大胆私拍| 亚洲成人精品视频在线观看| 国产精品久久激情| 国产一区二区欧美日韩| 国产精品视频yy9099| 国产精品一区二区三区久久久| 国产亚洲精品久久久优势| 亚洲第一页中文字幕| 欧美大奶子在线| 成人在线视频网| 欧美裸体视频网站| 欧美激情在线视频二区| 国产福利成人在线| 成人444kkkk在线观看| 精品久久香蕉国产线看观看亚洲| 在线电影欧美日韩一区二区私密| 欧美激情网友自拍| 最近2019年好看中文字幕视频| 成人中文字幕+乱码+中文字幕| 欧美日韩另类视频| 久久成人亚洲精品| 91久久久久久久一区二区| 国产精品第一页在线| 国产激情999| 国产精品福利在线观看| 亚洲小视频在线观看| 欧美最顶级的aⅴ艳星| 国产精自产拍久久久久久蜜| 欧美日韩国产专区| 久久久久久久激情视频| 亚洲欧美日韩中文在线制服| 欧美午夜激情小视频| 亚洲精品影视在线观看| 正在播放欧美一区| 亚洲国产成人精品女人久久久| 欧美日韩亚洲高清| 久久久久久久影院| 久久久在线观看| 国产欧美日韩免费| 亚洲一区二区三区成人在线视频精品| 久久久国产成人精品| 成人福利免费观看| 久久精品成人一区二区三区| 久久久999国产精品| 国产精品一区二区久久| 亚洲激情小视频| 国产精品视频在线观看| 成人妇女淫片aaaa视频| 日韩av影视综合网| 久久精品免费播放| 国产精品www| 秋霞成人午夜鲁丝一区二区三区| 国产精品美女在线| 久久精品99无色码中文字幕| 久久精品中文字幕| 热re91久久精品国99热蜜臀| 日韩在线观看成人| 国产精品劲爆视频| 日本sm极度另类视频| 国产精品久久久久久久久久新婚| 亚洲精品美女视频| 欧美壮男野外gaytube| 久久精品91久久久久久再现| 国产成人亚洲综合91| 在线电影中文日韩| 日韩高清有码在线| 精品成人久久av| 上原亚衣av一区二区三区| 欧洲成人免费视频| 亚洲精品久久久久久下一站| 91av中文字幕| 国产一区二区三区三区在线观看| 2025国产精品视频| 欧美电影在线观看高清| 日韩免费在线视频| 中文字幕一精品亚洲无线一区| 日韩一区二区久久久| 午夜美女久久久久爽久久| 欧美高跟鞋交xxxxxhd| 亚洲成年人在线播放| 精品久久久久久久久国产字幕| 亚洲成av人影院在线观看|