Socket通信
1、什么是套接字
Socket是封装了TCP/IP协议簇的系统API接口,这使得程序员无需关注协议本身,直接使用socket提供的接口与不同主机间的进程互联通信。
目前市面上主流的操作系统都采用这套机制进制网络通信,所以不同种类的操作系统,使用不同的编程语言,只要调用操作系统提供的Socket接口,都能进行网络通信。
2、基于TCP协议的Socket编程模型
被连接者需要完成的任务(服务端):
1、创建socket内核对象,内核创建完成后会返回它的描述符(该描述只是为了完成连接,三次握手)
2、准备本机地址(ip地址+端口号)
3、绑定(把本机地址与socket对象进行绑定)
4、开启监听,并设置排队的队列长度
5、等待连接,连接成功后,内核会再返回一个连接成功的Socket描述符,专门用来通信
for (;;) {
6、接收请求
7、返回结果
}
8、关闭通信的Socket对象
9、关闭连接的Socket对象
连接者需要完成的任务(客户端):
1、创建socket内核对象,内核创建完成后会返回它的描述符
2、准备被连接者的地址(ip地址+端口号)
3、发起连接,使用Socket+地址(ip地址+端口号)发起连接请求
for (;;) {
4、发送请求
5、接收结果
}
6、关闭Socket对象
3、TCP通信需要使用Socket接口
/**
* 功能: 创建Socket对象
* @domain:
* AF_UNIX, AF_LOCAL 采用本地socket文件进行通信,如果用它则只能本机上的两个进程进行通信
* AF_INET IPv4地址
* AF_INET6 IPv6地址
* @type:
* SOCK_STREAM 数据流 TCP
* SOCK_DGRAM 报文 UDP
* @protocol:
* 特殊通信协议,写0即可
* 返回值:
* 成功则返回Socket对象描述符,失败返回-1。
*/
int socket(int domain, int type, int protocol);
// 基本地址类型,它是socket系列接口的表面参数,而实际使用的是sockaddr_un或sockaddr_in,我们需要把sockaddr_in强制转换成sockaddr类型。
struct sockaddr_in {
sa_family_t sin_family; // 地址类型,与domain保持一致即可
in_port_t sin_port; // 端口号,网络字节序的2字节整数
struct in_addr sin_addr.s_addr; // IP地址,网络字节序的4字节整数
};
/**
* 功能: 绑定Socket对象与通信地址
* @sockfd: Socket对象描述符
* @addr: 通信地址,实际提供可能是sockaddr_un或sockaddr_in,需要对它们进行强制转换
* @addrlen: addr结构体的字节数
* 返回值: 成功返回0,失败返回-1
*/
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
/**
* 功能: 开启Socket对象的监听
* sockfd: Socket地址描述符
* backlog: 备胎的数量
*/
int listen(int sockfd, int backlog);
/**
* 功能: 等待连接,没有成功连接之前,会进入阻塞状态
* @sockfd: Socket对象描述符
* @addr: 用于存储连接者的通信地址
* @addrlen: 既是输入(告诉accetp接口,addr结构体的字节数),也是输出(实际接收到的addr结构的字节数)
* 返回值: 建立连接的,能够通信的Socket对象描述符,失败返回-1
*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/**
* 功能: 让sockfd对象向addr地址发起连接
* @sockfd: Socket对象描述符
* @addr: 连接目标的地址
* @addrlen: addr结构体的字节数
* 返回值: 成功返回0,失败返回-1
*/
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
/**
* 功能: 从Socket对象读接收干字节
* @sockfd: Socket对象描述符
* @buf: 接收数据的内存块首地址
* @len: buf的字节数
* @flags: 是否阻塞,写0即可
* 返回值: 成功接收到了多少个字节,失败返回-1
*/
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
/**
* 功能: 通过Socket对象发送若干字节
* @sockfd: Socket对象描述符
* @buf: 要发送的内存块首地址
* @len: 要发送的字节数
* @flags: 是否阻塞,写0即可
* 返回值: 成功发送了多少个字节,失败返回-1
*/
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
/**
* 功能: 关闭fd描述所代表的内核对象
*/
int close(int fd);
/**
* 功能: 把本地字节序的 unsigned short 类型的数据转换网络字节序
*/
uint16_t htons(uint16_t hostshort);
/**
* 功能: 把字符串格式 点分十进制的ip地址 转换成网络字节序的4字节ip地址
*/
in_addr_t inet_addr(const char *cp);
4、基于UDP协议的Socket编程模型
被接收者需要完成的任务(服务端):
1、创建socket内核对象,内核创建完成后会返回它的描述符
type:SOCK_DGRAM
2、准备本机地址(ip地址+端口号)
3、绑定(把本机地址与socket对象进行绑定)
for (;;) {
4、接收请求,同时接收发送者的地址
5、返回结果,按发送者的地址进行返回
}
6、关闭Socket对象
发送者需要完成的任务:
1、创建socket内核对象,内核创建完成后会返回它的描述符
type:SOCK_DGRAM
2、准备接收者的地址(ip地址+端口号)
for (;;) {
3、发送请求,根据接收者的地址发送数据
4、接收结果,并接收返回者的地址
}
5、关闭Socket对象
5、UDP通信需要使用Socket接口
/**
* 功能: UDP专用的数据发送函数。
* @dest_addr: 收件人的地址。
* @addrlen: 地址长度。
*/
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
/**
* 功能: UDP专用的数据接收函数。
* @src_addr: 发件人的地址,也是数据返回时的地址
* @addrlen: 地址长度,既是输入也是输出
*/
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
6、UDP的连接操作
UPD协议底层是否需要连接操作,客户端但可以在Socket层面进行连接,连接后的Socket对象在后续的通信过程中就不再需要通信地址了。
7、本地Socket通信
网络Socket通信是把网卡抽象成Socket文件配合TCP/IP协议簇,能够使当前进程与其它计算机的进程进行网络通信。
本地Socket通信是在文件系统中创建Socket
文件,能够使当前进程与本机的其它进程进行通信(IPC进程间通信)。
使用sockaddr_un
类型的通信地址,当调用socket对象与通信地址绑定时,会自动创建socket
文件。