日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

socket最開(kāi)始的含義是一個(gè)IP地址和端口隊(duì)(ip,port)。它唯一地表示了使用TCP通信的一端。這就是socket地址。

主機(jī)字節(jié)序和網(wǎng)絡(luò)字節(jié)序

現(xiàn)在CPU的累加器一次都能裝載(至少)4字節(jié)(這里考慮32位機(jī)器,下同),即一個(gè)整數(shù)。那么這4個(gè)字節(jié)在內(nèi)存中排列的順序?qū)⒂绊懰焕奂悠餮b載成的整數(shù)的值。這就是字節(jié)序的問(wèn)題。

字節(jié)序分為大端字節(jié)序(big endian)和小端字節(jié)序(little endian)。大端字節(jié)序是指一個(gè)整數(shù)的高位字節(jié)(23 ~ 31 bit)存儲(chǔ)在內(nèi)存的地址處,低位字節(jié)(0~7 bit)存儲(chǔ)在內(nèi)存的高地址處。小端字節(jié)序則指整數(shù)的高位字節(jié)序存儲(chǔ)在內(nèi)存的高地址處,而低位字節(jié)序則存在在內(nèi)存的低地址處。

下面的代碼是檢查機(jī)器的字節(jié)序:

#include <stdio.h>

void byteorder()
{
    union{
        short value;
        char union_bytes[sizeof(short)];
    }test;

    test.value = 0x0102;

    if((test.union_bytes[0] == 1) && (test.union_bytes[1] == 2)){
        printf("big endiann");
    }
    else if((test.union_bytes[0] == 2) && (test.union_bytes[1] == 1)){
        printf("little endiann");
    }
    else{
        printf("unknownn");
    }

}

int main(int argc, char const *argv[])
{
    byteorder();
    return 0;
}

當(dāng)格式化的數(shù)據(jù)(比如32bit整型數(shù)和16bit短型數(shù))在兩臺(tái)使用不同字節(jié)序的主機(jī)之間傳遞時(shí),接收端必然錯(cuò)誤地解釋之。

解決問(wèn)題的方法是:發(fā)送端總是把要發(fā)送的數(shù)據(jù)轉(zhuǎn)化成大端字節(jié)序再發(fā)送,而接收端知道對(duì)方傳送過(guò)來(lái)的數(shù)據(jù)總是采用大端字節(jié)序,所以接收端可以根據(jù)自身采用的字節(jié)序決定是否對(duì)接收到的數(shù)據(jù)進(jìn)行轉(zhuǎn)換(小端機(jī)轉(zhuǎn)換,大端機(jī)不轉(zhuǎn)換)。因此大端字節(jié)序也稱為網(wǎng)絡(luò)字節(jié)序,它給所有接受數(shù)據(jù)的主機(jī)提供了一個(gè)正確解釋收到的格式化數(shù)據(jù)的保證。

需要指出的是,即使是同一臺(tái)機(jī)器上的兩個(gè)進(jìn)程(比如一個(gè)由C語(yǔ)言,另一個(gè)JAVA編寫)通信,也要考慮字節(jié)序的問(wèn)題(JAVA虛擬機(jī)采用大端字節(jié)序)。

linux提供了4個(gè)函數(shù)來(lái)完成主機(jī)字節(jié)序和網(wǎng)絡(luò)字節(jié)序之間的轉(zhuǎn)換。

#include <netinet/in.h>

unsigned long int htonl(unsigned long int hostlong);

unsigned short int htons(unsigned short int hostshort);

unsigned long int ntohl(unsigned long int netlong);

unsigned short int ntohs(unsigned short int netshort);

它們的含義很明確,比如htonl表示“host to network long",即將長(zhǎng)整型(32bit)的主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)。這四個(gè)函數(shù)中,長(zhǎng)整型函數(shù)通常用來(lái)轉(zhuǎn)換IP地址,短整型函數(shù)用來(lái)轉(zhuǎn)換端口號(hào)。(當(dāng)然不限于此。任何格式化的數(shù)據(jù)通過(guò)網(wǎng)絡(luò)傳輸時(shí),都應(yīng)該使用這些函數(shù)來(lái)轉(zhuǎn)化字節(jié)序)。

通用socket地址

socket網(wǎng)絡(luò)編程接口中表示socket地址的是結(jié)構(gòu)體sockaddr,其定義如下:

#include <bits/socket.h>
struct sockaddr
{
    sa_family_t sa_family;
    char sa_data[14];
};

sa_family成員是地址族類型(sa_family_t)的變量。地址族類型通常與協(xié)議族類型對(duì)應(yīng)。

常見(jiàn)的協(xié)議族(protocol family,也稱domain)和對(duì)應(yīng)的地址族如下表:

Linux網(wǎng)絡(luò)API - socket地址API

 

宏P(guān)F_*和AF_*都定義在bits/socket.h頭文件中,且后者與前者有完全相同的值,所以二者通常混用。

sa_data成員用于存放socket地址值。但是不同的協(xié)議族的地址值具有不同的含義和長(zhǎng)度。如下表所示:

Linux網(wǎng)絡(luò)API - socket地址API

 

由此可以發(fā)現(xiàn),14字節(jié)的sa_data根本無(wú)法完全容納多數(shù)協(xié)議族的地址值。因此,Linux定義了下面這個(gè)新的通用socket地址結(jié)構(gòu)體:

#include <bits/socket.h>
struct sockaddr_storage
{
    sa_family_t sa_family;
    unsigned long int __ss_align;
    char __ss_padding[128 - sizeof(__ss_align)];
};

這個(gè)結(jié)構(gòu)體不僅提供了足夠大的空間用于存放地址值,而且是內(nèi)存對(duì)齊的(這是__ss_align成員的作用)。

專用socket地址

上面這兩個(gè)通用socket地址結(jié)構(gòu)體顯然很不好用,比如設(shè)置與獲取IP地址和端口號(hào)就需要執(zhí)行煩瑣的位操作。所以Liunx為各個(gè)協(xié)議族提供了專門的socket地址結(jié)構(gòu)體。

UNIX本地域協(xié)議族使用如下專用socket地址結(jié)構(gòu)體:

#include <sys/un.h>
struct sockaddr_un
{
    sa_family_t sin_family; /*地址族: AF_UNIX*/
    char sun_path[108];  /*文件路徑名*/
};

TCP/IP協(xié)議族有sockaddr_in和sockaddr_in6兩個(gè)專用socket地址結(jié)構(gòu)體,它們分別用于IPv4和IPv6:

struct sockaddr_in
{
    sa_family_t sin_family;     /*地址族:AF_INET*/
    u_int16_t sin_port;         /*端口號(hào),要用網(wǎng)絡(luò)字節(jié)序表示*/
    struct in_addr sin_addr;    /*IPv4地址結(jié)構(gòu)體*/
};

struct in_addr
{
    u_int32_t s_addr;           /*IPv4地址, 要用網(wǎng)絡(luò)字節(jié)序表示*/
};

struct sockaddr_in6
{
    sa_family_t sin6_family;     /*地址族:AF_INET6*/
    u_int16_t sin6_port;         /*端口號(hào),要用網(wǎng)絡(luò)字節(jié)序表示*/
    u_int32_t sin6_flowinfo;     /*流信息,應(yīng)設(shè)置為0*/
    struct in6_addr sin6_addr;   /*IPv6地址結(jié)構(gòu)體*/
    u_int32_t sin6_scope_id;     /*scope ID, 尚處于實(shí)驗(yàn)階段*/
};

struct in6_addr
{
    unsigned char sa_addr[16];           /*IPv6地址, 要用網(wǎng)絡(luò)字節(jié)序表示*/
};

這兩個(gè)專用socket地址結(jié)構(gòu)體各字段的含義很明確。

所有專用socket地址(以及sockaddr_storage)類型的變量在實(shí)際使用時(shí)都需要轉(zhuǎn)化為通用socket地址類型sockaddr(強(qiáng)制轉(zhuǎn)化即可),因?yàn)樗衧ocket編程接口使用的地址參數(shù)的類型都是sockaddr。

IP地址轉(zhuǎn)換函數(shù)

通常,人們習(xí)慣用可讀性好的字符串來(lái)表示IP地址,比如用點(diǎn)分十進(jìn)制字符串表示IPv4地址,以及用十六進(jìn)制字符串表示IPv6地址。但編程中我們需要先把它們轉(zhuǎn)化為整數(shù)(二進(jìn)制數(shù))方能使用。而記錄日志則相反,我們要把整數(shù)表示的IP地址轉(zhuǎn)化為可讀的字符串。

下面3個(gè)函數(shù)可用于用點(diǎn)分十進(jìn)制字符串表示的字符串表示的IPv4地址和用網(wǎng)絡(luò)字節(jié)序整數(shù)表示的IPv4地址之間轉(zhuǎn)換:

#include <arpa/inet.h>

in_addr_t inet_addr(const char * strptr);

int inet_aton(const char *cp, struct in_addr *inp);

char *inet_ntoa(struct in_addr in);

inet_addr函數(shù)將用點(diǎn)分十進(jìn)制串表示的IPv4地址轉(zhuǎn)化為用網(wǎng)絡(luò)字節(jié)序整數(shù)表示的IPv4地址。失敗返回INADDR_NONE。

inet_aton函數(shù)完成和inet_addr同樣的功能,但是將轉(zhuǎn)化結(jié)果存儲(chǔ)與參數(shù)inp指向的地址結(jié)構(gòu)中。它成功返回1,失敗則返回0。

inet_ntoa函數(shù)將用網(wǎng)絡(luò)字節(jié)序整數(shù)表示的IPv4地址轉(zhuǎn)換為用點(diǎn)分十進(jìn)制字符串表示的IPv4地址。但需要注意的是:該函數(shù)內(nèi)部用一個(gè)靜態(tài)變量存儲(chǔ)轉(zhuǎn)化結(jié)果,函數(shù)的返回值指向該靜態(tài)內(nèi)存,因此inet_ntoa是不可重入的。

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char const *argv[])
{
    char ip1[] = "1.2.3.4";
    char ip2[] = "10.194.71.60";
    
    struct in_addr inAddr1;    
    struct in_addr inAddr2;    

    inet_aton(ip1, &inAddr1);
    inet_aton(ip2, &inAddr2);

    char *szValue1 = inet_ntoa(inAddr1);
    char *szValue2 = inet_ntoa(inAddr2);

    printf("address1: %sn", szValue1);
    printf("address2: %sn", szValue2);

    return 0;
}
Linux網(wǎng)絡(luò)API - socket地址API

不可重入的inet_ntoa函數(shù)實(shí)驗(yàn)結(jié)果

下面這對(duì)更新的函數(shù)也能完成和前面3和函數(shù)一樣的功能,并且它們使用適用于IPv4地址和IPv6地址:

#include <arpa/inet.h>

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

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

inet_pton函數(shù)將用于字符串表示的IP地址src(用點(diǎn)分十進(jìn)制字符串表示的IPv4地址或用十六進(jìn)制字符串表示的IPv6地址)轉(zhuǎn)換成用網(wǎng)絡(luò)字節(jié)序整數(shù)表示的IP地址,并把轉(zhuǎn)換結(jié)果存儲(chǔ)于dst指向的內(nèi)存中。其中,af參數(shù)指定地址族:

  • AF_INET
  • AF_INET6

inet_pton成功返回1,失敗則返回0并設(shè)置errno。

inet_ntop函數(shù)進(jìn)行相反的轉(zhuǎn)換,前三個(gè)參數(shù)的含義與inet_pton參數(shù)相同,最后一個(gè)cnt指定目標(biāo)存儲(chǔ)單元的大小。下面兩個(gè)宏能幫助我們指定這個(gè)大小(分別用于IPv4和IPv6):

#include <netinet/in.h>

#define INET_ADDRSTRLEN 16

#define INET6_ADDRSTRLEN 46

inet_ntop成功時(shí)返回目標(biāo)存儲(chǔ)單元的地址,失敗返回NULL并設(shè)置errno。

#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>

int main()
{
    char *ipv4 = "10.0.0.200";
    char *ipv6 = "fe80::4bde:83d8:dbcf:72f3";

    in_addr inAddr4;
    in6_addr inAddr6;

    inet_pton(AF_INET, ipv4, &inAddr4);
    inet_pton(AF_INET6, ipv6, &inAddr6);

    char addr1[INET_ADDRSTRLEN];
    char addr2[INET6_ADDRSTRLEN];

    if(addr1 == inet_ntop(AF_INET, (void *)&inAddr4, addr1, INET_ADDRSTRLEN)){
        printf("truen");
    }

    printf("IPv4 addr: %sn", inet_ntop(AF_INET, (void *)&inAddr4, addr1, INET_ADDRSTRLEN));
    printf("IPv4 addr: %sn", inet_ntop(AF_INET6, (void*)&inAddr6, addr2, INET6_ADDRSTRLEN));

    return 0;
}
Linux網(wǎng)絡(luò)API - socket地址API

 

分享到:
標(biāo)簽:地址 socket
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定