Socket 基础
Socket概念
Socket英文愿意是“插孔”或“插座”,作为BSD UNIX的进程通信机制后,取后一种意思,通常也被称为套接字。使用TCP/IP协议的应用程序通常采用的应用编程是使用UNIX BSD的套接字Socket,来实现网络进程之间的通信。
Socket用于描述IP地址和端口,是一个通信链的句柄,用来实现不同虚拟机或物理机之间的通信。应用程序通过Socket向网络发出请求或应答请求。网络中两个进程通过一个双向的通信连接实现数据的交换,建立网络通信连接至少需要一对Socket,连接的一端称为一个Socket。
具有唯一标识的网络进程可利用 Socket 进行通信,而 Socket 是在应用层和传输层之间的一个抽象层,Socket把TCP/IP层复杂的操作抽象为简单的接口供应用层调用,以实现进程在网络中的通信。TCP/IP协议存在于操作系统中,网络服务是通过操作系统提供的,因此在操作系统中增加支持TCP/IP的系统调用Socket。
socket 通信流程
在Linux系统中,一切皆文件,Socket也不例外,对socket的操作实际上也是对某种特殊文件的读写操作。只不过操作的文件在服务器和客户端各自拥有一个。
下图是最基本的socket 通信流程,也就是最初的网络I/O模型(阻塞I/0模型),注意这里展示的是TCP通信流程。
服务器端:
1.生成一个监听的socket,绑定IP地址和端口号
2.监听客户端的请求,并接受客户端的连接
客户端:
1.创建通信socket,
2.连接到服务器
客户端与服务器通信:
1.客户端连接成功向服务器发送连接状态信息
2.服务器端Accetp返回连接socket
3.客户端使用socket写入数据
4.服务器使用连接socket来接收数据
Socket API
创建socket
int socket(int domain, int type, int protocol)
- domain
协议簇/域,通常为AF_INET(IPv4)、AF_INET6(IPv6) - type
套接字类型,主要是SOCK_STREAM(TCP)、SOCK_DGRAM(UDP),另外包括SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。 - protocol
顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。 - 返回值
success 返回大于0 的socketfd值,错误, 返回-1, 使用errno得到错误值。
socket绑定
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
将socket和对应的端口和地址进行绑定,一般用于绑定服务器的监听socket,以便能够让客户端连接到服务器对应的ip和端口。
- sockfd
使用socket接口创建的socket文件描述符 - addr
服务器端的通信地址
其中参数的结构体如下表示
struct sockaddr{
sa_family_t sa_family;
char sa_data[14];
};
- addrlen
参数addr的长度 - 返回值
bind函数调用成功返回0,否则返回-1,并设置erro;
listen
int listen(int sockfd, int backlog);
服务器端使用listen接口来监听客户端的连接
- sockfd
使用socket接口创建的socket文件描述符 - backlog
backlog通常被描述为连接队列的限制,理解这个参数需要和accept接口一起,一般accept会返回一个sockfd,如果服务器一直没有调用accpet接口,每次有客户端的连接,那么连接队列就会增加1,那么当连接的队列数达到backlog的数字,那么服务器就不会在监听其他客户端的连接。 - 返回值
bind函数调用成功返回0,否则返回-1,并设置erro;
connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
该接口用于客户端连接服务器
- sockfd
使用socket接口创建的socket文件描述符 - serv_addr
该参数是可传入的实际结构体为sockaddr_in和sockadd_in6
sockaddr_in:该结构体存有将要connect的ipv4的网络地址以及端口和协议族
sockaddr_in6:该结构体存有将要connect的ipv6的网络地址以及端口和协议族 - addrlen
为传入参数sockaddr结构体的大小,该参数实际应该是一个传出参数。 - 返回值
0-连接成功,-1连接服务器失败。错误值使用errno记录。
accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
主要用于服务器端接收客户端的连接,当客户端发起连接请求后,accept会返回当前连接客户端的socket信息。
参数同connect 接口
-返回值
成功,返回一个socketfd,这个socketfd记录了连接客户端的信息,服务器使用这个socket同客户端进行通信。
recv/recvfrom
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);
recv和recvfrom主要用于接收对等端发送的信息,接收的信息存储于buf参数中。
recvfrom与recv不同之处的主要是前者多了2个参数,src_addr和addrlen,从参数名字就知道这2个参数主要是用于表示该请求的发送者的地址信息,由于UDP通信一般不需要客户端先连接服务器,因此recvfrom通常用于UDP通信,recv用于TCP通信。
- sockfd
对于服务器端,sockfd为accept接口返回的socket(注意这个sockfd不为服务器使用socket接口生成的socket),对于客户端,为socket接口生成的socket信息。 - buf
存放要发送数据的缓冲区 - len
存放发送数据缓冲区的字节大小 - flags
接收数据的参数设置信息,一般设置为0,主要有以下几个值(很少用)
MSG_DONTWAIT:仅支持非阻塞的socket,如果socket为阻塞的,会报EAGAIN 或者 EWOULDBLOCK错误
MSG_OOB:发送或者接收带外数据
MSG_WAITALL:等待请求的所有数据到达后才返回
MSG_PEEK:窥探请求发送的数据类型,主要用于查看请求的前几个字节,判断是文本协议还是二进制协议,然后再针对不同的协议来做不同的操作。 - src_addr
消息发送方的地址信息 - addrlen
src_addr地址信息的长度 - 返回值
成功,返回接收到的实际字节数,错误,返回-1,错误码可以通过errno获取到
send/sendto
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);
send/sendto主要用于发送消息给对等方,send主要用于TCP通信,sendto主要用于UDP通信。
- sockfd
参数同recv/recvfrom接口的第一个参数信息 - buf
要发送的缓冲区信息 - len
要发送的缓冲字节长度 - flags
主要设置发送信息的标志,一般设置为0,其他标志主要有
MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
MSG_OOB:指明发送的是带外信息
- dest_addr
要发送的目标地址信息 - addrlen
发送的目标地址长度
其他接口
- write()
- read()
- close():关闭socket
- gethostbyname():IPv4专用
- gethostbyaddr():IPv4专用
- getsocketopt()获取对应socket的选项值
- setsocketopt()设置对应socket的选项值