插座结构:铸造?

Lar*_*rry 1 c sockets struct casting

上下文

我是自学的套接字是如何工作的.承认,我不是C大师,但学得很快.

我看了这个页面:

http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/rzab6/rzab6xafunixsrv.htm

问题

我被困在这条线上:

rc = bind(sd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
Run Code Online (Sandbox Code Playgroud)

我无法想象我们从这个演员(struct sockaddr*)和serveraddr得到了什么.

到目前为止我的测试:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>


int main(void)
{

/*JUST TESTING THE CAST THING NOTHING ELSE HERE*/

struct sockaddr_in localaddr ;
struct sockaddr_in * mi;
struct sockaddr * toto;

localaddr.sin_family = AF_INET;

localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
localaddr.sin_port = 38999;

/* DID I DEFINED MI CORRECTLY ? */
mi = (struct sockaddr*)&localaddr;
toto = (struct sockaddr*)&localaddr;

printf("mi %d\n",mi->sin_family);
printf("mi %d\n",mi->sin_port);
printf("toto %d\n",toto->sa_family);
/*ERROR*/
printf("toto %d\n",toto->sa_port);

}
Run Code Online (Sandbox Code Playgroud)

总结

有人可以告诉我什么是真正传递给关于结构演员的绑定函数?

我们在该结构中拥有哪些成员?

我怎么检查呢?

谢谢

Cro*_*man 5

这是struct sockaddr:

struct sockaddr {
    uint8_t      sa_len;
    sa_family_t  sa_family;
    char         sa_data[14];
};
Run Code Online (Sandbox Code Playgroud)

例如,这里是struct sockaddr_in:

struct sockaddr_in {
    uint8_t        sa_len;
    sa_family_t    sa_family;
    in_port_t      sin_port;
    struct in_addr sin_addr;
    char           sin_zero[8];
};
Run Code Online (Sandbox Code Playgroud)

并且struct sockaddr_in6:

struct sockaddr_in6 {
    uint8_t         sa_len;
    sa_family_t     sa_family;
    in_port_t       sin_port;
    uint32_t        sin6_flowinfo;
    struct in6_addr sin6_addr;
};
Run Code Online (Sandbox Code Playgroud)

你会注意到他们都共享前两个成员.因此,类似的函数bind()可以接受指向泛型的指针,struct sockaddr并且知道,无论struct它实际指向什么具体,它都具有sa_lensa_family共同(并且"共同"在这里意味着"在内存中以相同的方式布局",所以两个structs 都有一个sa_family成员,但是它们在两个不同的structs中处于完全不同的位置是没有任何奇怪的.技术上sa_len是可选的,但是如果它不存在,那么没有一个struct会有它,所以sa_family仍然是以相同的方式对齐,并且通常sa_family_t会增加数据类型以弥补大小的差异).因此,它可以访问sa_family并确定它的确切类型struct,并相应地进行,例如:

int bind(int socket, const struct sockaddr *address, socklen_t address_len) {
    if ( address->sa_family == AF_INET ) {
        struct sockaddr_in * real_struct = (struct sockaddr_in *)address;

        /*  Do stuff with IPv4 socket  */

    }
    else if ( address->sa_family == AF_INET6 ) {
        struct sockaddr_in6 * real_struct = (struct sockaddr_in6 *)address;

        /*  Do stuff with IPv6 socket  */

    }

    /*  etc  */
}
Run Code Online (Sandbox Code Playgroud)

(迂腐说明:从技术上讲,根据C标准[C11第6.5.2.3.6节],struct如果你将它们嵌入到一个中union,你应该只检查这样的常见初始部分,但实际上它几乎是总是在没有一个的情况下工作,为简单起见,我没有在上面的代码中使用过一个.

当你实际上没有真正的OOP结构时,它基本上是一种获取多态的方法.换句话说,这意味着你不必有一堆类似的功能bind_in(),bind_in6()以及它所有的休息,一个单一的bind()功能可以处理所有这些,因为它可以找出是什么类型的struct反倒(前提是你是你设定sa_family当然是正确的成员).

你需要实际演员的原因是因为C的类型系统需要它.你有一个通用指针void *,但除此之外一切都必须匹配,所以如果一个函数接受struct sockaddr *它只是不会让你传递任何其他东西,包括一个struct sockaddr_in *.演员基本上告诉编译器"我知道我在做什么,在这里,相信我",它会为你放松规则.bind() 本来可以写成接受一个void *而不是一个struct sockaddr *和一个演员阵容是没有必要的,但它不是那样写的,因为:

  1. 它在语义上更有意义 - bind()不写入接受任何指针,只是一个struct从"派生"的struct sockaddr; 和

  2. 最初的套接字API是在1983年发布的,它早在1989年的ANSI C标准之前,它的前身--K&R C - 就没有了void *,所以你不得不在任何情况下将其转换为某种东西.