内容 | 链接地址 |
---|---|
【一】深入理解网络通信基本原理和tcp/ip协议 | https://zhenghuisheng.blog.csdn.net/article/details/136359640 |
【二】深入理解Socket本质和BIO | https://zhenghuisheng.blog.csdn.net/article/details/136549478 |
深入理解socket本质和bio底层实现
一, Socket本质和初识BIO
在上一篇中,讲解了网络通信的基本原理,以及tcp/ip层与应用层之间的关系,可以得知在 OSI 七层模型中,数据需要先通过应用层将数据转成报文,然后将报文从应用层中传向传输层,封装成报文段,依次将数据封装到网络层,数据链路层,物理层,最后再通过以太网,光纤将数据传到到对应的主机上。
在网络编程中,由于tcp和ip已经有了对应的协议,因此在tcp层往下只需遵守对应的协议即可,因此在实际开发中,只需要将数据从应用层发送到传输层即可。
因此在操作系统的底层,封装了一个Socket,类似于一个中间件,用于应用层和传输层的TCP/IP协议族之间的通信,该中间层将所有与传输层连接的注意事项全部封装好,让开发者在开发无需关心底层的具体实现,更加的关注业务即可。如一些数据丢包的网络重传,滑动窗口等数据都会提前封装好。socket类似于sqlSession的功能,是一个门面模式,主要用于接收和转发,不做具体的执行功能。
在linux操作系统的源码中,会有一个 socket.c 的文件。在该文件中,里面已经封装了了应用层和tcp协议之间的细节,如如何建立连接,如何接受连接,如何监听,如何绑定等等都已经实现。因此对于网络应用程序来说,只需要与Socket进行交互即可。
1,Socket
客户端发送一条 hello word 到另一个客户端的流程如下,数据从客户端A的应用层再到传输层,再到网络层,再到数据链路层,再到物理层进行层层封包,通过以太网到客户端B的物理层,数据链路层,网络层,传输层,应用层进行层层解析,才能将数据进行解析出来
对于开发人员来说要实现层层的细节,肯定是不友好的。因此在操作系统底部,就为我们封装了一套socket,内部已经帮我们实现了tcp等协议的细节,让开发者更加的注重于业务上面的开发,其流程可以简化如下
让开发者只需考虑应用层的业务代码实现,不需要考虑底层的实现细节。因此在网络编程中只需要关注三件事,就是客户端和服务端的连接、读网络数据、写网络数据
2,BIO
2.1,单线程场景
在原生网络编程中,使用BIO编程的比较多,BIO指的是 Blocking IO 阻塞式io,顾名思义,就是在进行io时,会出现阻塞的情况。
先看一段原生通过BIO来实现网络编程的代码,来了解BIO的基本使用和被阻塞的时机,先看一段服务端的代码。改代码中创建一个serverSocket,用于实现应用层和tcp层之间的交互,随后绑定了一个端口8089,当有客户端来访问这个服务的这个端口时,就会做出响应
package com.zhs.netty.bio;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/\*\*
\* @author zhenghuisheng
\* @date 2024/3/7 22:31
\*/
public class BioServer {
public static void main(String[] args) throws IOException {
//创建一个socket
ServerSocket serverSocket = new ServerSocket();
//服务端监听的端口号
serverSocket.bind(new InetSocketAddress(8089));
System.out.println("服务端开始监听");
try{
while(true){
//监听事件
Socket socket = serverSocket.accept();
try{
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
//客户端传入的数据
String readData = input.readUTF();
System.out.println("成功接收到了数据" + readData);
output.writeUTF("已经接收到了" + readData);
}catch (Exception e){
e.printStackTrace();
}finally {
socket.close();
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
serverSocket.close();
}
}
}
在启动这个服务端的时候,可以看出有如下信息打印,表示此时正被阻塞着,并且阻塞在这个accept的监听上
服务端开始监听
随后再编写一个客户端的代码。服务端中需要使用ServerSocket,在客户端中则需要使用Socket
package com.zhs.netty.bio;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class BioClient {
public static void main(String[] args) throws IOException {
//客户端启动必备
Socket socket = null;
//实例化与服务端通信的输入输出流
ObjectOutputStream output = null;
ObjectInputStream input = null;
//服务器的通信地址
InetSocketAddress addr = new InetSocketAddress("127.0.0.1",8089);
try{
socket = new Socket();
socket.connect(addr);//连接服务器
System.out.println("连接成功");
output = new ObjectOutputStream(socket.getOutputStream());
input = new ObjectInputStream(socket.getInputStream());
System.out.println("Ready send message.....");
/\*向服务器输出请求\*/
output.writeUTF("zhenghuisheng");
output.flush();
//接收服务器的输出
System.out.println(input.readUTF());
}finally{
if (socket!=null) socket.close();
if (output!=null) output.close();
if (input!=null) input.close();
}
}
}
在启动完客户端之后,可以发现客户端打印的信息如下
连接成功
而在服务端中,由于接收到了客户端的请求,在服务端中也会将阻塞的代码继续往下执行
服务端开始监听
成功接收到了数据zhenghuisheng
除了服务端没有客户端来连接时会阻塞之外,在已经有一个客户端来连接且没释放,再来一个客户端进行连接时,此时的客户端也会出现阻塞的情况,假设在服务端刚开启之后,第一个客户端进行连接时在以下的代码处打一个debug断点阻塞在哪
output.writeUTF("zhenghuisheng");
此时第二个客户端来建立连接的代码如下,客户端这边不需要绑定具体的端口号,可以直接由操作系统进行分配即可,服务器的通信地址为刚刚设置的ip地址和端口号,目前设置的ip最地址为本地地址
package com.zhs.netty.bio;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class BioClient2 {
public static void main(String[] args) throws IOException {
//客户端启动必备
Socket socket = null;
//实例化与服务端通信的输入输出流
ObjectOutputStream output = null;
ObjectInputStream input = null;
//服务器的通信地址
InetSocketAddress addr = new InetSocketAddress("127.0.0.1",8089);
try{
socket = new Socket();
socket.connect(addr);//连接服务器
System.out.println("连接成功");
output = new ObjectOutputStream(socket.getOutputStream());
input = new ObjectInputStream(socket.getInputStream());
System.out.println("Ready send message.....");
/\*向服务器输出请求\*/
output.writeUTF("zhenghuisheng2号");
output.flush();
//接收服务器的输出
System.out.println(input.readUTF());
}finally{
if (socket!=null) socket.close();
if (output!=null) output.close();
if (input!=null) input.close();
}
}
}
此时客户端2打印的信息如下,就是处于连接成功的状态
连接成功
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
上面分享的腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
【Android高级架构视频学习资源】
**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-kTK2xH6Q-1712881041670)]