大多数套接字函数需要一个指向套接字地址结构的指针作为参数。每个协议族都定义自己的套接字地址结构,这些结构均以sockaddr_开头,并以对应每个协议族的唯一后缀结尾
本文地址:http://wuyudong.com/2016/09/21/2487.html,转载请注明源地址。
IPv4套接字地址结构
IPv4套接字地址结构通常也被称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中,下面给出它的定义:
struct in_addr { in_addr_t s_addr; //32位的IPv4地址 }; struct sockaddr_in { uint8_t sin_len; //结构体的长度(16) sa_family_t sin_family; //AF_INET in_port_t sin_port; //16位的TCP或UDP的端口号 struct in_addr sin_addr; //32位的IPv4地址 char sin_zero[8]; //未曾使用 };
in_addr_t为uint32_t
sin_len不是必须的,是为了增加对OSI协议的支持才加入的,其作用可以简化长度可变套接字地址结构的处理,如果有了sin_len字段,也无需设置和检查它,除非涉及路由套接字。
POSIX规范之需要这个结构的三个字段:sa_family_t 、 in_port_t sin_port、 struct in_addr
通用的套接字地址结构
作为一个参数传递进任何的套接字函数时,套接字地址结构总是以引用形式(也即是指向该结构的指针)来传递。于是以这样的指针作为参数之一的任何套接字函数必须处理来自所支持的任何协议族的套接字地址结构
在ANSI C之后可以使用void*作为通用的指针类型,但是套接字函数出来的时候还没有ANSI C,于是可以在<sys/socket.h>中定义一个通用的套接字地址结构:
struct sockaddr{ uint8_t sa_len; sa_family_t sa_family; char sa_data[14]; };
bind函数的原型是:
int bind(int, struct sockaddr *, socklen_t);
于是可以这样调用bind函数
struct sockaddr)_in serv; /* IPV4 套接字地址结构 */
bind(sockfd, (struct sockaddr *)&serv, sizeof(serv));
IPv6套接字地址结构
struct in6_addr { unit8_t s6_addr[16]; //128位的IPv6地址 }; #define SIN6_LEN struct sockaddr_in6 { uint8_t sin6_len; //结构体的长度(28) sa_family_t sin6_family; //AF_INET6 in_port_t sin6_port; //TCP或UDP的端口号 uint32_t sin6_flowinfo; struct in6_addr sin6_addr; //32位的IPv4地址 uint32_t sin6_scope_id; };
值-结果参数
以指针形式传递套接字地址参数的时候,同时还传递地址结构的大小,但是这个参数取决于该结构的传递方向:从进程到内核,从内核到进程
(1)从进程到内核传递套接字地址结构的函数有3个:bind、connect、sendto
这些函数的一个参数是指向套接字地址结构的指针,另一个参数是该结构的整数大小,例如:
struct sockaddr_in serv; connect(sockfd, (struct sockaddr *)&serv, sizeof(serv));
(2)从内核到进程传递套接字地址结构的函数有4个:accept、recvfrom、getsockname、getpeername
struct sockaddr_un cli; socklen_t len; len = sizeof(cli); getpeername(unixfd, (struct sockaddr *)&cli, &len);
可以看到该函数第三个参数是传递指向len的指针类型,这种形式的参数称为“值-结果参数”
为什么这样设计呢?原因是内核传递套接字地址结构给进程的时候,需要告诉进程内核在该结构中存储了多少信息。
Comments