在之前的认识TCP/IP协议中了解了OSI七层网络模型和TCP/IP四层模型,和TCP/IP中通信的三次握手、四次挥手,还用Socket写过聊天室的通信,但直到昨天才发现自己对Socket的通信原理的理解是模糊的,所以打算再梳理一下。
OSI七层网络模型和TCP/IP四层模型
先来看下OSI七层网络模型和TCP/IP四层模型有何区别,其实本质上他们是一样的,都是对一个网络通信过程的分层模型,只是分层时候侧重点有所不同。
我们知道TCP/IP协议是互联网协议(簇)的统称,对网络通信制定了一系列相应的规则,是通信的基础,它提供点对点的链接机制,将数据应该如何封装、定址、传输、路由以及在目的地如何接收,都加以标准化。而OSI模型是开放式系统互联通信参考模型。因为OSI是是先有模型后有进行实践,所以它是一个完整的宏观模型,包括了硬件层(物理层),当然也包含了很多协议(比如DNS解析协议等),而TCP/IP则相反,先有协议和应用再提出了模型,且是参照的OSI模型对其分层的。TCP/IP更加侧重的是互联网通信核心(也是就是围绕TCP/IP协议展开的一系列通信协议)的分层。
二者最大的不同在于OSI是一个理论上的网络通信模型,而TCP/IP则是实际运行的网络协议。
Socket是什么?
说了那么多,开始进入正题吧。下面是TCP/IP的四层模型,但为什么我们没有看到Socekt呢?它应该把放在哪一层?
socket是在应用层和传输层之间的一个抽象层,socket本质是编程接口(API),它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。TCP/IP只是一个协议栈,必须要具体实现,同时还要提供对外的操作接口(API),这就是Socket接口。通过Socket,我们才能使用TCP/IP协议。
JDK的java.net包下有两个类:Socket和ServerSocket,在Client和Server建立连接成功后,两端都会产生一个Socket实例,操作这个实例,完成所需的会话,而我们就通过这些API进行网络编程,不需要去关心底层的实现了。 Socket连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
Socket的工作原理
我们只是会用Socekt进行通信的编程了,但socket通信流程究竟是什么样的呢?废话不扯,还是直接上张图来理解吧。
socket是"打开—读/写—关闭"模式的实现。
先来看看客户端和服务器端的实现吧,来分析一下具体步骤!
客户端:
服务器端:
1.服务器端先初始化Socket。( listenfd 从名称看就是为了要监听而创建的socket描述符)
那bind 是干嘛?是为了声明说我要占用这个端口了, 你们都别用了。所以2.绑定端口(bind)
接着 3.listen函数才是真正开始对端口监听了。
接下来是个死循环啊,啊啊也对,因为服务器端需要一直提供服务,只能坐以待命。那这个accept是干啥的呢?
4.调用accept阻塞,等待客户端来连接我。
为什么使用了listenfd , 然后返回了一个新的connfd ? 你还记得服务器要应付很多的客户端发起的连接, 所以它一定得把各个客户端区分开,怎么区分呢? 那只有用一个新的socket来表示, 可以看到后面接受/发送(写和读)消息的操作都是基于connfd 来做的。 至于之前的listenfd , 它只起到一个大门的作用了, 意思是说,欢迎敲门, 进门之后我将为你生成一个独一无二的socket描述符!(引子张大胖的Socekt,o((⊙﹏⊙))o.)
这时有个客户端初始化一个Socket,然后5.该客户端连接服务器(connect),连接成功则建立连接。此时服务器的accept 相当于和客户端的connect 一起完成了TCP的三次握手 !
连接建立以后6.客户端发送发送数据请求 7.服务器接收请求并处理,然后回应数据给客户端 8.客户端读取到的数据,最后关闭连接。 这样一次完整的交互就结束了。
还有一个问题就是socket指的是 (IP, Port), 现在我已经有了一个listenfd 的socket, 端口是80 然后每次客户端发起连接还要创建新的connfd, 因为80端口已经被占用,难道服务器端会为每个连接都创建新的端口吗?
其实新创建的connfd 并没有使用新的端口号,也是用的80, (在实现聊天室的时候,我们只是为每一个客户端的连接单独创建一个线程去处理,但并没有为每个连接都创建新的端口。然而这样处理是有漏洞的,昨晚就被问到了(′д` )…彡…彡,然后我想了下也是,如果客户端很多,那这样做服务器不得崩了才怪,那怎么解决了?我答了下用线程池吧...又扯了)
因为可以这么理解,这个socket描述符指向一个数据结构, 例如 listenfd 指向的结构是这样的:
而一旦accept 新的连接, 新的connfd 就会生成, 像下面的表格, 就生成了两个connfd , 它们俩服务器端的ip和port都是相同的, 但是客户端的IP和Port是不同的, 自然就可以区分开来了。
所以socket 得通过五元组(协议, 客户端IP, 客户端Port, 服务器端IP, 服务器端Port)来确定。
‘我们只是简单的使用Socket与ServerSocket就完事了,那是因为底层为我们做了这么多的工作封装好让我们站在巨人肩上编程的。
昨日感受:仰望星空脚踏实地。站在巨人肩上可以看得更广更远&基础很重要 “会用与明白如何实现这是专业与非专业的区别” 。