套接字地址结构:
IPv4套接字地址结构
IPv4套接字地址结构也被成为网际套接字地址结构,被命名为sockaddr_in
,IPv6命名为sockaddr_in6
,他们被定义在<netinet/in.h
中,posix标准中的IPv4套接字地址结构定义如下:
/* ipv4套接字地址结构 */
struct sockaddr_in
{
sa_family_t sin_family /*AF_INET*/
in_port_t sin_port; /* 端口号 16位网络序二进制 */
struct in_addr sin_addr; /* ip地址 32位网络序二进制 */
char sin_zero[8];
};
通用套接字地址结构
每个协议簇都定义了自己套接字地址结构,他们都已sockaddr_
开头,比如上面的IPv4套接字地址结构。
一种通用的套接字地址结构被命名为sockaddr
,定义如下
struct sockaddr
{
sa_family_t sin_family
char sa_data[14];
};
bind函数是一个使用到通用套接字地址结构的函数:
typedef struct sockaddr SA;
bind(int,SA *,socklen_t);//其中socklen_t是套接字地址结构的大小,用sizeof获得
值-结果参数
当王一个套接字函数传递一个套接字地址结构时,该结构总是以引用的方式传递,也就是说传递的是指向结构的参数。该结构的大小也座位一个参数来传递,不过传递方式分直接传递和指针。
- 从进程到内核(connect、bind、sento)——值传递:
connect(sockfd,(SA *) &serv,sizeof(serv);
- 从内核到进程(accept、recvfrom、getsockname、getpeername)——引用传递:
getpeername(unixfd,(SA *)&cli,&len;
为何将结构大小由整数改为指向整数的指针呢?
这是因为:当函数被调用时,结构大小是一个值(value, 此值告诉内核该结构的大小,使内核在写此结构时不至于越界),当函数返回时,结构大小又是一个结果(result,它告诉进程内核在此结构中确切存储了多少信息),这种参数叫做值-结果参数(value-result)。
通俗一点讲就是,值传递不可以修改内核中数据,引用传递可以通过指针修改内核中的数据。
字节排序函数
考虑一个16位整数,由2个字节组成,内存存储这两个字节有两种方式:
- 将低序字节存储在起始地址(小端字节序)
- 将高序字节存储在起始地址(大端字节序)
- 小端和大端指多字节的哪一端存在起始位置。
对于网络,发送协议栈和接受协议栈必须就多字节字段多字节的传输顺序达成一致,因此存在网络字节序。
因为主机字节序和网络字节序会存在不同,所以出现了字节排序函数:
#include <netinet/in.h>
uint32_t ntohl (uint32_t __netlong);
uint16_t ntohs (uint16_t __netshort);
uint32_t htonl (uint32_t __hostlong);
uint16_t htons (uint16_t __hostshort);
字节操纵函数
字节操纵函数有两组:一组以b开头,代表字节;一组以mem开头,代表内存。他们都进行内存的初始化、复制和比较。
下面给出函数声明:
b组:
#include <string.h>
extern int bcmp (const void *__s1, const void *__s2, size_t __n);
/* Copy N bytes of SRC to DEST (like memmove, but args reversed). */
extern void bcopy (const void *__src, void *__dest, size_t __n);
/* Set N bytes of S to 0. */
extern void bzero (void *__s, size_t __n);
mem组
#include <string.h>
extern void *memccpy (void *__restrict __dest, const void *__restrict __src,
int __c, size_t __n);
/* Set N bytes of S to C. */
extern void *memset (void *__s, int __c, size_t __n);
/* Compare N bytes of S1 and S2. */
extern int memcmp (const void *__s1, const void *__s2, size_t __n)
地址转换函数
地址转换函数用于在ASCII字符串和网络字节序的二进制值之间转换网络地址。
- inet_aton、inet_addr、inet_ntoa在点分十进制(127.0.0.1)与长度为32位的网络字节序的二进制之间转换IPv4地址。
- 两个较新的函数
inet_pton
和inet_ntop
对IPv4和IPv6地址都适用。
后面主要使用较新的两个函数,下面给出他们的函数声明。
- 注意:有些系统没有实现这两个函数,需要自己声明和定义,参见下面。
//用于转换ip地址(类似127.0.0.1的“表述”转换为二进制的“数”);
int
inet_pton(int af,const char *src,void *dst);
//将网络序中的二进制数转化为点分十进制数(p——表达式)
const char *
inet_ntop(int af, const void *src, char *dst, socklen_t size);
/**
* 定义size是为了防止超出缓冲区
* size 使用 <netinet/in.h>中定义的
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
*/
inet_pton
和inet_ntop
的函数原型见我的github。