1.地址结构
1.1 IPv4地址结构
struct in_addr{
in_addr_t s_addr;
};
struct sockaddr_in
{
uint8_t sin_len;
sa_family_t sin_family; /* 协议族 AF_INET/AF_INET6/AF_LOCAL/AF_ROUTE/AF_KEY */
in_port_t sin_port; /* 端口号,网络序 */
struct in_addr sin_addr; /* 地址,网络序 */
char sin_zero[8];
};
由于一些历史原因,很多函数使用通用的套接字地址结构作为参数:
struct sockaddr
{
uint8_t sa_len;
sa_family_t sa_family;
char da_data;
};
1.2 IPv6地址结构
struct in6_addr{
uint8_t s6_addr[16];
};
struct sockaddr_in
{
uint8_t sin6_len;
sa_family_t sin6_family; /* 协议族 AF_INET/AF_INET6/AF_LOCAL/AF_ROUTE/AF_KEY */
in_port_t sin6_port; /* 端口号,网络序 */
struct in6_addr sin6_addr; /* 地址,网络序 */
uint32_t sin6_scope_id;
};
2. 地址转换函数
2.1 字符串地址格式转换为网络序的二进制值
2.1.1 inet_aton
int inet_aton(const char*strptr, struct in_addr *addrptr);入参:
1. strptr,指向字符串的指针;
2. addrptr, 指向存储转换后数据的指针;
返回值:
字符串有效返回1,无效返回0;
2.1.2 inet_addr
in_addr_t inet_addr(const char*strptr);
入参:
1. strptr,指向字符串的指针;
返回值:
转换后数据 or INADDR_NONE;
INADDR_NONE一般为0xffffffff,所以“255.255.255.255”不能由这个函数处理;
2.2 网络序的二进制值转换为字符串地址格式
char *inet_ntoa(struct in_addr inaddr);
入参:
struct in_addr 而不是指针;
返回值:存储转换后结果的指针;
这个指针是静态变量,因此这个函数是不可重入的;
2.3 inet_pton和inet_ntop
这两个函数适用于IPv4和IPv6;
presentation-->numeric
int inet_pton(int family, const char *strptr, void *addrptr);
numeric-->presentation
const char* inet_ntop(int family, void *addrptr, const char *strptr, int len);
strptr为字符串地址格式;
addrptr为数值格式;
3. socket相关基本函数
3.1 socket()
#include<sys/socket.h>
int socket(int family, int type, int protocol);
入参说明:
family | 说明 |
AF_INET | IPv4协议 |
AF_INET6 | IPv6协议 |
AF_LOCAL | Unix协议域 |
AF_ROUTE | 路由套接字 |
AF_KEY | 秘钥套接字 |
type | 说明 |
SOCK_STREAM | 字节流套接字 |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 有序分组套接字 |
SOCK_RAW | 原始套接字 |
protocol | 说明 |
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
socket在调用成功时会返回套接字描述符,失败是返回INVALID_SOCKET(-1);
一般设置参数时,将protocol设置为0,以选择给定family和type组合的系统默认值;并非每种family和type的组合都是有效的;
3.3 bind()
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind函数把一个本地协议地址绑定到套接字上;
用户可以指定ip地址和端口号中的任意0个、1个或两个;
如果指定端口号为0,那么内核就在bing被调用时选择一个临时端口;
如果指定的IP地址为通配地址(INADDR_ANY,一般为0),那么内核等到套接字已连接(TCP)或已在套接字上发出数据报(UDP)时才选择一个本地IP地址;
3.4 connect()
#include<sys/socket.h>
int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
addr中必须含有服务器的IP地址和端口号;
对于TCP而言,connect会触发三次握手,且只在连接建立成功或失败后才返回;
如果connect失败则该套接字不再可用(此时socket的状态是未知的),必须关闭,我们不能对这样的套接字再次调用connect函数;
3.5 listen()
#include<sys/socket.h>
int listen(int sockfd, int backlog);
listen将一个未连接的套接字转换成一个被动套接字,调用socket()创建套接字时,套接字都被设置为主动套接字;
backlog指定了未完成链接队列(SYN_RECV状态)和已完成队列(ESTABLISHED状态)的最大连接个数;
不要把backlog设置为0,因为不同的实现对此的解释不同;
在三次握手完成之后,但在服务器调用accept之前到达的数据应由服务器TCP排队,最大数据量为相应已连接套接字的接收缓冲区大小;
3.6 accept()
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr* cliaddr, socklen_t *addrlen);
addrlen是值-结果型参数;
cliadd和addrlen也可以全部置为NULL,表示对客户端协议地址不关系;
accept()从已连接队列头返回一个已完成连接, 如果已完成连接队列是空的,那么进程就会进入睡眠(套接字为阻塞方式),直到TCP在该队列放入一个连接;
3.7 getsockname()和getpeername()
#include<sys/socket.h>
int getsockname(int sockfd, struct sockaddr* localaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr* peeraddr, socklen_t *addrlen);
返回与sockfd关联的本地协议地址或远端协议地址;
3.8 shutdown()
#include<sys/socket.h>
int shutdown(int sockfd, int howto);
成功则返回0,出错则返回-1;
howto的取值:
①SHUT_RD,关闭连接的读这一半,套接字不再接收任何数据,并且接收缓冲区中的现有数据都被丢弃;
TCP套接字调用shutdown后,由该套接字接收的来自对端的任何数据都会被确认,然后丢弃;
②SHUT_WR,关闭连接的写这一半,当前留在发送缓冲区中的数据将被发送出去,后跟TCP的正常终止序列;
③SHUT_RDWR,读写同时关闭;