sockaddr_in和sockaddr_in6


背景:在muduo庫中,InetAddress類是一個包含socket地址的數據類型,包括這個ip地址和端口號。

在里面有一個表示socket地址的union。

1  private:
2   union
3   {
4     struct sockaddr_in addr_;
5     struct sockaddr_in6 addr6_;
6   };

當時覺得這個很奇怪,因為這個東西僅僅就是定義了一種數據類型,並沒有定義一個對應的變量,這個怎么用呢?

經查證 這是一個匿名的聯合體,有其特殊的用法。

參見: https://www.cnblogs.com/guozqzzu/p/3626893.html

--------------------------------------------------------------------------------------------------

Anonymous unions—匿名聯合

在 C++ 我們可以選擇使聯合(union)匿名。如果我們將一個 union 包括在一個結構(structure)的定義中,並且不賦予它對象(object)名稱 (就是跟在

花括號{}后面的名字),這個union 就是匿名的。這種情況下我們可以直接使用 union 中元素的名字來訪問該元素,而不需要再在前面加 union 對象的名稱。

在下面的例子中,我們可以看到這兩種表達方式在使用上的區別:

 

union anonymous union
struct {
  char title[50];
  char author[50];
  union {
    float dollars;
    int yen;
  } price;
} book;
struct {
  char title[50];
  char author[50];
  union {
    float dollars;
    int yen;
  };
} book;

 

以上兩種定義的唯一區別在於左邊的定義中我們給了 union 一個名字 price ,而在右邊的定義中我們沒給。

在使用時的區別是當我們想訪問一個對象(object)的元素 dollars 和 yen 時,在前一種定義的情況下,需要使用:

 

    book.price.dollars;

    book.price.yen;

而在后面一種定義下,我們直接使用:

    book.dollars;

    book.yen;

 

再一次提醒,因為這是一個聯合(union),域 dollars 和 yen 占據的是同一塊內存空間,所以它們不能被用來存儲兩個不同的值。也就是你可以使用

一個 dollars 或 yen 的價格,但不能同時使用兩者。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

總結:

1. 不管是匿名的聯合體或者是命名的聯合體都是共用同一段內存。

2. 匿名的聯合體按照聯合體內的變量直接使用不使用點 . 運算符,聯合體內的變量共享內存且同時只能使用一個。

 

下面來看一下結構體 sockaddr_in 和 sockaddr_in6 的區別:

首先在在網絡編程中是怎樣來表示socket地址的呢?

#include <bits/socket.h>
struct sockaddr
{
       sa_family_t sa_family;
       char sa_data[14];
}
sa_family是 地址族類型sa_family_t 的變量: 指示是Ipv4(AF_INET)還是ipv6(AF_INET6).

------------------------------------------------------

地址族與協議族:
PF_INET  -----  AF_INET   --------   ipv4  ------ 16bit端口號和32bit ipv4地址共6個字節
PF_INET6 -----  AF_INET6  --------   ipv6  ------ 16bit端口號和32bit流標識 128bit ipv6地址,32bit范圍ID共26字節
PF開頭的宏和AF開頭的宏通常擁有一樣的值所以二者經常混用。
------------------------------------------
sa_data:保存真正的socket地址。 只有14個字節

從上面的規定可以看出,這個經典的結構體的大小是不能保存ipv6的,因為他擁有的內存不足以保存。
這樣的話就沒有一個通用的結構體可以放下所有的地址,在LINUX中重新定義了一個結構體:
1 #include <bits/socket.h>
2 struct sockaddr_storage
3 {
4       sa_family_t    sa_family;
5       unsigned long int __ss_align;
6       char __ss_padding[128-sizeof(__ss_align)];
7 }

按照書上的解釋:

這個結構體不僅提供了足夠大的空間用於存放地址值,而且是內存對齊的(這是__ss_align成員的作用)

 

三個問題:

1. 是如何提供了足夠大的空間。

2. 什么是內存對齊,為什么要對齊。

3. __ss_align究竟起到了什么作用。

下面根據我的理解來回答這些問題。

首先,這個結構體里面保存了兩種形式的數據,一個是協議族的類型,另一個是一個socket的套接字地址。

__ss_align就是起到有個對齊的作用,一個結構體有兩個對齊的形式,一個是結構體本身的對齊,另一個是結構體內的成員之間的對齊,根據之前學習的知識。
結構體內的成員按照其自身的對齊規則對齊,結構體本身的初始地址按照成員的最大對齊寬度對齊。

所以這里的
__ss_align確實是起到了對齊的作用,但是有什么用呢?

經查閱資料 說是為了統一操作,包括根據起始地址讀取數據的偏移等。


引:
SOCKADDR_STORAGE可以包含IPv6或IPv4地址,並適當填充該結構以實現64位對齊。這樣的對齊使特定於協議的套接字地址數據結構能夠訪問SOCKADDR_STORAGE結構內的字段,而不會出現對齊問題。
通過填充,SOCKADDR_STORAGE結構的長度為128個字節。


以上兩個是通用的socket套接字地址表示法。
書上說這個顯然不好用,主要體現在:
1. 讀取ip地址和端口號的時候需要進行繁瑣的位操作。


LINUX為各個協議族提供了專門的socket地址結構體。

專用的socket地址
1. unix本地域協議族使用如下專用socket地址結構體:
1 #include <sys/un.h>
2 struct sockaddr_un
3 {
4        sa_family_t sin_family;  /* 地址族:AF_UNIX */
5        char sun_path[108];      /*文件路徑名*/ 
6 };

 

2. TCP/IP 協議簇的ipv4地址表示法:

struct sockaddr_in
{
       sa_family_t sin_family;  /* 地址族:AF_UNIX */
       u_int16_t sin_port;        /* 端口號: 要用網絡直接序表示 */
       struct in_addr sin_addr; /* ipv4地址結構體, 見下面 */
};

其中 struct in_addr 如下:

struct in_addr
{
      u_int32_t s_addr;   /*  ipv4地址, 要用網絡字節序表示  */
};

以上不難看出這個32Bit的空間  是一個用於存放ipv4地址的經過簡單封裝的結構體。

3. ipv6的地址表示結構體:

 1 struct sockaddr_in6
 2 {
 3        sa_family_t sin6_family;   /* 地址協議簇: AF_INET6 */
 4        u_int16_t sin6_port;         /* 端口號, 要用網絡字節序表示 */
 5        u_int32_t sin6_flowinfo     /* 流信息, 應設置為0 */
 6        struct in6_addr sin6_addr; /* ipv6 地址結構體, 見下面 */
 7        u_int32_t sin6_socpe_id;   /* scope ID, 尚處於實驗階段 */
 8 };
 9 
10 struct in6_addr
11 {
12        unsigned char sa_addr[16]; /* ipv6地址, 要使用網絡字節序表示 */
13 };

以上ipv6的表示形式比ipv4要復雜了許多,但大同小異,那些附加的用法暫時不想去管它。

 

總結: 專用地址格式意義明確也好用一些。

 

專用的socket地址和通用地址的關系

引用:

所有專用的socket地址結構體(以及sockaddr_storage)類型的變量在實際使用中都需要轉化為最通用的socket地址類型sockaddr(強制轉換即可),
因為所有socket編程接口使用的地址參數的類型都是sockaddr.
 
 




 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM