sys/socket.h
- 引用
#include <sys/socket.h>
连接
-
创建套接字
int socket(int domain, int type, int protocol);-
domain 协议族
- PF_INET —— IPv4【常用】
- PF_INET6 —— IPv6
- PF_LOCAL —— 本地UNIX
- PF_PACKET —— 底层套接字
- PF_IPX —— IPX Novell
-
type 传输类型
- SOCK_STREAM —— 面向连接,基于TCP
- SOCK_DGRAM —— 面向消息,基于UDP
-
protocol 最终选择协议
- 通常随便填0,为什么?
- 前面两个如果选 PF_INET + SOCK_STREAM,那唯一确定协议为 IPPROTO_TCP,所以可随便填0
- 前面两个如果选 PF_INET + SOCK_DGRAM,那唯一确定协议为 IPPROTO_UDP,所以可随便填0
- 通常随便填0,为什么?
-
-
绑定IP地址和端口
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);- sockfd 上面创建的套接字
- myaddr 地址结构体的指针
- 需通过sockaddr_in结构填写地址族、IP地址、端口,然后取址并转换为sockaddr指针传入
- addrlen 地址结构体的大小
-
开启监听,进入等待请求状态
int listen(int sockfd, int backlog);- sockfd 上面创建的套接字
- backlog 连接请求等待队列的长度
- 可以把服务端套接字理解为一扇门,在繁忙时,任何客户端请求都要进入等待队列
-
接受请求,自动创建新的套接字处理
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);- sockfd 上面创建的套接字
- addr 地址结构体的指针
- 传入后,此函数会把客户端信息填充进去
- addrlen 一个指针,指向地址结构体的大小
- 将返回一个新套接字,用于与客户端交换信息
-
【客户端】发起请求
int connect(int sock, struct sockaddr *servaddr, socklen_t addrlen);- sock 客户端的套接字
- servaddr 目标服务端的地址结构体的指针
- addrlen 地址结构体的大小
- 会在连接前自动分配IP和随机端口,所以无需服务端bind的过程
- 在服务端接受连接、或者断网情况下,才会完成函数调用并返回
- 注意,所谓服务端接受连接,并不代表accept被调用,而是指进入到等待队列
- 因此返回后,并不会立即进行数据交换
-
断开连接
int shutdown(int sock, int howto);- sock 需要断开的与客户端交流的新套接字
- howto 断开的方式
- SHUT_RD —— 断开输入流
- SHUT_WR —— 断开输出流
- SHUT_RDWR —— 同时断开I/O流
- 服务端可使用 SHUT_WR 实现半关闭
- 只关闭服务端的输出流,告知客户端数据传送完毕,但客户端还能继续发送数据
- 这时客户端的read函数会返回0,就可以考虑直接close,或者再传一点信息再close
-
以上函数若调用失败,均返回-1,需要在调用后检查
UDP
-
UDP直接传送
ssize_t sendto(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);- sock 上面创建的套接字
- buff 待传送数据的缓冲区指针
- nbytes 待传送数据的大小
- flags 可选项,传0即可
- to 数据目标端的地址结构体的指针
- addrlen 地址结构体的大小
- 返回成功传送的字节数
- UDP不会保持连接,因为可以直接传输,只不过每次传输都要添加地址
- 而TCP需要绑定IP端口+监听
- UDP不存在请求和受理过程,因此无法区分服务端和客户端,只是把提供内容的叫做服务端
-
UDP直接接收
ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t addrlen);- 基本同上
- buff 保存接收数据的缓冲区指针
- nbytes 可接收的最大字节数
- from 数据来源端的地址结构体的指针
- 返回成功接收的字节数
配置
-
读取套接字的配置
int getsockopt(int sock, int level, int optname, void *optval, socklen_t* optlen);- level 协议层,可选三个值
- SOL_SOCKET —— 套接字层
- IPPROTO_IP —— IP层
- IPPROTO_TCP —— TCP层
- optname 想查询的具体选项名
- 取值归属于某个协议层,比如SO_xxx / IP_xxx / TCP_xxx
- optval 保存查看结果的缓冲地址
- optlen 保存查看结果占用的缓冲大小,用来读取optval
- level 协议层,可选三个值
-
修改套接字的配置
int setsockopt(int sock, int level, int optname, const void* optval, socklen_t optlen);- level 协议层,同上
- optname 想配置的具体选项名,常用的有
- SO_SNDBUF 发送缓冲的大小、SO_RCVBUF 接收缓冲的大小,操作系统会参考提供的数字
- SO_REUSEADDR —— 设置为需要重用地址,避免Time-wait状态下无法立即重用端口
- 使用例子
int option = TRUE; socklen_t optlen = sizeof(option) setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen);
- 使用例子
- TCP_NODELAY —— 设置为“禁用Nagle算法”,实现无需等待ACK的连续传输,加快大文件的传输速度
- 使用例子
int opt_val = 1; setsockopt(serv_sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, sizeof(opt_val));
- 使用例子
- optval 保存目标配置信息的缓冲地址
- optlen 保存目标配置信息占用的缓冲大小
- 经常修改的选项
- SO_SNDBUF