Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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
  • 绑定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
  • 修改套接字的配置

    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