文章目录
一、概念
- 网络之间通过路由器连接,这就构成一个覆盖范围更大的计算机网络,这就称之为互联网。
- 网络把许多计算机连接在一起。
互联网把网络通过路由器连接在一起。
与网络相连的计算机称之为主机(通过IP地址标识),网络互连并不是把计算机简单地在物理层面连接起来,因为这样计算机不能够进行交互,所以我们还必须在计算机上安装能够交互的软件(port端口)才行。
计算机网络的分类
- 按照网络的作用范围分类
1)广域网(WAN):长距离运送数据
2)域域网(MAN):一个城市或跨越街区城市
3)局域网(LAN)
4)个人区域网(PAN):个人电子设备,使用无线技术连接起来的网络(例:手机热点) - 按照网络的使用者分类
1)公有网
2)私有网
计算机网络中性能指标
- 速率:数据传输速率
- 带宽:表示网络中某通道传输数据的能力,单位时间内网络信道所能通过的最高
- 吞吐量:单位时间内某个网络实际的数据量
- 时延
1)发送时延
2)传播时延
3)处理时延
4)排队时延 - 往返时间 RTP
网络体系结构
计算机网络各层协议的集合就是网络的体系结构,下面是几种常用协议:
- OSI 七层协议体系结构
各层协议数据单元:
OSI七层协议 | 数据单元 |
---|---|
7 应用层 | 报文 |
6 表示层 | |
5 会话层 | |
4 传输层 | 报文段 / 用户数据报 |
3 网络层 | 数据报 |
2 数据链路层 | 帧 |
1 物理层 | 比特 |
- TCP/IP 体系结构(4层)
应用层 传输层 网际层(网络层) 网络接口层 - 五层协议 的体系结构
应用层 传输层 网络层 数据链路层 物理层
1)应用层
应用进程之间的交互,这里的进程指的是在主机中正在运行的程序。应用层交互的数据单元称为报文。
2)运输层
向两台主机中进程之间的通信提供一个数据传输服务。
运输层起着承上启下的作用,涉及源端节点到目的端节点之间可靠信息的传输。传输层需要解决网络连接的建立和释放(涉及TCP协议/UDP协议)
3)网络层
网络层负责分组交换网上的不同主机提供通信扶额u,在发送数据时,网络层会将传输层产生的报文端或用户的数据报封装成分组或者包进行传输。网络层传输的是数据报。
4)数据链路层
两台主机之间的数据传输,总是在链路上传输,所以需要链路层。链路层传输的数据称之为帧。
5)物理层
物理层所传输的单位是比特,发送方发送0或1,接收方接收0或1。
- 数据在往下一层传输时,需要加上下一层数据单元所需要的控制信息。
- 最常用的体系结构:TCP/IP 体系结构,TCP/IP 体系结构
五层协议举例:
- 主机1 往 主机2发送数据。(实际上是指主机1的应用程序往主机2的应用程序发送数据)
- 主要过程如下图:
二、TCP协议 / UPD协议
TCP协议
- 概念:
TCP协议是数据传输控制协议,是面向连接的传输层协议,采用字节流机制传输数据。 - 特点:
(1)提供可靠交付服务,通过TCP连接传输的数据,无差错、不丢失、不重复;
(2)提供全双工通信
(3)面向字节流,点对点通信 - 网络通信 两要素
主机 IP
端口 port
TCP编程
在实现TCP编程之前,首先要了解Java Socket。
- socket称之为“套接字”,用于描述IP地址和端口号,是一个通信的句柄。应用程序通过套接字向网络中发送请求或者应答请求(客户端 <-> 服务器端)
客户端 Socket
服务器端 ServerSocket
这两者都存在于java.net
客户端(Socket常用于API)
- Accept常用于产生“阻塞”,直到接收一个连接,并且返回一个客户端的Socket对象实例。服务器端使用,如果该方法有返回值,说明此时有一个客户端请求当前服务器端,接下来的操作都需要使用这样的一个返回值。
- 简单来说:客户端有连接,accpet()返回一个Socket;客户端没有连接,就一直处于阻塞状态。
getInputStream 获取网络连接输入
getOutStream 获取网络连接的输出
服务器端
- 实现服务器端需要构造ServerSocket,下面是会用到的方法
ServerSocket()
ServerSocket(int port)
ServerSocket(int port,int backlog)
ServerSocket(int port,int backlog,InetAddress bindAddr)
服务器端如何应对多个客户端访问?
- backlog 一个线程处理多个请求(backlog参数用来设置连接请求队列的长度)
- 多线程
实现逻辑(下图):
TCP编程流程图:
服务器端代码实现:
//子线程
class SocketHandler extends Thread{
private Socket socket;
public SocketHandler(Socket socket){
this.socket = socket;
}
@Override
public void run() {
BufferedReader reader = null;
OutputStream outputStream = null;
//进行通信
try {
while (true) {
//进行读操作
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = reader.readLine();
System.out.println("客户端:"+socket.getRemoteSocketAddress()+"发送信息:"+msg);
//进行写操作
outputStream = socket.getOutputStream();
outputStream.write(("reponse:"+msg+"\n").getBytes());
if("".equals(msg) || "exit".equals(msg)){
System.out.println("lvting-Debug");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
reader.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//主线程
public class MyTcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(3333);
System.out.println("服务器端已经启动...");
while (true){
//进行监听等待客户端连接
Socket accept = serverSocket.accept();
System.out.println("与客户端:"+accept.getRemoteSocketAddress()+"连接建立成功...");
//将accept实例交给子线程处理客户端请求
new SocketHandler(accept).start();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
serverSocket.close();
}
}
}
客户端代码:
public class MyTcpClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader input = null;
BufferedReader reader = null;
PrintWriter output = null;
try {
//构造Socket
socket = new Socket("127.0.0.1", 3333);
System.out.println("客户端已经启动...");
while (true){
//发送数据到服务器端
//获取控制台的输入流
input = new BufferedReader(new InputStreamReader(System.in));
String info = input.readLine();
//获取连接对象的输出流
output = new PrintWriter(socket.getOutputStream());
System.out.println("debug-info:"+info);
//输出流对象写入info数据
output.println(info);//tcp存在缓冲区,有可能缓冲区没有刷新就导致数据无法传输
output.flush();
//输出服务器端返回的数据
//获取连接对象的输入流
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String strs = reader.readLine();
System.out.println("客户端显示--服务器端响应的数据:"+strs);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
socket.close();
reader.close();
input.close();
output.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- TCP协议是传输层向上面的应用层提供通信服务的协议。提供面向连接的服务,再传输数据之前必须要建立连接,数据传输之后还需要释放连接,TCP产生的额外开销会比较大。
保证可靠传输(安全):确认机制、流量控制、拥塞控制机制等也需要占用处理器资源。
TCP建立连接的过程(三次握手)
- 为了准确无误地将数据送达目标处, TCP 协议采用了三次握手(three-way handshaking) 策略。 用 TCP 协议把数据包送出去后, TCP不会对传送后的情况置之不理, 它一定会向对方确认是否成功送达。
- 握手过程中使用了 TCP 的标志(flag) —— SYN(synchronize) 和 ACK(acknowledgement) 。
① 发送端首先发送一个带 SYN 标志的数据包给对方。
② 接收端收到后,回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。
③ 最后, 发送端再回传一个带 ACK 标志的数据包, 代表“握手”结束。- 若在握手过程中某个阶段莫名中断, TCP 协议会再次以相同的顺序发送相同的数据包。
- 第一次握手
client发送连接请求数据报文到server端。client进入SYN_SENT状态; - 第二次握手
server收到连接请求数据报文,如果同意建立连接,向client回应确认报文段(为TCP连接分配TCP缓存和变量); - client收到server确认之后,它再次向服务器端发送确认数据报文段;(第三次握手)
seq表示当前传输的字节流第一个字节的编号
下面是更容易理解的版本:
- 三次握手可不可以设置为两次?
不行。如果发送端第一次发送的连接请求1没有及时到达,收不到接收端的回复,它就会再次发送一个 连接请求2,接收端回复后成功连接。进行通信后断开连接。此时 连接请求1(超时)到达,接收端就会再次处理了这个过期请求,因此不可以设置为两次。
TCP断开连接的过程(四次挥手)
- 第一次挥手
client发送一个断开连接的请求数据报文到server - 第二次挥手
server收到断开连接请求数据报文之后,只是回应一个确认报文段 - 第三次挥手
server发送一个关闭连接请求数据报文到client
当前TCP的报文段存活时间为2MSL,等待2MSL为了保证最后一个报文段都能够到达。 - 第四次挥手
client发回ACK数据报文确认
等待2MSL再进行最后一次挥手,保证最后一个报文段都能从server发送到client。(报文段存活最长时间为2MSL)
- 四次挥手可不可以设置为三次?
不可以。如果只有三次挥手,意味着客户端发送了请求报文之后,server将断开连接的请求和ack的请求同时发送给client(没有第二次)。client端回复ACK完成三次挥手。但此时server端发送缓冲区或网络节点上还有未发送完的数据,会造成数据的丢失。
UDP协议
特点:
- UDP是无连接状态
- UDP尽最大能力交付
- UDP面向报文
- UDP协议没有拥塞控制
- UDP头部开销小
只有4个字段:源端口 目的端口 长度 校验和
UDP编程逻辑:编程时只需要将数据进行打包
服务器端:
try {
//构造发送数据的对象
DatagramSocket socket = new DatagramSocket(6666);
//接收数据
byte[] buf = new byte[1024];
//将数据接收
DatagramPacket dp = new DatagramPacket(buf, 1024);
socket.receive(dp);
String info = new String(dp.getData(), 0, dp.getLength());
System.out.println("接收到数据为: "+info);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
客户端:
//将数据进行打包
String info = "hello world";
try {
//构造发送数据的对象
DatagramSocket socket = new DatagramSocket();
//将数据打包-> 发送
DatagramPacket dp = new DatagramPacket(info.getBytes(), info.length(), InetAddress.getByName("localhost"), 6666);
socket.send(dp);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
TCP和UDP的区别?
- TCP
面向连接、可靠的、流式服务。
流式服务:数据是一个数据流,数据没有界限,数据的发送和接收存在直接的联系;
面向连接,可靠:
① 应答确认机制 & 超时重传机制
② 保证有序,每一个TCP数据报文都会有序号
③ 接收的数据和源数据完全相同,数据没有损坏 - UDP
无连接、不可靠的、面向报文的服务。
尽最大努力交付,即不保证可靠交付。
实时性强,工作效率高,适用于对高速传输和实时性有较高的通信或广播通信。
表格对比:
TCP | UDP |
---|---|
面向连接 | 无连接 |
可靠 | 不可靠 |
面向字节流 | 面向报文 |
点对点通信 | 点对点、一对一、一对多、多对多通信 |
首部开销 20字节 | 首部开销 8字节 |
对系统资源要求较多 | 对系统资源要求较少 |
传输效率低 | 传输效率高 |
传输速度慢 | 传输速度快 |
TCP为什么可靠?
- 确认和重传机制
建立连接时三次握手同步双方的“序列号 + 确认号 + 窗口大小信息”,是确认重传、流控的基础。传输过程中,如果Checksum校验失败、丢包或延时,发送端重传。 - 数据排序
TCP有专门的序列号SN字段,可提供数据re-order - 流量控制
滑动窗口和计时器的使用。TCP窗口中会指明双方能够发送接收的最大数据量,发送方通过维持一个发送滑动窗口来确保不会发生由于发送方报文发送太快接收方无法及时处理的问题。 - 拥塞控制(由4个核心算法组成):
“慢启动”(Slow Start)
“拥塞避免”(Congestion avoidance)
“快速重传 ”(Fast Retransmit)
“快速恢复”(Fast Recovery)