套接字描述符
套接字是通信端点的抽象,和用文件描述符来访问文件同理,应用程序用套接字描述符来访问套接字,套接字在unix系统中被当做是一种文件描述符。虽然套接字本质上是一种文件描述符,但并不是完全可以当做文件描述符来操作,例如,有些以文件描述符作为参数的函数就不能接受套接字为参数。例如lseek函数就不能以套接字作为参数,因为套接字不支持文件偏移量的概念。我们可以理解套接字是一种添加了某些限制的文件描述符。
套接字通信的本质是进程将要发送的字符写入套接字描述符所对应的文件中,然后操作系统负责传输数据,接收方从接收方的套接字描述符所对应的文件中读取传送的数据。
创建套接字<span style="font-size:10px;font-weight: normal;">#include <sys/socket.h>
int socket(int domain,int type,int protocol);</span>
第一个参数指定套接字的通信特性,包括地址结构等,POSIX.1标准定义的域:
<span style="font-size:10px;font-weight: normal;">#include <sys/socket.h>
int socket(int domain,int type,int protocol);</span>
AF_INET //Ipv4英特网域
AF_INET6 //Ipv6英特网域
AF_UNIX //unix域
AF_UPSPEC //未指定
//在有些系统中AF_UNIX用AF_LOCAL代替,它用来标示本地进程间通信
//前缀为AF表示的是地址族,老一点的系统中PF标示协议族
第二个参数指定套接字的类型常用的有TCP协议对应的有序,可靠,双向,面向连接的字节流和UDP协议对应的固定长度的,无连接的,不可靠的报文传递。
SOCK_STREAM //TCP字节流
SOCK_DGRAM //UDP报文传递
SOCK_RAW //IP协议的数据报接口
SOCK_SEQPACKET //固定长度的,可靠地有序的数据报传输
第三个参数通常为0,表示给定的域和套接字选择默认的协议,例如SOCK_STREAM对应的是TCP协议,如果选定的协议族和套接字类型有多个协议可选,可以通过该参数来指定,因特网套接字定义的协议:
<span style="font-size:10px;font-weight: normal;">IPPROTO_IP //IPv4网际协议
IPPROTO_IPV6 //IPv6网际协议
IPPROTO_ICMP //因特网控制报文协议
IPPROTO_RAW //原始IP数据包协议
IPPROTO_TCP //传输控制协议
IPPROTO_UDP //用户数据报协议</span>
<span style="font-size:10px;font-weight: normal;">IPPROTO_IP //IPv4网际协议
IPPROTO_IPV6 //IPv6网际协议
IPPROTO_ICMP //因特网控制报文协议
IPPROTO_RAW //原始IP数据包协议
IPPROTO_TCP //传输控制协议
IPPROTO_UDP //用户数据报协议</span>
字节序及转换
不同的处理器架构可能对应不同的字节序。
大端字节序:
最大字节地址出现在最低有效字节上。
小端字节序:
最大字接地址出现在最高有效位上。
常用的四个处理器字节序和网络字节序间的转换函数
<span style="font-size:12px;font-weight: normal;">#include <arpa/inet.h>
unit32_t htonl(unit32_t hostint32) //32为整数处理器序转换为网络序
unit32_t ntohl(unit32_t nettint32) //32为整数网络序转换为处理器序
unit16_t htons(unit16_t hostint16) //16为整数处理器序转换为网络序
unit16_t ntohs(unit16_t nettint16) //16为整数网络序转换为处理器序</span>
<span style="font-size:12px;font-weight: normal;">#include <arpa/inet.h>
unit32_t htonl(unit32_t hostint32) //32为整数处理器序转换为网络序
unit32_t ntohl(unit32_t nettint32) //32为整数网络序转换为处理器序
unit16_t htons(unit16_t hostint16) //16为整数处理器序转换为网络序
unit16_t ntohs(unit16_t nettint16) //16为整数网络序转换为处理器序</span>
套接字地址结构
通用套接字(为了实现协议的无关性,在bind和connect等系统调用时,特定于某种协议的套接字结构指针都要强制转换为通用套接字指针)
struct sockaddr{
sa_family_t sa_family; //通信地址类型,实际上被定义为unsigned short
char sa_data[14]; //14字节的IP地址和端口信息
}
IPv4套接字
struct sockaddr_in{
unsigned short sin_len; //地址长度
sa_family_t sin_family; //通信地址类型
unsigned short int sin_port; //16位端口号,以网络字节序存储
struct in_addr sin_addr; //地址结构 in_addr结构只有一个成员 unit32_t s_addr:32位IPv4地址,网络字节序存储
unsigned char sin_zero[8]; //未使用的字段,填充为0
}
IPv6套接字
struct sockaddr_in6{
unsigned short int sin6_len; //地址长度
short int sin6_family; //通信地址类型
in_port_t sin6_port; //16位端口号,以网络字节序存储
uint32_t sin6_flowinfo; //低24位是流量标号,中间4字节的优先级标识,其他4位保留
struct in6_addr sin6_addr; //结构中有一个成员unsigned long s6_addr; 128位的ipv6地址
}
常用的字节操作函数
#include <string.h>
void bzero(void *dest, size_t nbytes); //内存置0
void bcopy(const void *ptrl,void*dest,size_t nbytes) //内存数据的copy
int bcmp(const void *ptrl1,const void *ptrl2,size_t nbytes); //内存数据比较,如果第一个不同的位置ptrl1大,返回大于0的数,相等返回0,小于返回小于0的值
void memset(void *dest int c,size_t len); //给指定位数的字节设置位值
int memcopy(const void *ptrl,void*dest,size_t nbytes); //用法同上
int memcmp(const void *ptrl1,const void *ptrl2,size_t nbytes); //用法同上
整数与IP地址的转换
#include <arpa/inet.h>
int inet_aton(const char *straddr,struct in_addr *addrptr); //将点分十进制的IP地址转换为网络字节序的32位二进制
char *inet_ntoa(struct in_addr); //将地址结构转换为点分十进制ip地址对应的串
in_addr_t inet_addr(const char *straddr); //和inet_aton的功能相同
域名与IP地址之间的转换
#include <netdb.h>
struct hostnet *gethostbyname(const char *hostname); //实现域名或主机名到IP地址的转换
struct hostnet *gethostbuaddr(const char * addr,size_t len,int family); //len是字符串的大小,family是协议族
/结构体 struct hostent也在<netdb.h>中定义*/
struct hostent{
char *h_name; //主机的正式名称
char **h_aliases; //主机的别名
int h_addrtype; //主机的地址类型
int h_length; //主机的地址长度
char **h_addr_list; //主机的IP地址列表
};
#define h_addr h_addr_list[0]; //主机的第一个IP地址