我正在按照教程學習 HTTP 協議,該教程提供了一段易于理解的代碼,這是其中的一部分。
struct sockaddr_in address;
...
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
memset(address.sin_zero, '\0', sizeof address.sin_zero);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("In bind");
exit(EXIT_FAILURE);
}
示例代碼運行良好,雖然我不理解兩個結構之間的某種傳輸。
struct sockaddr_inin的定義<netinet/in.h>是
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddrin的定義<sys/socket.h>是
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
它們有不同的結構,“轉移/鑄造”在那里如何作業?
uj5u.com熱心網友回復:
我認為這個轉換打破了嚴格的別名規則,然后如果bind函式取消參考指標,則是未定義的行為。
在實踐中,代碼假定 的所有欄位struct sockaddr_in都是連續的,因此您可以以 astruct sockaddr_in或struct sockaddr等效的方式訪問位元組緩沖區。但是結構的欄位不能保證是連續的。如果in_port_t是2個位元組長,例如,那里很可能之間的孔sin_port和sin_addr具有32位元組的機器的編譯器,因為它可能需要對準sin_addr欄位上32位元組的地址。
當您開發通信介面驅動程式時,這種編碼方式很常見:您會收到一個需要解釋為資料結構的位元組緩沖區(例如:第一個位元組是地址,后面的位元組是長度,等等)。從一個結構轉換到另一個結構避免了復制資料。
請注意,通常編譯器提供非標準 C 方法來保證結構的所有欄位都是連續的。例如使用 gcc 它是__attribute__((packed))
現在,回答你的問題:只要結構被打包并且沒有未定義的行為,演員基本上什么都不做。sa_data將是位于欄位之后的位元組陣列sin_family。所以這個陣列將由sin_port, 后跟sin_addr陣列組成sin_zero。
uj5u.com熱心網友回復:
鑄造作業。查看兩個結構:
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
前兩個成員, sin_lenand sa_len, sin_familyandsa_family不會有問題,因為它們具有相同的資料型別。sa_family_t兩端的填充作業完全相同。
看參考資料,
in_port_t相當于型別uint16_t中所描述的<inttypes.h>
in_addr_t相當于型別uint32_t中所描述<inttypes.h>
對于windows,struct in_addr如下所示:
struct in_addr {
union {
struct {
u_char s_b1;
u_char s_b2;
u_char s_b3;
u_char s_b4;
} S_un_b;
struct {
u_short s_w1;
u_short s_w2;
} S_un_w;
u_long S_addr;
} S_un;
};
對于linux 來說是:
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
您可能遇到的整個混亂是因為內容如何對齊。然而,這是一個深思熟慮的歷史設計。它旨在適應設計中與實作相關的方面。其次,依賴于實作——它指的in_addr_t是在所有系統中的實作不一致的事實,如上所示。
In a nutshell, this entire magic works, because of the 2 things: The exact size and padding nature of the first two members and then lastly the data type of sa_data[14] is char, or more precisely an array of a 1-byte data-type. This design trick with union inside a struct has been widely used.
Unix Network Programming Volume 1 states:
The reason the sin_addr member is a structure, and not just an in_addr_t, is historical. Earlier releases (4.2BSD) defined the in_addr structure as a union of various structures, to allow access to each of the 4 bytes and to both of the 16-bit values contained within the 32-bit IPv4 address. This was used with class A, B, and C addresses to fetch the appropriate bytes of the address. But with the advent of subnetting and then the disappearance of the various address classes with classless addressing, the need for the union disappeared. Most systems today have done away with the union and just define in_addr as a structure with a single in_addr_t member.
Not what you asked for, but good to know:
The same header states:
The
sockaddr_instructure is used to store addresses for the Internet address family. Values of this type shall be cast by applications to structsockaddrfor use with socket functions.
So, sockaddr_in is a struct specific to IP-based communication and sockaddr is more of a generic structure for socket operations.
Just a try:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(void)
{
printf("sizeof(struct sockaddr_in) = %zu bytes\n", sizeof(struct sockaddr_in));
printf("sizeof(struct sockaddr) = %zu bytes\n", sizeof(struct sockaddr));
return 0;
}
Prints:
sizeof(struct sockaddr_in) = 16 bytes
sizeof(struct sockaddr) = 16 bytes
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/359869.html
上一篇:讀取指定為引數的檔案并回傳其行
下一篇:C中的一維和二維陣列
