本章主要介绍socket相关基础知识, 包括套接字地址结构, 字节序, ip地址转换.
1. 套接字地址结构
IPv4套接字地址结构, 主要使用sin_family, sin_port, sin_addr这3个字段
#include <netinet/in.h>
struct in_addr{
in_addr_t s_addr;
};
struct sockaddr_in{
uint8_t sin_len;
sa_family_t sin_family; //AF_INET
in_port_t sin_port; //端口号(网络字节序)
struct in_addr sin_addr; //32位ip地址(网络字节序)
char sin_zero[8];
};
通用套接字地址结构sockaddr, 主要用于支持任何协议族的套接字地址结构
#include <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), 当直接传sockaddr_in地址会出现警告, 指针类型不匹配
IPv6套接字地址结构
struct in6_addr{
uint8_t s6_addr[16];
};
struct sockaddr_in6{
uint8_t sin6_len;
sa_family_t sin6_family; //AF_INET6
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
新的通用套接字地址结构, 新的足够大,能满足系统支持的任何套接字地址结构
struct sockaddr_storage{
uint8_t ss_len;
sa_family_t ss_family;
....
};
2. 字节序问题
机器通常有两种字节序, 大端和小端. 大端: 高字节数据存储在起始地址; 小端: 低字节数据存储在起始地址
而网络协议必须指定一个网络字节序, 大端字节序
如何在主机字节序和网络字节序进行转换,有以下四种函数
#include<netinet/in.h>
uint16_t htons(uint16_t); //主机字节序到网络字节序, 16位整型
uint32_t htonl(uint32_t); //主机字节序到网络字节序, 32位整型
uint16_t ntohs(uint16_t); //网络字节序到主机字节序, 16位整型
uint32_t ntohl(uint32_t); //网络字节序到主机字节序, 32位整型
3. ip地址转换, 点分十进制到网络字节序的二进制值
#include<arpa/inet.h>
int inet_aton(const char *, struct in_addr *); //将c字符串转换成32位网络字节序二进制
in_addr_t inet_addr(const char *); //返回32位二进制数据
char * inet_ntoa(struct in_addr); //返回一个点分十进制数据的指针 (注: 该函数不可重入, 它返回一个静态内存的指针)
通用的转换函数inet_pton和inet_ntop
#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);//将c字符串转换成32位网络字节序二进制, 成功返回0, 出错为-1
char * inet_ntop(int family,const void *addrptr, char *strptr, size_t len); //出错返回NULL
1. 套接字地址结构
IPv4套接字地址结构, 主要使用sin_family, sin_port, sin_addr这3个字段
#include <netinet/in.h>
struct in_addr{
in_addr_t s_addr;
};
struct sockaddr_in{
uint8_t sin_len;
sa_family_t sin_family; //AF_INET
in_port_t sin_port; //端口号(网络字节序)
struct in_addr sin_addr; //32位ip地址(网络字节序)
char sin_zero[8];
};
通用套接字地址结构sockaddr, 主要用于支持任何协议族的套接字地址结构
#include <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), 当直接传sockaddr_in地址会出现警告, 指针类型不匹配
IPv6套接字地址结构
struct in6_addr{
uint8_t s6_addr[16];
};
struct sockaddr_in6{
uint8_t sin6_len;
sa_family_t sin6_family; //AF_INET6
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
新的通用套接字地址结构, 新的足够大,能满足系统支持的任何套接字地址结构
struct sockaddr_storage{
uint8_t ss_len;
sa_family_t ss_family;
....
};
2. 字节序问题
机器通常有两种字节序, 大端和小端. 大端: 高字节数据存储在起始地址; 小端: 低字节数据存储在起始地址
而网络协议必须指定一个网络字节序, 大端字节序
如何在主机字节序和网络字节序进行转换,有以下四种函数
#include<netinet/in.h>
uint16_t htons(uint16_t); //主机字节序到网络字节序, 16位整型
uint32_t htonl(uint32_t); //主机字节序到网络字节序, 32位整型
uint16_t ntohs(uint16_t); //网络字节序到主机字节序, 16位整型
uint32_t ntohl(uint32_t); //网络字节序到主机字节序, 32位整型
3. ip地址转换, 点分十进制到网络字节序的二进制值
#include<arpa/inet.h>
int inet_aton(const char *, struct in_addr *); //将c字符串转换成32位网络字节序二进制
in_addr_t inet_addr(const char *); //返回32位二进制数据
char * inet_ntoa(struct in_addr); //返回一个点分十进制数据的指针 (注: 该函数不可重入, 它返回一个静态内存的指针)
通用的转换函数inet_pton和inet_ntop
#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);//将c字符串转换成32位网络字节序二进制, 成功返回0, 出错为-1
char * inet_ntop(int family,const void *addrptr, char *strptr, size_t len); //出错返回NULL