在套接字和地址文章中,我们画出基于套接字接口网络应用的一张图,本文章就是详细解释这些函数具体怎么实现的。
socket创建套接字
客户端和服务器使用socket函数来创建一个套接字描述符。
int socket(int domain, int type, int protocol)
clientfd = Socket(AF_INET,SOCK_STREAM , 0)
- domain 就是指 PF_INET、PF_INET6 以及 PF_LOCAL 等,表示什么样的套接字。
- type 可用的值是:
SOCK_STREAM: 表示的是字节流,对应 TCP;
SOCK_DGRAM: 表示的是数据报,对应 UDP;
SOCK_RAW: 表示的是原始套接字。 - protocol 原本是用来指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成。protocol 目前一般写成 0 即可。
socket返回的clientfd描述符仅是部分打开的,还不能用于读写,如何完成打开套接字的工作,取决于我们是客户端还是服务端。
connect连接
客户端通过调用 connect函数来建立和服务器的连接。
int connect(int clientid, const struct sockaddr *addr,socklen_t addrlen);
connect 函数会阻塞,一直到连接成功建立或是发生错误。
如果(x:y,addr.sin_addr:addr.sin_port) 成功,clientfd描述符现在就准备好可以读写了,并且得到的连接是由套接字对
刻画的,其中x表示客户端的IP地址,而y表示临时端口,它唯一地确定了客户端主机上的客户端进程。
bind将套接字和套接字地址绑定
绑定套接字意味着为套接字分配地址和端口号。
bind函数告诉内核将addr中服务器套接字地址和套接字描述符sockfd联系起来。
bind(int fd, sockaddr * addr, socklen_t len)
sockaddr * addr:虽然接收的是通用地址格式,实际上传入的参数可能是 IPv4、IPv6 或者本地套接字格式。
bind 函数会根据 len 字段判断传入的参数 addr 该怎么解析,len 字段表示的就是传入的地址长度,它是一个可变值。
listen监听
客户端是发起连接请求的主动实体。服务器是等待来自客户端的连接请求的被动实体,默认情况下,内核会认为 socket 函数创建的描述符对应干主动套接字(active sock
) 不是客户端使用的。
它存在于一个连接的客户端,服务器调用 1isten函数告诉内核,播述符是被服务器
int listen (int socketfd, int backlog)
backlog 参数的确切含义要求对TCP P协议的理解,这超出了我们讨论的范围。通常我们会把它设置为一个较大的值。比首1024。
accept
服务器通过调用 accept 函数来等待来自客户端的连接请求。
int accept(int listenfd, struct sockaddr *addr, int *addrlen);
accept 函数等待来自客户端的连接请求到达侦听描述符 listenfd,然后在addc中填写客户端的套接字地址,并返回一个已连接描述符(connected descriptor),这个描述可被用来利用UnixI/O函数与客户端通信。
监听描述符是作为客户端连接请求的一个端点。它通常被创建一次,并存在于服务器的整个生命周期。
已连接描述将是客户端和服务器之间已经建立起来了的连接的一个端点。服务器每次接受连接请求时会创建一次,它只存在于服务器为一个客户端服务的过程中。
这两者的区别,能够让服务器同时处理多个客户端连接。每次一个连接请求到达监听描述符时,我们可以派生(fork)一个新的进程,它通过已连接描述符与客户端通信。