[a] socket / socketpair
#include <sys/socket.h> int socket(int domain, int type, int protocol)
int socketpair(int domain, int type, int protocol, int sv[2])
//成功返回套接字描述符,出错返回 -1
- 套接字描述符本质上是就是文件描述符
- socket 类型由三个参数共同确定
- domain:AF_UNIX / AF_LOCAL / AF_INET / AF_INET6,其中 AF_LOCAL 是 AF_UNIX 的别名
- type:SOCK_STREAM / SOCK_DGRAM / SOCK_SEQPACKET / SOCK_RAW
- protocol:IPPROTO_TCP / IPPROTO_UDP / IPPROTO_ICMP / IPPROTO_IP / IPPROTO_IP6,通常设为 0,表示采用与 type 对应的默认值
- 可以操纵套接字的通用函数:close /read / write / dup / dup2 / fcntl / epoll / kqueue ...
- socketpair 仅用于创建匿名 UNIX 域套接字,功能相当于全双工管道
[b] shutdown
#include <sys/socket.h> int shutdown(int sockfd, int how) //成功返回 0,出错返回 -1
- 用于关闭双向传输中的一个或全部两个方向
- 与 close 的区别是,shutdown 允许使一个套接字处于不活动状态,和引用它的描述符数量无关
- how 参数可用值:SHUT_RD / SHUT_WR / SHUT_RDWR
[c] htonl / htons / ntohl / ntohs
#include <arpa/inet.h> uint32_t htonl(uint32_t hostint32) uint16_t htons(uint16_t hostint16) //返回以网络字节序表示的整数 uint32_t ntohl(uint32_t netint32) uint16_t ntohs(uint16_t netint16) //返回以主机字节序表示的整数
- TCP/IP 协议栈使用大端字节序(big-endian,右侧代表高位),Intel 处理器通常采用小端字节序(little-endian,左侧代表高位)
[d] getaddrinfo / freeaddrinfo / gai_strerror / getnameinfo
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) //成功返回 0,出错返回非 0 void freeaddrinfo(struct addrinfo *ai) const char *gai_strerror(int errno) //返回描述错误码的字符串指针 int getnameinfo(const struct sockaddr *saddr, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags) //成功返回 0,出错返回非 0
struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_profocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; //canonical name,not alias name! struct addrinfo *ai_next; //next in list }
- getaddrinfo 函数通过主机名或服务名或两者一起查询对应的网络地址信息,存储在 addrinfo 链表结构中,若查询出错,不能使用 perror 或 strerror 生成错误信息,需要使用 gai_strerror
- *hints 参数用于设定过滤条件,可以设定字段: ai_flags / ai_family / ai_socktype / ai_protocol ,其余字段必须指定为 0 或 NULL
- **res 参数用于存储查询结果
- struct addrinfo 结构体
- ai_flags(按位 '|' 组合,作用于 *hints 参数)
- AI_CANONNAME:提供的查询依据是正规主机名称,不是别名
- AI_NUMERICHOST:提供的主机名不是字符串,是点分样式的 IP 地址
- AI_NUMERICSERV:提供的服务名不是字符串,是端口号
- ...
- ai_family:AF_UNIX / AF_INET / AF_INET6 ...
- ai_socktype:SOCK_STREAM / SOCK_DGRAM / SOCK_SEQPACKET ...
- ai_protocol:IPPROTO_TCP / IPPROTO_UDP / IPPROTO_ICMP ...
- ai_addrlen:地址长度
- *ai_addr:指向 struct sockaddr 通用地址结构体的指针
- *ai_canonname:正规主机名,非别名
- *ai_next:指向下一个 addrinfo 结构体(如果有)
- ai_flags(按位 '|' 组合,作用于 *hints 参数)
- freeaddrinfo 函数用于释放 addrinfo 链表结构
- getnameinfo 函数将二进制网络地址信息转换成主机名或服务名,flags 可用标志如下:
- NI_DGRAM:指定此标志将返回 datagram 对应的服务端口号,默认将返回 stream 对应的端口号,如某个服务 TCP / UDP 端口号不相同的情况
- NI_NUMERICHOST:返回主机 IP 点分形式
- NI_NUMERICSCOPE:针地 IPv6 返回范围 ID 的数字形式,而非名字
- NI_NUMERICSERV:返回服务的端口号,而非名称
[e] bind
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t len); //成功返回 0,出错返回 -1
/*通用地址结构*/ struct sockaddr { sa_family_t sa_family; char sa_data[14]; unsigned char sa_len; //FreeBSD only! } /*AF_UNIX 地址结构*/ struct sockaddr_un { sa_family_t sun_family; //AF_UNIX char sun_path[104]; //For Linux: sun_path[108] unsigned char sun_len; //FreeBSD only! sockaddr len including null } /*AF_INET 地址结构*/ struct sockaddr_in { sa_family_t sun_family; in_port_t sin_port; struct in_addr sin_addr; //IPv4 address; unsigned char sin_zero[8]; //Linux only! must fill with 0 } struct in_addr { in_addr_t s_addr; }
- 在服务器端,bind 函数用于将 addr 与 sockfd 关联在一起,若 addr 指定为 INADDR_ANY,表示此套接字可以接收当前系统所安装的任何一个网卡的数据包
- socklen_t 类型字段可使用常量 INET_ADDRSTRLEN / INET6_ADDRSTRLEN 指定恰当的空间大小(长度)存放代表 IPv4 与 IPv6 地址的字符串
[f] getsockname / getpeername
#include <sys/socket.h> int getsockname(int sockfd, struct sockaddr *addr, socklen_t *alenp) int getpeername(int sockfd, struct sockaddr *addr, socklen_t *alenp) //成功返回 0,出错返回 -1
- getsockname 函数用于查询与 sockfd 关联的本机地址,getpeername 用于查询与 sockfd 关联的对端主机地址(前提是已建立连接)
[g] listen
#include <sys/socket.h> int listen(int sockfd, int backlog) //成功返回 0,出错返回 -1
- 调用 listen 函数后,服务器才能被连接
- 用于创建监听队列,backlog 参数指定了队列长度,表示可同时接受的连接请求最大数量
[h] accept
#include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *alenp) //成功返回实际与客户端建立连接的新 sockfd,出错返回 -1
- 传递给 accept 的原始 sockfd 并没有与客户端连接,而是继续用于接收其它连接请求
- addr 参数用于存放配对的客户端 sockfd 地址,函数返回时,将把 alenp 的值更新为客户端地址的实际长度
[i] connect
#include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t len) //成功返回 0,出错返回 -1
- 处理面向连接的网络服务(SOCK_STREAM / SOCK_SEQPACKET),在开始交换数据之前,需要首先建立连接
- 通常客户端调用 connect 发起与服务器的连接,addr 参数指定要连接的服务器地址
- 若先前创建 sockfd 时未绑定一个本机地址,此时 connect 会自动给 sockfd 绑定一个默认地址
- 可用于 SOCK_DGRAM,此时传送报文的目标地址会设置成 addr,之后每次传送报文时不再需要提供地址,同时,也仅能接收来 addr 的报文
[j] send / sendto / sendmsg
#include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags) ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) //成功返回送出的字节数,出错返回 -1
- send 成功返回,表示数据已发送到网络上,不代表连接的另一端已收到数据,通常用于面向连接的套接字(除非事先调用 connect 指定了无连接套接字的目标地址),对于支持报文件边界的协议,若尝试发送的单个报文长度超过协议所支持的最大长度,将出错返回
- 除 flags 参数外,其余参数与 write 相同,flags 可用标志:
- MSG_DONTWAIT:非阻塞传输,意同 O_NONBLOCK
- MSG_DONTROUTE:勿将数据包路由出本地网络
- MSG_NOSIGNAL:在写无连接的套接字时,不产生 SIGPIPE 信号
- MSG_OOB:发送带外数据(即优先传送数据,不排队,须协议支持)
- MSG_EOR :如果协议支持,标记记录结结束
- sendto 与 send 类似,多出的两个参数用于指定目标地址和长度,主要用于无连接套接字
- sendmsg 行为类似于 writev
[k] recv / recvfrom / recvmsg
#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags) ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
- flags:
- MSG_DONTWAIT:非阻塞
- MSG_OOB:若协议支持,获取带外数据
- MSG_PEEK:返回数据包内容,但不真正取走数据
- MSG_WAITALL:针对 SOCK_STREAM 等待所有的数据可用时再返回
[l] setsocketopt / getsocketopt
#include <sys/types.h> #include <sys/socket.h> int getsockopt(int s, int level, int optname, void * restrict optval, socklen_t * restrict optlen) int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
- level:
- SOL_SOCKET:表示将设置通用选项
- IPPROTO_TCP:TCP 协议特定选项
- IPPROTO_IP:IP 协议特定选项
- ...
- option:
- SO_DEBUG enables recording of debugging information
- SO_REUSEADDR enables local address reuse
- SO_REUSEPORT enables duplicate address and port bindings
- SO_KEEPALIVE enables keep connections alive
- SO_DONTROUTE enables routing bypass for outgoing messages
- SO_LINGER linger on close if data present
- SO_BROADCAST enables permission to transmit broadcast messages
- SO_OOBINLINE enables reception of out-of-band data in band
- SO_SNDBUF set buffer size for output
- SO_RCVBUF set buffer size for input
- SO_SNDLOWAT set minimum count for output
- SO_RCVLOWAT set minimum count for input
- SO_SNDTIMEO set timeout value for output
- SO_RCVTIMEO set timeout value for input
- SO_ACCEPTFILTER set accept filter on listening socket
- SO_NOSIGPIPE controls generation of SIGPIPE for the socket
- SO_TIMESTAMP enables reception of a timestamp with datagrams
- SO_BINTIME enables reception of a timestamp with datagrams
- SO_ACCEPTCONN get listening status of the socket (get only)
- SO_TYPE get the type of the socket (get only)
- SO_PROTOCOL get the protocol number for the socket (get only)
- SO_PROTOTYPE SunOS alias for the Linux SO_PROTOCOL (get only)
- SO_ERROR get and clear error on the socket (get only)
- SO_SETFIB set the associated FIB (routing table) for the socket (set only)
[m] inet_addr / inet_ntoa
#include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> char *inet_ntoa(struct in_addr in) //转换二进制地址为字符串形式 int inet_aton(const char *cp, struct in_addr *inp) //转换字符串地址为二进制地址(网络字节序),结果写入 inp 指向的结构体
- 仅能用于 IPv4,可使用 getaddrinfo 与 getnameinfo 实现