客户端-服务器模型
互联网已经深入我们生活的方方面面,重要性不言而喻。上世纪70年代互联网刚发明的时候,就确定了客户端-服务器模型,这个编程模型一直沿用到现在都没有变过,我们在电脑上打开一个网站,在微信上聊天,打开一个app本质上都是客户端和服务器的连接和交互。
客户端-服务器模型的操作方式如下:
(图片来源:极客时间)
- 客户端需要一个服务的时候,比如需要打开一个网页,它就向服务器发送一个请求。
- 服务器收到请求后,会进行适当的处理,比如从数据库读取客户端需要的数据。
- 服务器处理好请求后,会按一定的格式返回客户端需要的数据,然后发给客户端一个响应。
- 客户端收到服务器的响应后,会处理网页数据,然后再显示器上显示出来,然后客户端再发送请求给服务器,如此循环。
socket 套接字
客户端和服务器是通过什么来连接的呢?是20 世纪 80 年代加州大学伯克利分校提出来的socket套接字,也叫伯克利套接字。socket的工作模式如下图
(图片来源:极客时间)
服务器socket准备工作:
- 初始化socket
- 执行bind绑定操作,将服务器的服务绑定在一个ip地址和一个特定的端口上
- 执行listen操作,将原先的socket转化为服务端的socket
- 执行accept操作,将进程阻塞在accept方法上,等待客户端的连接
客户端初始化一个socket后,可以直接调用connet方法连接服务端的socket,经过著名的TCP三次握手,客户端和服务器建立连接,进入数据传输状态。
客户端发起write写操作,服务器通过read接收数据,然后write到客户端,客户端用read接收数据,当客户端和服务器交互完成之后,客户端发起close操作,发送一个FIN包通知服务器关闭链接。
socket 套接字地址格式
客户端和服务器建立连接的时候,需要知道对方的地址。就像快递小哥要给你送快递就得知道你家住那栋楼,哪个房间一样。互联网世界的ip地址和端口就像哪栋楼几零几一样,能够精确的找到服务器上的某个服务。
(图片来源:极客时间)
我们使用socket的时候就需要对方的ip地址和端口号,socket的地址结构在程序里的表示如下面的图示。
sin_family表示地址的类型,IPv4的值就是AF_INET,IPv6的值就是AF_INET6,程序通过sin_family的类型就可以知道该套接字地址是哪种类型了。
in_port_t表示端口号,我们看到端口号占16位,所以端口号的数量最大支持2的16次方,就是65536个端口,也就是0~65535的范围。因为每次建立连接的时候都需要知道对方的端口号,很多知名的服务就把自己的端口号固定了,叫做保留端口。这样客户端要连接这些服务的时候就知道用哪个端口了,不用每次再去问了,比如:ftp的21端口,ssh的22端口,telnet的23端口,http的80端口。
通用套接字地址格式
(图片来源:极客时间)
IPv4 套接字格式地址
(图片来源:极客时间)
IPv6 套接字地址格式
(图片来源:极客时间)
本地套接字地址格式
(图片来源:极客时间)
几种套接字地址格式比较
(图片来源:极客时间)
通用套接字地址
IPv4套接字格式地址表示IPv4的地址,IPv6套接字格式地址表示IPv6的地址,还有本地地址结构用于本地 socket 通信,还有一个是通用地址结构,为什么要有通用地址结构呢?
原来为了方便函数的的调用,像connect,bind,accept方法都需要一个套接字地址结构的指针参数:
为了方便起见,函数的定义就直接用通用的地址结构指针定义,然后调用的时候再传入具体的地址结构指针,有没有和面向对象编程的多态特性很像?
但是ipv4地址结构的大小为16字节,ipv6地址结构的大小为28字节,本地地址地址结构的大小最多为110字节,而通用地址结构的大小为16字节。除了ipv4地址,其他地址都比通用地址结构大,那么其他地址是如果转换为通用地址的呢?
仔细看一下,这里参数传入的是地址结构的指针哦,后面还有一个*addrlen的参数,表示地址结构的长度,所以只要取addrlen长度的数据就行了,具体的类型可以根据sin_family来确定哦。
总结一下
客户端和服务器只能通过socket套接字连接和传输数据,套接字地址结构的类型通过sin_family来确定,connect,bind,accept方法中的套接字地址参数是通用的套接字地址。