网络编程相关结构体整理
2016-04-13 17:49
627 查看
常用的套接字类型有3种:
(1)流套接字(SOCK——STREAM):使用了面向连接的可靠的数据通信方式,即TCP协议;
(2)数据报套接字(Raw Sockets):使用了不面向连接的数据传输方式,即UDP;
(3)原始套接字(SOCK——RAW):没有经过处理的IP数据包,可以根据自己程序的要求进行封装。
1、套接字相关的数据类型:sockaddr和sockaddr_in;sockaddr用来保存一个套接字,定义如下:
struct sockaddr
{
unsigned short int sa_family; //指定通信地址类型,如果是TCP/IP通信,则值为AF_inet
char sa_data[14]; //最多用14个字符长度,用来保存IP地址和端口信息
};
struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。
struct sockaddr_in
{
unsigned short int sin_family; //指定通信地址类型
uint16_t sin_port; //套接字使用的端口号
struct in_addr sin_addr; //需要访问的IP地址
unsigned char sin_zero[8]; //未使用的字段,填充为0
};
在这一结构中,in_addr也是一个结构体,定义如下,用于保存一个IP地址:
/* IPv4 address */
struct in_addr
{
uint32_t s_addt;
};
/* IPv6 address */
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
if defined __USE_MISC || defined __USE_GNU
endif
}
2、内核中用于接收/发送数据的结构体
struct msghdr {
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
size_t msg_iovlen;
void *msg_control;
size_t msg_controllen;
int msg_flags;
};
struct iovec {
ptr_t iov_base; /* Starting address */
size_t iov_len; /* Length in bytes */
};
成员msg_name与msg_namelen
这些成员只有当我们的套接口是一个数据报套接口时才需要。msg_name成员指向我们要发送或是接收信息的套接口地址。成员msg_namelen指明了这个套接口地址的长度。
当调用recvmsg时,msg_name会指向一个将要接收的地址的接收区域。当调用sendmsg时,这会指向一个数据报将要发送到的目的地址。
注意,msg_name定义为一个(void )数据类型。我们并不需要将我们的套接口地址转换为(struct sockaddr )。
成员msg_iov与msg_iovlen
Msg_iov中,iov_base字段指向一个缓冲区,缓冲区是存放的是所接收的数据或是将要发送的数据。iov_len字段在各种情况下分别确定了接收的最大长度以及实际写入的长度。
这些成员指定了我们的I/O向量数组的位置以及他包含多少项。msg_iov成员指向一个struct iovec数组。我们将会回忆起I/O向量指向我们的缓冲区。成员msg_iovlen指明了在我们的I/O向量数组中有多少元素。
成员msg_control与msg_controllen
这些成员指向了我们附属数据缓冲区并且表明了缓冲区大小。msg_control指向附属数据缓冲区,而msg_controllen指明了缓冲区大小。
成员msg_flags
当使用recvmsg时,这个成员用于接收特定的标记位(他并不用于sendmsg)。
3、向内核发送消息时使用msghdr,netlink socket使用自己的消息头nlmsghdr和自己的消息地址sockaddr_nl
struct nlmsghdr {
__u32 nlmsg_len;
__u16 nlmsg_type;
__u16 nlmsg_flags;
__u32 nlmsg_seq;
__u32 nlmsg_pid;
};
nlmsg_len 指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小,一般地,我们使用netlink提供的宏NLMSG_LENGTH来计算这个长度,仅需向NLMSG_LENGTH宏提供要发送的数据的长度,它会自动计算对齐后的总长度;
nlmsg_type 用于应用内部定义消息的类型,它对 netlink 内核实现是透明的,因此大部分情况下设置为 0;
nlmsg_flags 用于设置消息标志,对于一般的使用,用户把它设置为 0 就可以,只是一些高级应用(如 netfilter 和路由 daemon 需要它进行一些复杂的操作);
字段 nlmsg_seq 和 nlmsg_pid 用于应用追踪消息,前者表示顺序号,后者为消息来源进程 ID。
struct sockaddr_nl {
sa_family_t nl_family;
unsigned short nl_pad;
__u32 nl_pid;
__u32 nl_groups;
};
成员 nl_family为协议簇 AF_NETLINK;
成员 nl_pad 当前没有使用,因此要总是设置为 0;
成员 nl_pid 为接收或发送消息的进程的 ID,如果希望内核处理消息或多播消息,就把该字段设置为 0,否则设置为处理消息的进程 ID;
成员 nl_groups 用于指定多播组,bind 函数用于把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入任何多播组。
4、控制信息头部本身由下面的C结构定义
struct cmsghdr {
socklen_t cmsg_len;
int cmsg_level;
int cmsg_type;
/* u_char cmsg_data[]; */
};
其成员描述如下:
成员 描述
cmsg_len 附属数据的字节计数,这包含结构头的尺寸。这个值是由CMSG_LEN()宏计算的。
cmsg_level 这个值表明了原始的协议级别(例如,SOL_SOCKET)。
cmsg_type 这个值表明了控制信息类型(例如,SCM_RIGHTS)。
cmsg_data 这个成员并不实际存在。他用来指明实际的额外附属数据所在的位置。
/* Protocol families. */
nlmsg_type 决定这次要执行的操作,如查询当前路由表信息,所使用的就是RTM_GETROUTE。标准nlmsg_type包括:NLMSG_NOOP, NLMSG_DONE, NLMSG_ERROR等。根据采用的nlmsg_type不同,还要选取不同的数据结构来填充到nlmsghdr后面:
操作 数据结构
RTM_NEWLINK ifinfomsg
RTM_DELLINK
RTM_GETLINK
RTM_NEWADDR ifaddrmsg
RTM_DELADDR
RTM_GETADDR
RTM_NEWROUTE rtmsg
RTM_DELROUTE
RTM_GETROUTE
RTM_NEWNEIGH ndmsg/nda_chcheinfo
RTM_DELNEIGH
RTM_GETNEIGH
RTM_NEWRULE rtmsg
RTM_DELRULE
RTM_GETRULE
RTM_NEWQDISC tcmsg
RTM_DELQDISC
RTM_GETQDISC
RTM_NEWTCLASS tcmsg
RTM_DELTCLASS
RTM_GETTCLASS
RTM_NEWTFILTER tcmsg
RTM_DELTFILTER
RTM_GETTFILTER
(1)流套接字(SOCK——STREAM):使用了面向连接的可靠的数据通信方式,即TCP协议;
(2)数据报套接字(Raw Sockets):使用了不面向连接的数据传输方式,即UDP;
(3)原始套接字(SOCK——RAW):没有经过处理的IP数据包,可以根据自己程序的要求进行封装。
1、套接字相关的数据类型:sockaddr和sockaddr_in;sockaddr用来保存一个套接字,定义如下:
struct sockaddr
{
unsigned short int sa_family; //指定通信地址类型,如果是TCP/IP通信,则值为AF_inet
char sa_data[14]; //最多用14个字符长度,用来保存IP地址和端口信息
};
struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。
struct sockaddr_in
{
unsigned short int sin_family; //指定通信地址类型
uint16_t sin_port; //套接字使用的端口号
struct in_addr sin_addr; //需要访问的IP地址
unsigned char sin_zero[8]; //未使用的字段,填充为0
};
在这一结构中,in_addr也是一个结构体,定义如下,用于保存一个IP地址:
/* IPv4 address */
struct in_addr
{
uint32_t s_addt;
};
/* IPv6 address */
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
if defined __USE_MISC || defined __USE_GNU
uint16_t __u6_addr16[8]; uint32_t __u6_addr32[4];
endif
} __in6_u;
}
2、内核中用于接收/发送数据的结构体
struct msghdr {
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
size_t msg_iovlen;
void *msg_control;
size_t msg_controllen;
int msg_flags;
};
struct iovec {
ptr_t iov_base; /* Starting address */
size_t iov_len; /* Length in bytes */
};
成员msg_name与msg_namelen
这些成员只有当我们的套接口是一个数据报套接口时才需要。msg_name成员指向我们要发送或是接收信息的套接口地址。成员msg_namelen指明了这个套接口地址的长度。
当调用recvmsg时,msg_name会指向一个将要接收的地址的接收区域。当调用sendmsg时,这会指向一个数据报将要发送到的目的地址。
注意,msg_name定义为一个(void )数据类型。我们并不需要将我们的套接口地址转换为(struct sockaddr )。
成员msg_iov与msg_iovlen
Msg_iov中,iov_base字段指向一个缓冲区,缓冲区是存放的是所接收的数据或是将要发送的数据。iov_len字段在各种情况下分别确定了接收的最大长度以及实际写入的长度。
这些成员指定了我们的I/O向量数组的位置以及他包含多少项。msg_iov成员指向一个struct iovec数组。我们将会回忆起I/O向量指向我们的缓冲区。成员msg_iovlen指明了在我们的I/O向量数组中有多少元素。
成员msg_control与msg_controllen
这些成员指向了我们附属数据缓冲区并且表明了缓冲区大小。msg_control指向附属数据缓冲区,而msg_controllen指明了缓冲区大小。
成员msg_flags
当使用recvmsg时,这个成员用于接收特定的标记位(他并不用于sendmsg)。
3、向内核发送消息时使用msghdr,netlink socket使用自己的消息头nlmsghdr和自己的消息地址sockaddr_nl
struct nlmsghdr {
__u32 nlmsg_len;
__u16 nlmsg_type;
__u16 nlmsg_flags;
__u32 nlmsg_seq;
__u32 nlmsg_pid;
};
nlmsg_len 指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小,一般地,我们使用netlink提供的宏NLMSG_LENGTH来计算这个长度,仅需向NLMSG_LENGTH宏提供要发送的数据的长度,它会自动计算对齐后的总长度;
nlmsg_type 用于应用内部定义消息的类型,它对 netlink 内核实现是透明的,因此大部分情况下设置为 0;
nlmsg_flags 用于设置消息标志,对于一般的使用,用户把它设置为 0 就可以,只是一些高级应用(如 netfilter 和路由 daemon 需要它进行一些复杂的操作);
字段 nlmsg_seq 和 nlmsg_pid 用于应用追踪消息,前者表示顺序号,后者为消息来源进程 ID。
struct sockaddr_nl {
sa_family_t nl_family;
unsigned short nl_pad;
__u32 nl_pid;
__u32 nl_groups;
};
成员 nl_family为协议簇 AF_NETLINK;
成员 nl_pad 当前没有使用,因此要总是设置为 0;
成员 nl_pid 为接收或发送消息的进程的 ID,如果希望内核处理消息或多播消息,就把该字段设置为 0,否则设置为处理消息的进程 ID;
成员 nl_groups 用于指定多播组,bind 函数用于把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入任何多播组。
4、控制信息头部本身由下面的C结构定义
struct cmsghdr {
socklen_t cmsg_len;
int cmsg_level;
int cmsg_type;
/* u_char cmsg_data[]; */
};
其成员描述如下:
成员 描述
cmsg_len 附属数据的字节计数,这包含结构头的尺寸。这个值是由CMSG_LEN()宏计算的。
cmsg_level 这个值表明了原始的协议级别(例如,SOL_SOCKET)。
cmsg_type 这个值表明了控制信息类型(例如,SCM_RIGHTS)。
cmsg_data 这个成员并不实际存在。他用来指明实际的额外附属数据所在的位置。
/* Protocol families. */
nlmsg_type 决定这次要执行的操作,如查询当前路由表信息,所使用的就是RTM_GETROUTE。标准nlmsg_type包括:NLMSG_NOOP, NLMSG_DONE, NLMSG_ERROR等。根据采用的nlmsg_type不同,还要选取不同的数据结构来填充到nlmsghdr后面:
操作 数据结构
RTM_NEWLINK ifinfomsg
RTM_DELLINK
RTM_GETLINK
RTM_NEWADDR ifaddrmsg
RTM_DELADDR
RTM_GETADDR
RTM_NEWROUTE rtmsg
RTM_DELROUTE
RTM_GETROUTE
RTM_NEWNEIGH ndmsg/nda_chcheinfo
RTM_DELNEIGH
RTM_GETNEIGH
RTM_NEWRULE rtmsg
RTM_DELRULE
RTM_GETRULE
RTM_NEWQDISC tcmsg
RTM_DELQDISC
RTM_GETQDISC
RTM_NEWTCLASS tcmsg
RTM_DELTCLASS
RTM_GETTCLASS
RTM_NEWTFILTER tcmsg
RTM_DELTFILTER
RTM_GETTFILTER
相关文章推荐
- Linux socket 初步
- Linux Kernel 4.0 RC5 发布!
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- Linux 下无损图片压缩小工具介绍