之前,我们在网络中给大家谈到过一个概念:叫做协议分层。
- 应用层是和我们应用程序直接相关的。
- 传输层是端对端的一个传输,不关心中间过程,只关心起点和终点。
- 网络层是点到点传输规划传输中的中间路径。
- 数据链路层是负责两个相邻点之间的传输。
- 物理层是负责底层基础设施,硬件设备的。
我们的应用层 和传输层与我们编程的关系是最大的,应用层直接和我们的程序相关,传输层我们得去调用它里面的socket;而网络层中的内容都是在操作系统内核中调用和实现的,我们的数据链路层和物理层呢是由硬件和驱动程序实现的,他和我们的应用程序的开发没啥关系。
因此站在我们JAVA学习的角度,我们重点介绍的是应用层和传输层。那么下边的网络层,数据链路层我们只做简单介绍,而物理层我们就不介绍了。
应用层
我们先对应用层做一个泛泛的介绍,那么这里边有一个核心的协议就是HTTP协议,他是我们应用层最重要的协议,这个HTTP协议,我们放到后边去慢慢介绍。虽然在应用层中有很多大佬已经研究出来很多协议,但是呢在实际开发过程中呢,我们应用层还是需要我们自定义的,即需要自定义协议,那么不要看到协议,大家就被唬住了,其实协议呢?它就是通信双方的一个约定。
我们举一个这样的例子,假设呢我们开发了一个外卖软件,有一个功能:实现获取商家列表的功能,那么针对这样的功能,我们约定一下应用层协议。如何来约定呢?所谓的自定义协议,其实呢就是要我们做以下两件事,这两件事呢也是我们在写代码之前提前定义好的。
1.先根据我们的需求明确我们客户端和服务器之间需要传输哪些数据。
我们确定功能中客户端和服务端需要交互哪些的数据?那么这些需要交互的数据是由我们的功能需求是直接相关的。
比如呢我们有这样的一个需求:就是根据用户当前所处的位置获取方圆3km范围内的所有商家,并且呢按照距离升序排序,每一个商家的信息包括名称,图片,评分,距离等等,这样的话,我们就构成一个简化版本的需求。
请求里面的数据要求:
- 用户所在的位置(经纬度)
- 用户ID
响应里面的数据要求:
- 商家列表【每个商家的名称,图片,评分,距离】
2.确定数据组织的格式。
因为我们网络通信本质上是字节流或者是字符串。
那么我们得把上述的结构化的数据能够组织成字符串,同时还能把字符串解析为结构化的数据。
- 把结构化的数据能够组织成字符串,称之为序列化。
- 把字符串解析为结构化数据,称之为反序列化。
那么如何来组织数据格式往往是我们程序员重点考虑的
方案一) 直接使用纯文本的格式:基于简单的分隔符的定义方式。
请求:
- 用户ID,精度,纬度\n
响应:
- 商家商家名称,商家图片,商家评分,商家距离;商家商家名称,商家图片,商家评分,商家距离;商家商家名称,商家图片,商家评分,商家距离\n
缺点:
- 不能直观的看到每个属性是什么意思
- 未来如果要扩展或者说修改某一个属性都会很麻烦
方案二)基于xml格式来约定,这个是比较经典的格式,我们通过成对的标签描述某个值的含义。他有一个很好的优点呢,就是可读性比较好。
请求:
xml
<request>
<userid>1001</userid>
<jingdu>45</jingdu>
<weidu>67</weidu>
</request>
那么这样的格式,其实我们现在也很少使用,因为他也有缺点。
缺点:
- 非常消耗资源,比如网络带宽,也就是说你本来就传输几个内容,但是你引入一堆的标签很占资源啊。
- 格式上也不是很优雅
方案三)JSON,当前非常流行的数据组织格式。他不仅可读性好,而且消耗的带宽呢也比刚才谈到的xml更节省。
例如:
请求:
json
{
"userId":1001,
"jingdu":45,
"weidu":67
}
响应
json
[
{
"accountId":2001,
"name":"xxx",
"image":"xxxx",
"distance":"1.2km",
"rank":5
},
{
"accountId":2001,
"name":"xxx",
"image":"xxxx",
"distance":"1.2km",
"rank":5
}
]
缺点
- 还是存在冗余的信息,为了解决冗余信息,我们引入了方案四。
方案4)protobuf 这是谷歌提出的数据组织格式,它是纯二进制的数据格式,它不可读,我们无法使用肉眼解析。
它的可读性很差,但是呢传输效率是很高的。
- 如果我们对效率要求较高的场景,我们就用protobuf。
- 那如果对效率要求没那么高的话,我们更多考虑的是json
演示上述自定义协议的程序代码
约定好协议 :
1.我们先明确传递哪些数据?
请求:
- 运算符加减乘除
- 操作数只有两个:一个是操作数1(num1),另一个操作数2(num2)。
响应 - 运算结果。
2.组织数据的传输格式【我们使用的是自定义文本格式 】
比如:
请求: - 运算符,操作数1,操作数2,\n
响应:
- 结果\n
举个例子
请求:
- +,12,10\n
响应:
- 30\n
请求【结构化数据】
java
package application;
/**
* @ClassName Request
* @Description TODO 表示一个请求【结构化数据】
* @Author zhongge
* @Date 2026-02-02 16:20
* @Version 1.0
*/
public class Request {
//运算符 支持+-*/
private String operator;
//操作数
private double num1;
private double num2;
public Request(String operator, double num1, double num2) {
this.operator = operator;
this.num1 = num1;
this.num2 = num2;
}
//能够把这个对象转成字符串==》序列化
public String convertToString(){
//String.format类似于printf只是String.format把结果转到string 中了
return String.format("%s,%f,%f\n",operator,num1,num2);
}
//把字符串装换为对象==》方序列化
public static Request convertFromString(String request){
String[] split = request.split(",");
String operator = split[0];
double num1 = Double.parseDouble(split[1]);
double num2 = Double.parseDouble(split[2]);
return new Request(operator, num1, num2);
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public double getNum1() {
return num1;
}
public void setNum1(double num1) {
this.num1 = num1;
}
public double getNum2() {
return num2;
}
public void setNum2(double num2) {
this.num2 = num2;
}
}
响应【结构化数据】
java
package application;
/**
* @ClassName Response
* @Description TODO 响应结构化数据
* @Author zhongge
* @Date 2026-02-02 16:31
* @Version 1.0
*/
public class Response {
private double result;
public Response(double result) {
this.result = result;
}
//能够把这个对象转成字符串
public String convertToString(){
//String.format类似于printf只是String.format把结果转到string 中了
return String.format("%f\n",result);
}
//把字符串装换为对象
public static Response convertFromString(String response){
double result = Double.parseDouble(response);
return new Response(result);
}
public double getResult() {
return result;
}
public void setResult(double result) {
this.result = result;
}
}
服务器代码
java
package application;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName TcpCalcServer
* @Description TODO 进行算术运算的服务器
* @Author zhongge
* @Date 2026-02-02 16:16
* @Version 1.0
*/
public class TcpCalcServer {
private ServerSocket serverSocket = null;
public TcpCalcServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("启动服务器。。。");
ExecutorService service = Executors.newCachedThreadPool();
while (true){
//1.获取请求
Socket socket = serverSocket.accept();
service.submit(() -> {
processConnection(socket);
});
}
}
private void processConnection(Socket socket) {
//注意我们一个连接中可能会涉及多组请求和响应的交互。
// 也就是说你打一次电话,可能说的话是很多很多,做的事情都很多。
System.out.printf("[%s:%d] 客⼾端上线!\n", socket.getInetAddress().toString(),socket.getPort());
try(InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()
){
//读取一个请求方法二使用Scanner
Scanner scanner = new Scanner(inputStream);
//一次循环呢,处理一组请求和响应。
while (true){
if (!scanner.hasNext()){
//判断当前tcp的连接是否断开了,如果断开的话,那么就没有必要再读取了
break;
}
String requestString =scanner.next();
//反序列化【字符串变为结构化数据】
Request request = Request.convertFromString(requestString);
//2.根据请求来计算响应
Response response = process(request);
//3.把响应写回到客户端
//序列化
String responseString = response.convertToString();
//前面序列化String.format("%f\n",result); 已经有 \n 了 所以这里就不需要了
outputStream.write(responseString.getBytes());//转为字节流写过去
// ⽇志, 打印当前的请求详情.
System.out.printf("[%s:%d] req: %s, resp: %s\n", socket.getInetAddress().toString(),socket.getPort(),
requestString, responseString);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try {
System.out.printf("[%s:%d] 客⼾端下线!\n", socket.getInetAddress().toString(),socket.getPort());
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private Response process(Request request) {
if (request.getOperator().equals("+")) {
return new Response(request.getNum1() + request.getNum2());
} else if (request.getOperator().equals("-")) {
return new Response(request.getNum1() - request.getNum2());
}else if (request.getOperator().equals("*")) {
return new Response(request.getNum1() * request.getNum2());
}else if (request.getOperator().equals("/")) {
return new Response(request.getNum1() / request.getNum2());
}else {
throw new RuntimeException("不支持的运算符:" + request.getOperator());
}
}
public static void main(String[] args) throws IOException {
TcpCalcServer tcpCalcServer = new TcpCalcServer(9090);
tcpCalcServer.start();
}
}
客户端代码
java
package application;
import net.tcp.TcpEchoClient;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* @ClassName TcpCalcClient
* @Description TODO 进行算数运算的客户端
* @Author zhongge
* @Date 2026-02-02 16:16
* @Version 1.0
*/
public class TcpCalcClient {
private Socket socket = null;
public TcpCalcClient(String serverIp,int ServerPort) throws IOException {
socket = new Socket(serverIp, ServerPort);
}
public void start() {
System.out.println("客户端启动");
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()
) {
Scanner scanner = new Scanner(System.in);
Scanner scannerNetWork = new Scanner(inputStream);
while (true) {
//1.从控制台读取用户输入
System.out.print("请输入你要进行的运算(+ - * /):-> ");
String operator = scanner.next();
System.out.print("请输入操作数1:-> ");
double num1 = scanner.nextDouble();
System.out.print("请输入操作数2:-> ");
double num2 = scanner.nextDouble();
//2.构造请求发送给服务器
Request request = new Request(operator, num1, num2);
//序列化
String requestString = request.convertToString();
// request += "\n"; //由于加了\n了所以就不用再+\n了
outputStream.write(requestString.getBytes());
//3.从服务器读取响应
if (!scannerNetWork.hasNext()) {
break;
}
String responseString = scannerNetWork.next();
Response response = Response.convertFromString(responseString);
//4.把响应显示到控制台
System.out.println(response.convertToString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpCalcClient tcpCalcClient = new TcpCalcClient("127.0.0.1",9090);
tcpCalcClient.start();
}
}
服务端结果

客户端结果

传输层
传输层我们说过最核心的协议是TCP和UDP,也是和Java网络编程关联最紧密的一层------我们写代码时调用的Socket,本质就是对传输层协议的封装,不用自己关心底层传输细节,只需要调用API就能实现端到端的通信,这一层只关心数据从哪个应用程序发出去,最终到哪个应用程序收,中间经过多少路由器、交换机都不管。
再谈端口号
我们说端口号是用整数表示,用来区分同一台主机上不同的应用程序。
我们前面在网络编程中每个程序的Socket创建的时候都需要关联端口号,那么对于服务器来说,端口号是程序员手动指定的;而对于我们的客户端来说,端口号是系统自动分配的。
端口号是由两个字节表示的无符号整数
范围:0~65535。
虽然它的范围比较多,但是并不是所有的数都可以使用的。
- 0~1023 这样的范围通常我们是不使用的,他们叫做知名端口号,是给一些知名的服务器预留的。
虽然现在知名的服务器协议对应的端口没有太多,但有两个知名的端口,一定要重点认识。
- 80 ==> 这个是给HTTP服务器留的端口号。
- 443 ==》 这个是给HTTPS服务器留的端口。
问题1 :一个进程是否可以绑定多个端口号?
答:这个是完全可以的,但是注意其实不是进程绑定端口号,而是我们的Socket绑定端口,我们一个进程中完全可以创建多个Socket,所以可以同时关联到多个端口号,这个是一个非常常见的情况。
举个例子:我们在开发服务器程序的时候,一般会提供至少两个端口:
- 业务端口: 供用户的客户端连接使用,处理正常的业务请求;
- 调试端口/管理端口:这个是不对外公开的,我们开发和运维人员自己使用,生产环境下服务器程序不能直接用调试器调试,可能会把整个进程阻塞住,导致用户无法访问,就可以通过这个端口做远程管理。
问题2 :一个端口号是否可以被多个进程绑定?
答:如果是同一种协议则不行,如果是不同协议是可以的 。
比如UDP的8080端口和TCP的8080端口,能被两个不同的进程分别绑定,系统会通过协议类型区分,不会混淆;但如果是两个进程都想绑定TCP的8080端口,就会报错,端口被占用。
核心协议:TCP 和 UDP
传输层就这两个核心协议,所有基于网络的应用程序,最终都会选择其中一个来传输数据,Java Socket编程也分TCP Socket和UDP Socket,就是对这两个协议的直接使用。
我们用通俗的例子来理解,把数据传输比作寄快递:
UDP 协议:快递里的「平邮普通件」
UDP的全称是用户数据报协议,核心特点就是无连接、不可靠、面向数据报、传输速度快。
- 无连接:寄件前不用和收件人确认"你是否能收",直接把包裹扔到快递站就行,不用建立任何连接;
- 不可靠:快递站只负责发,不保证包裹一定能到收件人手里,也不保证包裹的顺序,可能丢件、可能少件、可能先发的后到;
- 面向数据报:包裹有固定的大小限制,一次只能发一个完整的包裹,不能拆分,也不能拼接,发的时候是一个整体,收的时候也必须一次性收一个整体。
UDP协议不会做任何额外的校验、重传操作,所以开销小、速度快,适合对速度要求高,对数据可靠性要求低的场景。
常见使用场景 :直播、短视频、语音通话、网络游戏、广播消息(比如局域网设备发现)。
Java中使用DatagramSocket、DatagramPacket就是基于UDP协议开发。
TCP 协议:快递里的「顺丰保价件」
TCP的全称是传输控制协议,核心特点是面向连接、可靠、面向字节流、传输速度稍慢。
- 面向连接:寄件前必须先和收件人通电话,确认"你在家、能收快递",双方建立好连接后,再开始寄件,对应TCP的「三次握手」;寄完件后,还要确认"所有包裹都收到了",再断开连接,对应TCP的「四次挥手」;
- 可靠:快递站会全程跟踪包裹,丢件了会重新发,包裹顺序乱了会重新整理,保证收件人收到的包裹和寄件人发的完全一致,而且会确认"你已经收到了"(对应TCP的确认应答、重传机制、拥塞控制);
- 面向字节流:没有包裹大小限制,想发多少发多少,数据会被拆成一个个小数据包传输,到了对方手里再拼接成完整的字节流,就像水管流水一样,连续不断。
TCP协议通过各种机制保证了数据的可靠传输,但是会做很多额外的操作,开销比UDP大,速度稍慢,适合对数据可靠性要求高的场景。
常见使用场景 :网页访问(HTTP/HTTPS)、文件传输、聊天软件、电商支付、数据库连接。
Java中使用ServerSocket、Socket就是基于TCP协议开发,也是我们Java网络编程中最常用的方式。
一句话总结
UDP:只管发,不管到,速度快;TCP:先连好,再发货,保送到。
实际开发中,选哪个协议完全看业务需求:比如直播不能卡,偶尔掉几个帧没关系,用UDP;比如转账付款,数据不能丢、不能错,必须用TCP。
网络层
网络层是负责点到点传输、规划路径 的一层,简单说就是决定数据从A主机到B主机,要走哪条路,经过哪些路由器,这一层只关心「主机与主机之间」的通信,不关心主机上的哪个应用程序,识别主机的唯一标识就是IP地址,这也是网络层的核心标识。
站在Java开发的角度,我们只需要知道IP地址的作用,不用关心网络层的具体实现------因为这一层的所有操作,都是在操作系统内核中完成的,我们写代码时只需要指定目标主机的IP地址,系统会自动完成路径规划、数据转发,不用我们手动干预。
核心概念:IP地址
IP地址是用来在网络中唯一标识一台主机的,就像每个房子都有唯一的门牌号,网络中的每台设备(电脑、手机、路由器)都有唯一的IP地址,网络层通过IP地址,知道数据要从哪个主机发,到哪个主机收。
目前常用的有两个版本的IP地址:IPv4和IPv6:
- IPv4:32位的二进制数,通常拆成4个8位的十进制数,用点分隔,比如127.0.0.1(本地回环地址)、192.168.1.1(路由器常用地址),范围是0.0.0.0~255.255.255.255,可用的地址数量有限,现在已经不够用了;
- IPv6:128位的二进制数,通常拆成8个16位的十六进制数,用冒号分隔,比如::1(对应IPv4的127.0.0.1),地址数量极大,能满足未来所有设备的联网需求,是未来的主流。
注意:IP地址标识的是「主机的网络接口」,不是主机本身------一台主机如果连了多个网络(比如电脑连了有线网和无线网),就会有多个IP地址。
核心协议:IP协议、ICMP协议、ARP协议
网络层的核心协议以IP协议为核心,其他协议都是为IP协议服务的,解决IP协议的各种问题,我们还是用通俗易懂的方式介绍,不用深究细节:
1. IP协议:网络层的「核心总指挥」
IP协议的全称是网际协议,是网络层最核心的协议,作用就是定义IP地址的格式、规定数据如何在主机之间转发、规划传输路径 。
简单说,IP协议就是给数据打上「寄件人IP(源IP)」和「收件人IP(目标IP)」的标签,然后根据网络情况,选择一条合适的路径,把数据从源主机转发到目标主机。
但IP协议和UDP协议类似,是不可靠的,它只负责转发数据,不保证数据一定能到达,也不保证顺序,丢包、乱序的问题,会交给上层的TCP协议去解决(TCP会做重传、排序)。
2. ICMP协议:网络层的「快递物流查询+故障反馈」
ICMP协议的全称是互联网控制报文协议,是IP协议的"辅助小弟",作用就是反馈网络传输中的错误信息、检测网络连通性 。
我们平时在电脑上用的ping命令,就是基于ICMP协议实现的------ping某个IP地址,就是向目标主机发送ICMP请求包,目标主机收到后返回ICMP响应包,通过这个过程就能知道"目标主机是否可达""网络延迟多少""是否丢包"。
比如你ping 192.168.1.1不通,就说明你的电脑和路由器之间的网络有问题,ICMP协议会返回具体的错误信息,比如"请求超时""目标主机不可达"。
3. ARP协议:网络层的「门牌号转身份证号」
ARP协议的全称是地址解析协议,作用是将IP地址转换为MAC地址(MAC地址是数据链路层的标识,是设备网卡的唯一物理地址,烧录在网卡上,全球唯一)。
为什么需要这个转换?因为网络层用IP地址找主机,但实际在局域网内传输数据时,底层的硬件设备(交换机)不认识IP地址,只认识MAC地址,就像你知道朋友的门牌号(IP),但快递员需要知道朋友的身份证号(MAC)才能准确送件。
ARP协议会在局域网内广播查询"哪个设备的IP是XXX,你的MAC地址是多少?",对应IP的设备会回复自己的MAC地址,这样就完成了IP到MAC的转换,后续数据就能在局域网内准确传输了。
网络层的核心工作
简单总结网络层的工作:给传输层传过来的数据打上源IP、目标IP标签,通过ARP协议把IP转成MAC地址,然后通过ICMP协议检测网络,规划出一条从源主机到目标主机的最优路径,把数据转发给下一个路由器,直到数据到达目标主机所在的局域网。
数据链路层
数据链路层是负责两个相邻设备之间的传输,简单说就是「点对点的直接通信」,比如你的电脑和路由器之间、路由器和路由器之间,都是相邻的设备,数据链路层就负责把网络层传过来的数据,在这些相邻设备之间可靠地传输,这一层不关心整体的路径,只关心"我身边的这个邻居,能不能收到我发的数据"。
这一层由硬件(网卡、交换机)和驱动程序实现,和Java应用开发完全无关,我们只做泛泛介绍,知道核心作用和核心协议就行。
核心标识:MAC地址
MAC地址是数据链路层的唯一标识,全称是媒体访问控制地址,也叫物理地址,是烧录在网卡上的一串唯一的二进制数,全球唯一,不会重复,长度是48位,通常用12位的十六进制数表示,中间用冒号或横线分隔,比如00:11:22:33:44:55。
MAC地址的特点是面向硬件,不管设备连到哪个网络,MAC地址都不会变,就像人的身份证号,终身不变;而IP地址是面向网络的,设备连到不同的网络,IP地址会变,就像人的门牌号,换房子就会变。
数据链路层就是通过MAC地址,识别相邻的设备,保证数据在两个相邻设备之间准确传输。
核心协议:以太网协议、CSMA/CD协议、PPP协议
数据链路层的协议和底层硬件关联紧密,核心是以太网协议,因为现在我们的有线网络、局域网,基本都是基于以太网技术的,其他协议都是为了解决以太网的使用问题,或者适配其他网络场景。
1. 以太网协议:局域网数据传输的「通用规则」
以太网协议是目前最主流的数据链路层协议,定义了数据链路层数据的传输格式、MAC地址的使用规则、数据在局域网内的传输方式 。
简单说,以太网协议就是给网络层传过来的数据,再打上源MAC地址、目标MAC地址的标签,然后封装成「以太网帧」(数据链路层的传输单位),在局域网内传输,相邻设备通过MAC地址识别,只接收目标MAC是自己的帧,其他帧直接丢弃。
以太网协议还规定了数据的传输介质,比如双绞线、光纤,还有传输的速率,比如百兆以太网、千兆以太网、万兆以太网。
2. CSMA/CD协议:以太网的「先听后说,冲突避让」规则
CSMA/CD协议的全称是载波监听多路访问/冲突检测,是以太网协议的「访问控制规则」,解决的是局域网内多个设备同时发数据,导致的信号冲突问题。
用通俗的话讲,就是局域网内的所有设备,共用一条传输线路,就像多人用同一个话筒说话,要是同时说,就会听不清,CSMA/CD协议就是定了一个规矩:
- 先听后说:设备要发数据前,先监听线路上有没有其他设备在发数据,如果有,就等一会儿,不发;
- 边说边听:发数据的同时,继续监听线路,检测是否有其他设备也在发数据(冲突);
- 冲突避让:如果检测到冲突,立刻停止发数据,发送一个冲突信号,告诉其他设备"这里冲突了",然后随机等一段时间,再重新尝试发数据。
这个规则保证了局域网内的设备,能有序地使用传输线路,避免数据冲突。
3. PPP协议:广域网的「点对点通信规则」
PPP协议的全称是点对点协议,和以太网协议的使用场景不同,以太网协议用于局域网,而PPP协议用于广域网的点对点连接,比如我们早期的宽带拨号、光纤入户的光猫和运营商机房之间的连接,都是用PPP协议。
PPP协议解决的是两个远程设备之间的点对点通信问题,定义了数据的封装格式、身份验证方式(比如宽带的账号密码验证),保证广域网中两个相邻设备之间的数据可靠传输。
数据链路层的核心工作
简单总结数据链路层的工作:接收网络层传过来的数据包,打上源MAC、目标MAC标签,封装成以太网帧,按照CSMA/CD(局域网)或PPP(广域网)的规则,在两个相邻设备之间传输,同时做简单的校验,保证数据在相邻设备之间不丢包、不错包,传输完成后,再把MAC标签去掉,把数据包交给上层的网络层。
物理层
按照我们Java学习的视角,物理层和应用程序开发完全无关,它是整个网络协议栈的最底层,负责底层硬件基础设施的物理传输,简单说就是把数据链路层的二进制数据,转换成电信号、光信号、无线电信号,通过物理介质(双绞线、光纤、无线电波)传输,再把接收到的信号转换回二进制数据,交给数据链路层。
物理层不关心数据的含义,也不关心任何协议,只关心「信号的传输」,比如电压的高低、光的亮灭、无线电波的频率,对应的硬件设备有网线、光纤、网卡、交换机、路由器的物理端口等,我们只需要知道有这一层,是整个网络的物理基础就行,无需深入了解。
总结
整个网络的数据传输,就是从上到下逐层封装(加标签:应用层数据→加端口→加IP→加MAC→转物理信号),到了目标主机后,再从下到上逐层解封装(去标签:物理信号→解MAC→解IP→解端口→应用层数据),最终到达目标应用程序,这就是协议分层的核心意义------每层做好自己的事,互不干扰,简化开发和维护。
那么最后到这儿呢,我们就广泛的介绍了各个层次的一个情况。对于我们JAVA学习者来说呢,我们后续博客我们会详细的介绍应用层的HTTP和HTTPS协议,以及我们传输层的TCP和UDP的协议,那么对于网络层和数据链路层我们只做了解,物理层我们可以直接不管,感谢各位老铁,我们下期见。