1 sockaddr和sockaddr_in
这两个结构体都用来处理网络通信的地址
sockaddr在头文件<sys/socket.h>
中定义,它的缺陷是sa_data将目标地址和端口混在了一起,其具体定义如下:
struct sockaddr{
sa_family_t sin_family; ;地址族
char sa_data[14]; ;14字节,包括socket中的目标地址和端口信息
}
sockaddr_in在头文件netinet/in.h
或arpa/inet.h
中定义,该结构体解决了sockaddr的缺陷,将port和addr分开储存在两个变量中,其定义如下:
struct sockaddr_in{
sa_family_t sin_family; //地址族
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用
该结构体中提到的另一个结构体in_addr定义如下,它用来存放32位IP地址:
struct in_addr{
In_addr_t s_addr; //32位IPv4地址
}
sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序(HBO)。
对比
两者长度一样,都是16个字节,即占用内存的大小是一致的,因此可以互相转化。两者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。
sockaddr_in是Internet环境下socket的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数。即,sockaddr_in用于socket定义和赋值,sockaddr用于函数参数。
htons()和inet_addr()
htons()的作用是将端口号由主机字节序转换为网络字节序的整数值(host to net)。
inet_addr()的作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。
inet_ntoa()的作用是将一个sin_addr结构体输出成IP字符串(network to ascⅡ)。
htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是16位的(short)。
与htonl()和htons()作用相反的两个函数是ntohl()和ntohs()。
2 socket()函数介绍
函数原型:
int socket(int domain, int type, int protocol);
这个函数建议一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1。
domain
这个参数用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族。通信协议族在文件sys/socket.h
中定义。
domain的值及含义如下表所示:
名称 | 含义 |
---|---|
PF_UNIX, PF_local | 本地通信 |
AF_INET, PF_INET | IPv4网络协议 |
PF_INET6 | IPv6网络协议 |
PF_IPX | IPX-NoveⅡ协议 |
PF_NETLINK | 内核用户界面设备 |
PF_X25 | ITU-T X25 / ISO-8208协议 |
PF_AX25 | Amateur radio AX.25 |
PF_ATMPVC | 原始ATM PVC访问 |
PF_APPLETALK | Appletalk |
PF_PACKET | 底层包访问 |
type
用于设置套接字通信的类型,主要值及对应解释如下:
名称 | 含义 |
---|---|
SOCK_STREAM | Tcp连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输 |
SOCK_DGRAM | 支持UDP连接(无连接状态的消息) |
SOCK_SEQPACKET | 序列化包,提供一个序列化的、可靠的、双向的基本连接的数据传输通道,数据长度定长。每次调用读系统调用时数据需要将全部数据读出 |
SOCK_RAW | RAW类型,提供原始网络协议访问 |
SOCK_RDM | 提供可靠的数据报文,不过可能数据会有乱序 |
SOCK_PACKET | 这是一个专用类型,不能呢过在通用程序中使用 |
注意,并不是所有的协议族都实现了这些协议类型,例如,AF_INET协议族就没有实现SOCK_SEQPACKET协议类型。
protocol
这个参数用于指定某个协议的特定类型,即type类型的某个类型。通常某协议中只有一种特定类型,这样protocol参数仅能设置为0;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。
errno
函数socket()并不总是执行成功,有可能会出现错误,错误的产生有多种原因,可以通过errno获得:
值 | 含义 |
---|---|
EACCES | 没有权限建立指定的domain的type的socket |
EAFNOSUPPORT | 不支持所给的地址类型 |
EINVAL | 不支持此协议或者协议不可用 |
EMFILE | 进程文件表溢出 |
ENFILE | 已经达到系统允许打开的文件数量,打开文件过多 |
ENOBUFS/ENOMEM | 内存不足。socket只有到资源足够或者有进程释放内存 |
EPROTONOSUPPORT | 指定的协议type在domain中不存在 |