常用的数据结构及函数
地址转换类
主机字节序与网络字节序的转换
主机序通常为小端,网络字节序为大端。下列函数在
- usinged long int htonl(unsigned long int hostlong) —— 主机序转网络序(host to network long)
- usinged short int htons(unsigned short int hostlong) —— 主机序转网络序(host to network short)
- usinged long int ntohl(unsigned long int hostlong) —— 网络序转主机序(network to host long)
- usinged short int ntohl(unsigned short int hostlong) —— 网络序转主机序(network to host short)
IP地址转换函数
计算机能够识别二进制的IP地址,但生活中我们常用点分十进制来表示IPv4地址,用十六进制表示IPv6地址,于是需要一些函数对他们进行一些转换,这些函数位于
- in_addr_t inet_addr(const char* strprt) —— 将点分十进制IPv4地址转化成网络字节序的地址
- int inet_aton(const char cp, struct in_addr inp) —— 同上,将结果存在指针里
- char* inet_ntoa(struct in_addr in) —— 将二进制结果转为点分十进制
- int inet_pton(int af, const char src, void dst) —— 将十六进制地址转为二进制地址,并存在指针。其中af表示地址族,可以是AF_NET或AF_NET6。
- const char inet_ntop(int af, const void src, char* dst, socklen_t cnt) —— 将二进制地址转换为十六进制地址,其中cnt指定目标存储单元大小
socket地址结构
通用socket地址——sockaddr
#include
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
其中,sa_family是地址族,包括:PF_UNIX(UNIX本地域协议族)、PF_INET(TCP/IPv4地址协议族)、PF_INET6(IPv6地址协议族)。注意:AF_xxx和PF_xxx在socket.h中值相同,因此常混用。
sa_data用于存放socket地址值,不同协议的地址具有不同含义和长度。PF_UNIX存放的是文件的路径名(疑问:在PF_UNIX中,socket是以一个什么样的形式存在的?),长度可达到108字节;在PF_INET中,存放16bit的端口号和32bit的IPv4地址;在AF_INET6中,存放的是128bit的IPv6地址、16bit端口号以及32bit的ID。
由于14字节显然无法容下所有地址,因此linux中sockaddr定义为:
#include
struct sockaddr {
sa_family_t sa_family;
unsigned long int __ss_align;
char __ss_padding[128-sizeof(__ss_align)];
}
linux下各协议的socket地址
- PF_UNIX的socket地址结构:
#include
struct sockaddr_un {
sa_family_t sin_family;
char sun_path[108]; //文件路径名
}
- PF_INET的socket地址结构;
struct sockaddr_in {
sa_family_t sin_family;
u_int16_t sin_port; //端口号
struct in_addr sin_addr; //IPv4结构体
}
struct in_addr {
u_int32_t s_addr;
}
- PF_INET6的socket地址结构;
struct sockaddr_in6 {
sa_family_t sin_family;
u_int16_t sin_port; //端口号
u_int32_t sin6_flowinfo; //流信息,应设置为0
struct in6_addr sin6_addr; //IPv4结构体
u_in32_t sin6_scope_id;
}
struct in_addr {
unsigned char sa_addr[16];
}
注意:所有专用socket地址在实际使用时都需要强制转换为通用sockaddr!
socket通信常用基础函数
创建socket
int socket(int domain, int type, int protocol);
其中domain为底层协议族,即PF_UNIX, AF_INET, AF_NET6;
type为服务类型,主要分为SOCK_STREAM(流服务)和SOCK_UGRAM(数据报服务),TCP面向流,UDP面向数据报。
protocol表示在前两个参数构成的集合下,再选择一个具体的协议。但由于这个集合通常只有一个值,一般情况下protocol都设置为0。
该函数设置成功时会返回一个socket文件描述符,失败时会返回-1并设置errno
注意:若设置失败,可以使用perror()来打印具体的错误信息
socket命名(绑定)—— 仅服务端使用
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
bind作用是将由socket()创建后的未经分配的文件描述符与一个ip地址绑定。注意:上面提到的需要强制地址转换就是在这里进行。addrlen直接用sizeof即可。
监听socket —— 仅服务端使用
int listen(int sockfd, int backlog)
此处的backlog指所有处于完全连接和半连接状态的客户端socket上限。最多可有backlog+1个socket请求连接。
接收连接 —— 仅服务端使用
int accept(int sockfd, struct sockaddr addr, socklen_t addrlen)
该函数作用是从listen()监听的队列中接收一个连接,接收后返回一个新的连接socket,服务器可通过读写该socket与客户端进行通信。
注意:对于客户端来说,accept仅仅只是从请求连接的socket中选一个出来进行连接,而不管客户端是否保持连接
发起连接 —— 仅客户端使用
int accept(int sockfd, struct sockaddr serv_addr, socklen_t addrlen)
关闭连接
int close(int fd)