本系列主要描述网络编程中使用到的基础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信号。
- flags 参数为数据收发提供了额外的控制:
- 返回值
- 成功是返回读取的字节,失败则返回-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;