国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 操作系統(tǒng) > 正文

網(wǎng)絡(luò)IPC:套接字之尋址

2024-06-28 13:27:51
字體:
供稿:網(wǎng)友
網(wǎng)絡(luò)ipC:套接字之尋址

在學(xué)習(xí)用套接字做一些有意義的事情之前,需要知道如何確定一個目標(biāo)通信進(jìn)程。

進(jìn)程的標(biāo)識有兩個部分:計(jì)算機(jī)的網(wǎng)絡(luò)地址可以幫助標(biāo)識網(wǎng)絡(luò)上想與之通信的計(jì)算機(jī),而服務(wù)可以幫助標(biāo)識計(jì)算機(jī)上特定的進(jìn)程。

1、字節(jié)序

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

未命名

圖16-1 32位整數(shù)內(nèi)部的字節(jié)序

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

網(wǎng)絡(luò)協(xié)議指定了字節(jié)序,因此異構(gòu)計(jì)算機(jī)系統(tǒng)能夠交換協(xié)議信息而不會混淆字節(jié)序。TCP/IP協(xié)議棧采用大端字節(jié)序。應(yīng)用程序交換格式化數(shù)據(jù)時(shí),字節(jié)序問題就會出現(xiàn)。對于TCP/IP,地址用網(wǎng)絡(luò)字節(jié)序表示,所以應(yīng)用程序有時(shí)需要在處理器的字節(jié)序與網(wǎng)絡(luò)字節(jié)序之間的轉(zhuǎn)換。

對于TCP/IP應(yīng)用程序,提供了四個通用函數(shù)以實(shí)施在處理器字節(jié)序和網(wǎng)絡(luò)字節(jié)序之間的轉(zhuǎn)換

#include <arpa/inet.h>uint32_t htonl(uint32_t hostint32);返回值:以網(wǎng)絡(luò)字節(jié)序表示的32位整型數(shù)uint16_t htons(uint16_t hostint16);返回值:以網(wǎng)絡(luò)字節(jié)序表示的16位整型數(shù)uint32_t ntohl(uint32_t netint32);返回值:以主機(jī)字節(jié)序表示的32位整型數(shù)uint16_t ntohs(uint16_t netint16);返回值:以主機(jī)字節(jié)序表示的16位整型數(shù)

h表示“主機(jī)(host)”字節(jié)序,

n表示“網(wǎng)絡(luò)(network)”字節(jié)序。

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

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

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

2、地址格式

地址標(biāo)識了特定通信域中的套接字端點(diǎn),地址格式與特定的通信域相關(guān)。為使不同格式地址能夠被傳入到套接字函數(shù),地址被強(qiáng)制轉(zhuǎn)換成通用的地址結(jié)構(gòu)sockaddr表示:

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

套接字實(shí)現(xiàn)可以自由地添加額外的成員并且定義sa_data成員的大小。例如在linux中,該結(jié)構(gòu)定義如下:

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

因特網(wǎng)地址定義在<netinet/in.h>中。在IPv4因特網(wǎng)域(AF_INET)中,套接字地址用如下結(jié)構(gòu)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 */};

數(shù)據(jù)類型in_port_t定義為uint16_t。數(shù)據(jù)類型in_addr_t定義成uint32_t。這些整數(shù)類型在<stdint.h>中定義并指定了相應(yīng)的位數(shù)。與IPv4因特網(wǎng)域(AF_INET)相比較,IPv6因特網(wǎng)域(AF_INET6)套接字地址用如下結(jié)構(gòu)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必須的定義,每個實(shí)現(xiàn)可以自由地添加額外的字段。例如,在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相差比較大,它們均被強(qiáng)制轉(zhuǎn)換成sockaddr結(jié)構(gòu)傳入到套接字例程中

有時(shí),需要打印出能被人而不是計(jì)算機(jī)所理解的地址格式。BSD網(wǎng)絡(luò)軟件中包含了函數(shù)inet_addr和inet_ntoa,用于在二進(jìn)制地址格式與點(diǎn)分十進(jìn)制字符串表示(a.b.c.d)之間相互轉(zhuǎn)換。這些函數(shù)僅用于IPv4地址,但功能相似的兩個函數(shù)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

函數(shù)inet_ntop將網(wǎng)絡(luò)字節(jié)序的二進(jìn)制地址轉(zhuǎn)換成文本字符串格式,inet_pton將文本字符串格式轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序的二進(jìn)制地址。參數(shù)domain僅支持兩個值:AF_INET和AF_INET6。

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

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

3、地址查詢

理想情況下,應(yīng)用程序不需要了解套接字地址的內(nèi)部結(jié)構(gòu)。如果應(yīng)用程序只是簡單地傳遞類似于sockaddr結(jié)構(gòu)的套接字地址,并且不依賴于任何協(xié)議相關(guān)的特性,那么可以與提供相同服務(wù)的許多不同協(xié)議協(xié)作。

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

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

通過調(diào)用gethostent,可以找到給定計(jì)算機(jī)的主機(jī)信息。

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

如果主機(jī)數(shù)據(jù)文件沒有打開,gethostent會打開它。函數(shù)gethostent返回文件的下一個條目。函數(shù)sethostent會打開文件,如果文件已經(jīng)被打開,那么將其回繞。函數(shù)endhostent將關(guān)閉文件。

當(dāng)gethostent返回時(shí),得到一個指向hostent結(jié)構(gòu)的指針,該結(jié)構(gòu)可能包含一個靜態(tài)的數(shù)據(jù)緩沖區(qū)。每次調(diào)用gethostent將會覆蓋這個緩沖區(qū)。數(shù)據(jù)結(jié)構(gòu)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 */    ...};

返回的地址采用網(wǎng)絡(luò)字節(jié)序。

兩個附加的函數(shù)gethostbyname和gethostbyaddr,原來包含在hostent函數(shù)里面,現(xiàn)在被認(rèn)為是過時(shí)的,馬上將會看到其替代函數(shù)。

能夠采用一套相似的接口來獲得網(wǎng)絡(luò)名字和網(wǎng)絡(luò)號。

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

結(jié)構(gòu)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 */    ...};

網(wǎng)絡(luò)號按照網(wǎng)絡(luò)字節(jié)序返回。地址類型是一個地址族常量(例如AF_INET)。

可以將協(xié)議名字和協(xié)議號采用以下函數(shù)映射。

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

POSIX.1定義的結(jié)構(gòu)protoent至少包含如下成員:

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

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

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

結(jié)構(gòu)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定義了若干新的函數(shù),允許應(yīng)用程序?qū)⒁粋€主機(jī)名字和服務(wù)名字映射到一個地址,或者相反。這些函數(shù)代替老的函數(shù)gethostbyname和gethostbyaddr。

函數(shù)getaddrinfo允許將一個主機(jī)名字和服務(wù)名字映射到一個地址。

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

需要提供主機(jī)名字、服務(wù)名字,或者兩者都提供。如果僅僅提供一個名字,另外一個必須是個空指針。主機(jī)名字可以是一個節(jié)點(diǎn)名或點(diǎn)分十進(jìn)制記法表示的主機(jī)地址。

函數(shù)getaddrinfo返回一個結(jié)構(gòu)addrinfo的鏈表。可以用freeaddrinfo來釋放一個或多個這種結(jié)構(gòu),這取決于用ai_next字段鏈接起來的結(jié)構(gòu)有多少。

結(jié)構(gòu)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 */    ...};

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

表16-5 addrinfo結(jié)構(gòu)標(biāo)志

未命名

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

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

函數(shù)getnameinfo將地址轉(zhuǎn)換成主機(jī)名或者服務(wù)名。

#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)被轉(zhuǎn)換成主機(jī)名或服務(wù)名。如果host非空,它指向一個長度為hostlen字節(jié)的緩沖區(qū)用于存儲返回的主機(jī)名。同樣,如果service非空,它指向一個長度為servlen字節(jié)的緩沖區(qū)用于存儲返回的服務(wù)名。

參數(shù)flags指定一些轉(zhuǎn)換的控制方式,表16-6總結(jié)了系統(tǒng)支持的標(biāo)志。

表16-6 getnameinfo函數(shù)標(biāo)志

未命名

實(shí)例

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

程序清單16-1 打印主機(jī)和服務(wù)信息

#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系統(tǒng)上運(yùn)行輸出如下:

未命名

4、將套接字與地址綁定

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

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

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

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

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

對于因特網(wǎng)域,如果指定IP地址為INADDR_ANY,套接字端點(diǎn)可以被綁定到所有的系統(tǒng)網(wǎng)絡(luò)接口。這意味著可以收到這個系統(tǒng)所安裝的所有網(wǎng)卡的數(shù)據(jù)包。

可以調(diào)用函數(shù)getsockname來發(fā)現(xiàn)綁定到一個套接字的地址。

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

調(diào)用getsockname之前,設(shè)置alenp為一個指向整數(shù)的指針,該整數(shù)指定緩沖區(qū)sockaddr的大小。返回時(shí),該整數(shù)會被設(shè)置成返回地址的大小。如果該地址和提供的緩沖區(qū)長度不匹配,則將其截?cái)喽粓?bào)錯。如果當(dāng)前沒有綁定到該套接字的地址,其結(jié)果沒有定義。

如果套接字已經(jīng)和對方連接,調(diào)用getpeername來找到對方的地址

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

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

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


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 邯郸市| 百色市| 乌鲁木齐市| 渭南市| 乡城县| 会泽县| 麻城市| 喀什市| 工布江达县| 罗江县| 朝阳县| 苍山县| 临潭县| 迭部县| 伊通| 太仆寺旗| 华池县| 安乡县| 郓城县| 乌拉特后旗| 洞头县| 格尔木市| 白城市| 抚远县| 南丹县| 临沂市| 元朗区| 陵川县| 开鲁县| 新干县| 兴文县| 阳西县| 本溪| 综艺| 蒲城县| 乡宁县| 大渡口区| 阳原县| 五大连池市| 元江| 屯留县|