Linux/Unix socket 基础API (一)

本系列主要描述网络编程中使用到的基础API,按照基础接口、特殊配置接口、网络信息接口等进行编排整理。已发表的博文也将保持不定期更新优化,力求精简准确。

socket基础API

1. 创建socket

  • 函数原型:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
  • 函数说明:
    创建一个sockfd,以文件描述符的形式作为网络实体抽象的操作接口,包括unix domain socket, internet domain socket等不同场景下,可以按照文件读写方式来收发数据。
  • 参数说明:

    • domain 表示使用何种底层协议族 , 比如PF_UNIX(alias PF_LOCAL, Unix domain socket),PF_INET (TCP/IPv4),PF_INET6 (TCP/IPv6)等。由于历史原因,地址族AF_*(包括AF_LOCAL)通常作为实际使用,AF_*PF_*对应同值;
    • type 表示指定服务类型,主要有SOCK_STREAM(TCP流服务)和SOCK_DGRAM(UDP数据报)等服务;
    • protocol 表示在前两个参数确定的协议集合下,进一步确认具体传输协议,比如:IPPROTO_TCP、IPPROTO_UDP等。由于前两个参数已经给出了足够的信息,该参数已经确定,因此通常该参数置为0即可。
  • 返回值
    ​成功返回socket文件描述符;失败返回 -1, 并设置errno。

2. 命名socket

  • 函数原型:
   #include <sys/types.h>
   #include <sys/socket.h>
   int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
  • 函数说明:
    ​将一个socket地址绑定到socket文件描述符上。客户端socket文件描述符通常不需要绑定socket地址,采用匿名方式即可。

  • 参数说明:

    • sockfd 表示需要命名(绑定)的目标socket文件描述符;
    • my_addr 表示将socket地址绑定至sockfd,即命名该sockfd;
    • addrlen 表示该地址的长度。
  • 返回值:
    ​成功时返回0, 失败时返回-1并设置errno, 常见的errno如下:

    • EACCES 被绑定的地址是受保护的地址(知名服务器端口:0~1023),仅超级用户能够访问;
    • EADDRINUSE 被绑定的地址正在使用中,比如将socket绑定到一个处于TIME_WAIT状态的socket地址。

3. 监听socket

  • 函数原型:
#include <sys/socket.h>
int listen(int sockfd, int backlog);
  • 函数说明:
    ​ 在内核创建最大长度为backlog的监听队列。

  • 参数说明:

    • sockfd 表示创建的socket 文件描述符;
    • backlog 表示提示内核监听队列中处于完全连接状态(ESTABLISHED)socket的最大长度,而半连接状态(SYN_RCVD)socket队列的最大长度定义在/proc/sys/net/ipv4/tcp_max_syn_backlog,若队列满,则peer客户端(connect())将收到ECONNREFUSED。
  • 返回值:
    ​成功时返回0, 失败则返回-1, 并设置errno。常见的errno如下:

    • EADDRINUSE 已有其他socket监听该port. 当未命名sockfd时(极罕见,例RPC,客户端通过端口映射器获取到临时端口完成建立连接),通常会从/proc/sys/net/ipv4/ip_local_port_range端口范围内获取临时端口,会发生临时端口范围内的端口都被使用,此时返回该错。

4.接受连接

  • 函数原型:
   #include <sys/types.h>
   #include <sys/socket.h>
   int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 函数说明:
    从内核的连接队列中获取已经完成三次握手的完成连接,并返回新的连接sockfd。服务器端可通过对该文件描述符的读写来完成与客户端的通信。

  • 参数说明:

    • sockfd 表示已监听的socket文件描述符;
    • addr 表示用来获取远端的socket地址;
    • addrlen 表示地址的长度。
  • 返回值:
    ​成功时返回新的连接socket文件描述符;失败则返回-1,并设置errno:

    • EAGAIN、EWOULDBLOCK :表示监听sockfd为非阻塞的,且当前没有可以接受的新连接。通常这两个错误码值相同,但POSIX.1-2001和POSIX.1-2008标准并不要求二者值一定相同,因此为保证兼容性,通常两者都需要检查。
    • 其他错误码,见名知意。

5.发起连接

  • 函数原型:
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr * serv_addr, socklen_t addrlen);
  • 函数说明:
    当三次握手中,客户端收到server-syn及local-syn-ack的合并报文段时,就可以返回了。表明sockfd已经可写。

  • 参数说明:

    • sockfd 表示客户端创建的socket文件描述符;
    • serv_addr 表示服务器端socket地址;
    • addrlen 表示服务器端socket地址的大小;
  • 返回值
    成功时返回0,且sockfd唯一标志了该连接,客户端通过读写该sockfd来与服务器端通信;失败时返回-1,并设置errno,常见错误码:

    • ECONNREFUSED 端口不存在、未监听、监听队列已满;
    • EINPROGRESS 当sockfd为非阻塞non-blocking时,且连接的三次握手并未完全完成,多见于IO复用。收到该错误码后,判断连接建立完成的常规方法(二选一):
    • 在select\poll\epoll等IO复用接口工具中对该sockfd进行可写事件的监听注册,若有可写事件,则按如下方式判断是否建立完成:
    int status = 0;
    int slen = 0;
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen) < 0)
    {
    perror("getsockopt error!");
    return -1;
    }
    if (status != 0) {
    perror("connect error!");
    return -1;
    }
    //连接建立完毕:
    conn_info.fd = fd;
    conn_info.status = CONN_ESTABLISHED;
    • 在select\poll\epoll等IO复用接口工具中对该sockfd进行可读事件的监听注册,若有可读事件,表示连接已经建立,此时直接读取服务器端发送到本地的数据,连接建立完成。

6.关闭连接

  • 函数原型:
  #include <sys/unistd.h>
  #include <sys/socket.h>
  int close(int sockfd);
  int shutdown(int sockfd, int hwoto);
  • 函数说明:
    • close函数会将本地sockfd的读写两个方向全部关闭,并将sockfd的引用计数减一。
    • shutdown可以分别对sockfd读端或者写端单独关闭;
  • 函数参数:
    • 见名知意
  • 返回值
    • 见名知意

7.TCP数据读写

  • 函数原型
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const coid *buf, size_t len, int flags);
  • 函数说明:
    专门用来发送接收TCP流数据读写。

  • 函数参数:

    • flags 参数为数据收发提供了额外的控制:
      • MSG_OOB发送和接收紧急保温;
      • MSG_NOSIGNAL 往读端关闭的管道或者socket连接中写入数据时,不引发SIGPIPE信号。
  • 返回值
    • 成功是返回读取的字节,失败则返回-1, 并设置errno,具体错误见名知意。

8.UDP数据读写

  • 函数原型
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addren);
  • 函数参数:

    • 由于UDP没有连接的概念,因此对于recvfrom,每次读取都要获取发送端的地址src_addr;对于sendto,每次发送都需要指定接收端的dst_addr地址;
    • flags 同recv,send中的说明;
  • 返回值:

    • 同recv, send中的说明。

9.通用读写函数

  • 函数原型
    #include <sys/socket.h>
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
  • 参数说明:

    • msg 为msghdr结构体类型的指针,该类型兼容TCP\UDP两种协议。
    • flags 同recv, send中的说明;
  • 返回值:
    recvmsg返回接收到的数据长度,sendmsg返回发送的数据长度;失败时都返回 -1;

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值