IPv4套接字地址结构
IPv4套接字地址结构以“sockaddr_in”命名,定义在头文件
struct in_addr {
in_addr_t s_addr; /* 32-bit IPv4 address */
/* network byte ordered */
};
struct sockaddr_in {
uint8_t sin_len; /* length of structure (16) */
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* 16-bit TCP or UDP port number */
/* network byte ordered */
struct in_addr sin_addr; /* 32-bit IPv4 address */
/* network byte ordered */
char sin_zero[8]; /* unused */
};
sin_len成员表示地址结构的长度,它是一个无符号的八位整数。需要强调的是,这个成员并不是地址结构必须有的。假如没有这个成员,其所占的一个字节被并入到sin_family成员中;同时,在传递地址结构的指针时,结构长度需要通过另外的参数来传递。
sin_family成员指代的是所用的协议族,在有sin_len成员的情况下,它是一个8位的无符号整数;在没有sin_len成员的情况下,它是一个16位的无符号整数。由于IP协议属于TCP/IP协议族,所以在这里该成员应该赋值为“AF_INET”。
sin_port成员表示TCP或UDP协议的端口号,它是一个16位的无符号整数。它是以网络字节顺序(大端字节序)来存储的。
in_addr成员用来保存32位的IPv4地址,它同样是以网络字节顺序来存储的。
sin_zero成员是不使用的,通常会将它置为0,它的存在只是为了与通用套接字地址结构struct sockaddr在内存中对齐。
通用套接字
struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family; /* address family: AF_xxx value */
char sa_data[14]; /* protocol-specific address */
};
产生由于历史原因,使用方式为强制转换 ,如
bind函数原型与使用:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
//usage
struct sockaddr_in serv; /* IPv4 socket address structure */
/* fill in serv{} */
bind(sockfd, (struct sockaddr *) &serv, sizeof(serv));
//将serv地址 强制转换为struct sockaddr *类型作参数传入
IPv6套接子地址结构
The IPv6 socket address is defined by including the
struct in6_addr {
uint8_t s6_addr[16]; /* 128-bit IPv6 address */
/* network byte ordered */
};
#define SIN6_LEN /* required for compile-time tests */
struct sockaddr_in6 {
uint8_t sin6_len; /* length of this struct (28) */
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* transport layer port# */
/* network byte ordered */
uint32_t sin6_flowinfo; /* flow information, undefined */
struct in6_addr sin6_addr; /* IPv6 address */
/* network byte ordered */
uint32_t sin6_scope_id; /* set of interfaces for a scope */
};
在sin6_len中,存储有sockaddr_in6结构体的长度。
在 sin6_family中,存储有表示IPv6地址系列的AF_INET6.
在sin6_port中,存储有一个传输层所使用的端口号。
在sin6_flowinfo中,存储有一个在QoS中所使用的流标识符。
在sin6_addr域中,存储有IPv6协议的地址 。
在sin6_scope_id中,存储有表示范围的ID.
值-结果参数
- 进程到内核传递套接字地址结构的函数有3个: bind, connect,sendto 这些函数的一个参数是指向某套接字结构的指针,另一个参数为该结构的整数大小 ,如
struct sockaddr_in serv;
/* fill in serv{} */
connect (sockfd, (SA *) &serv, sizeof(serv));
- 从内核到进程传递套接字地址结构函数有4个: accept, recvfrom, getsockname, getpeername 参数为指向某个套接字结构的指针和指向表示该结构大小的整形变量的指针,如
注意 套接字大小的类型为socklen_t 而不是 int 建议将socklen_t定义为 uint32_t
struct sockaddr_un cli; /* Unix domain */
socklen_t len;
len = sizeof(cli); /* len is a value */
getpeername(unixfd, (SA *) &cli, &len);
/* len may have changed */
字节排序(大小端)
判断大小端函数
typedef union {
int i;
char c;
}my_union;
int checkSystem1(void)
{
my_union u;
u.i = 1;
return (u.i == u.c);
}
int checkSystem2(void)
{
int i = 0x12345678;
char *c = &i;
return ((c[0] == 0x78) && (c[1] == 0x56) && (c[2] == 0x34) && (c[3] == 0x12));
}
int main(void)
{
if(checkSystem1())
printf("little endian\n");
else
printf("big endian\n");
if(checkSystem2())
printf("little endian\n");
else
printf("big endian\n");
return 0;
}
主机字节序与网络字节序转换函数
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue) ;
uint32_t htonl(uint32_t host32bitvalue) ;
// Both return: value in network byte order
uint16_t ntohs(uint16_t net16bitvalue) ;
uint32_t ntohl(uint32_t net32bitvalue) ;
// Both return: value in host byte order
h : host 主机
n : network 网络
s : short
l : long
字节操纵函数
名字以b (byte)开头的第一组函数,现今系统依然支持
#include <strings.h>
void bzero(void *dest, size_t nbytes);
void bcopy(const void *src, void *dest, size_t nbytes);
int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);
// Returns: 0 if equal, nonzero if unequal
常用bzero把目标字节串指定数目字节置0
ANSI C中的函数
#include <string.h>
void *memset(void *dest, int c, size_t len);
void *memcpy(void *dest, const void *src, size_t nbytes);
int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);
// Returns: 0 if equal, <0 or >0 if unequal (see text)
memcpy两个指针的顺序是按照c中赋值语句相同的顺序写的
dest = src
地址转换函数
‘inet_aton’, ‘inet_addr’, and ‘inet_ntoa’
inet_aton, inet_ntoa, and inet_addr convert an IPv4 address from a
dotted-decimal string (e.g., “206.168.112.96”) to its 32-bit network byte
ordered binary value. You will probably encounter these functions in lots of
existing code.
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
//Returns: 1 if string was valid, 0 on error
in_addr_t inet_addr(const char *strptr);
//Returns: 32-bit binary network byte ordered IPv4 address;
//INADDR_NONE if error
char *inet_ntoa(struct in_addr inaddr);
//Returns: pointer to dotted-decimal string
inet_pton’ and ‘inet_ntop’ Functions
适用于IPv4和iPv6,函数名字中的p代表表达(presentation),n代表数值(numeric)
地址表达式通常是ASCII字符串,数值格式则是存放到套接字地址的二进制值
#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);
//Returns: 1 if OK, 0 if input not a valid presentation format, -1 on error
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
//Returns: pointer to result if OK, NULL on error
family参数可以是 AF_INET 或者 AF_INET6
第一个函数尝试转换由strptr指针所指字符串,并通过addrptr指针存放二进制结果
inet_ntop进行相反的转换,从数值格式(addrptr)转换为表达格式(strptr),len为目标存储单元大小,避免函数溢出其调用者的缓冲区,为有助于指定这个大小,
#define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */
#define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */
Example
Even if your system does not yet include support for IPv6, you can start using these
newer functions by replacing calls of the form
foo.sin_addr.s_addr = inet_addr(cp);
with
inet_pton(AF_INET, cp, &foo.sin_addr);
and replacing calls of the form
ptr = inet_ntoa(foo.sin_addr);
with
char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
‘sock_ntop’
#include "unp.h"
char *sock_ntop(const struct sockaddr *sockaddr, socklen_t addrlen);
//Returns: non-null pointer if OK, NULL on error
//函数用他自己的静态缓冲区来保存结果,返回的指针就是指向它
‘readn’, ‘writen’, and ‘readline’ Functions
字节流上调用read或者write输入或者输出的字节数可能比请求的数量小,然而这并不是出错的状态。现象出现的原因是内核中用于套接字的缓冲区已经达到了极限。此时所需的是调用者再次调用read或者write函数
针对上述情况,我们总是改为调用writen函数来取代write函数
#include "unp.h"
ssize_t readn(int filedes, void *buff, size_t nbytes);
ssize_t writen(int filedes, const void *buff, size_t nbytes);
ssize_t readline(int filedes, void *buff, size_t maxlen);
//All return: number of bytes read or written, –1 on error