表示IPv4地址的结构体
1 2 3 4 5 6
| struct sockaddr_in { sa_family_t sin_family; uint16_t sin_port; struct in_addr sin_addr; char sin_zero[8]; };
|
其中的struct in_addr里面为一个32位整型
1 2 3
| struct in_addr { In_addr_t s_addr; };
|
sockaddr_in的成员分析
sin_family
地址族 |
含义 |
AF_INET |
IPv4网络协议中使用的地址族 |
AF_INET6 |
IPv6网络协议中使用的地址族 |
AF_LOCAL |
本地通信中采用的UNIX协议的地址族 |
sin_port
保存16位端口号,且以网络字节序保存。
sin_addr
保存32位IP地址信息,也以网络字节序保存。
sin_zero
无特殊含义。方便结构体sockaddr_in的大小与sockaddr结构体保持一致。必须填充0。
sockaddr
1 2 3 4
| struct sockaddr { sa_family_t family; char sa_data[14]; };
|
字节序与网络字节序
CPU像内存保存数据的方式有2种
- 大端序:高位字节存放到低位地址。
- 小端序:高位字节存放到高位地址。
对于网络字节序,统一采用大端序。
字节序转换
1 2 3 4
| unsigned short htons(unsigned short); unsigned short ntohs(unsigned short); unsigned long htonl(unsigned long); unsigned long ntohl(unsigned long);
|
htons中的h代表主机字(host)节序,n代表网络(network)字节序。
将字符串信息转换为网络字节序的整数型
1 2 3 4
| #include <arpa/inet.h>
in_adddr_t inet_addr(const char* string);
|
可以向该函数传递类似"xxx.xxx.xxx.xxx"十进制格式的字符串。
1 2 3 4
| #include <arpa/inet.h>
int inet_aton(const char* string, struct in_addr* addr);
|
inet_aton函数和inet_addr函数功能完全相同,但inet_aton可以自动填入in_addr的结构体。
1 2 3 4
| #include <arpa/inet.h>
char* inet_ntoa(struct in_addr adr);
|
该函数将整数型IP地址转换为字符串格式并返回。
网络地址初始化
套接字常见的初始化方式。
1 2 3 4 5 6 7
| struct sockaddr_in addr; char* serv_ip = "123.124.1.23"; char* serv_port = "9190"; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(serv_ip); addr.sin_port = htons(atoi(serv_port));
|
INADDR_ANY
每次创建套接字都要输入IP会很繁琐,所以可以利用INADDR_ANY
1 2 3 4 5 6
| struct sockaddr_in addr; char* serv_port = "9190"; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(atoi(serv_port));
|
该方式可自动获取运行服务器端的计算机IP地址。
向套接字分配网络地址
1 2 3 4 5 6
| #include <sys/socket.h>
int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int serv_sock; struct sockaddr_in serv_addr; char* serv_port = "9190";
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(serv_port));
bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)); ...
|
基于UDP的数据I/O函数
由于UDP套接字不会保持连接状态,因此每次传输数据都需要添加目标地址信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <sys/socket.h>
ssize_t sendto(int sock, void* buff, size_t nbytes, int flags, struct sockaddr* to, socklen_t addrken);
ssize_t recvfrom(int sock, void* buff, size_t nbytes, struct sockaddr* from, socklen_t* addrlen);
|
断开连接的shutdown函数
shutdown函数用来关闭其中的一个流。
1 2 3 4 5
| #include <sys/socket.h>
int shutdown(int sock, int howto);
|
对于第二个参数有:
- SHUT_RD:断开输入流。
- SHUT_WR:断开输出流。
- SHUT_RDWR:同时断开I/O流。