网络:跨主机的过程和过程(Host)通信。
如何将流程发送的数据发送到网卡?
OS的网络管理(网络协议栈)
发送线路(不是我们的重点,是通信专业的研究)
如何让网卡发送数据?
1.填充数据
2.触发发送
网卡如何接受数据?
物理数据可以在入口处转换为数字数据
1.接收数据
2.通过中断控制权OS
3.OS将数据转移到过程中
如何发送和接受两台直接连接的主机的数据?
主机A
1.用数据填充网卡
2.通过网卡发送
主机B
网卡接收数据
通过中断通知OS ,转到进程
流程发送的数据是原封不动的网卡吗?
数据需要连续包装才能发送
实现网络的层次不同,所以现代网络是分层讨论的。
理论层次:OSI 7层(了解)
实际层次:TCP/IP 5层(重要)
要求:记住有哪些层次,层次前后的关系,每个层次的职责是什么
1.物理层physical layer:解决物理介质中传输的问题 系统实现OS(内核态)
hub集线器
2.数据链路层data link layer:LAN内部主机到主机,用于局域网 系统实现(我们)
switch交换机
3.网络层network layer:跨局域网LAN通信,解决路由问题 系统实现
主机到主机的上述实现 router路由器
4.传输层transmission layer:数据到达主机后,应分配哪个过程,从过程到过程 系统实现
TCP、UDP、IP、ICMP
5.应用层 application layer:处理业务逻辑 用户实现
从过程到过程 DNS、HTTP(S)协议
局域网 Local Area Network LAN:
被路由器网线直接连接的设备
广域网Wide Area Network WAN:
局域网组合形成广域网
针对LAN局域网,主机可以直接通信
组网:
1.主机与主机连接
2.星形、总线、环形...
网络设备的存在:集线器hub、交换机switch
集线器:插座(在物理层)
携带目标信息
只有目标主机接收数据,其他主机直接忽略
职责:数据链路层处理
数据链路层:解决局域网内的数据通信问题
交换机switch:交换机可以知道发送给哪个目标,只会将数据交给相应的主机
交换机在数据链路层工作
路由器router:在网络层工作
路由:寻路问题(哪条路最合适)
传输层:解决过程正确 过程通信问题
应用层:在解决通信问题的情况下,每个业务如何通信?
MAC地址:数据链路层
物理地址:网卡上绑定的地址(硬件地址),MAC地址是绝对唯一
IP地址:网络层
网络内部IP地址,不应重复。(软件地址)
端口port:
在主机内部区分不同的过程 0-65535
依赖于ip port
通过IP地址必须是网络中唯一的主机
通过port,主机内部的过程可以唯一确定
ip:port 确定网络中唯一的过程
通信主题A:IP1 :port1
通信主题B: IP2 : port2
四元组:网络中唯一确定的通信线路
发送IP:发送port 目标ip:目标port
本地IP:本地port 远端ip:远端port
传输层:TCP/UDP
五元组: 协议:Ip1:port 1:ip2:port2
过程与端口的关系:
一个过程可以同时绑定bind多个端口
同一时刻的端口只能属于一个过程
协议:网络协议的缩写,网络协议是网络通信通过的所有网络设备同遵守的一组规定。
0-1023是知名端口号,预留给服务端程序绑定广泛使用的应用层协议。
封装:
数据链路层:数据帧frame
网络层:数据包packet
传输层:
UDP:报文datagram
TCP:段segment
应用层:
请求 - 响应模型,订阅 - 推送模型
HTTP:请求、相应
一般封装的数据都是头部信息。
网络分布式架构具有以下特点:
1.在整个通信线路上,数据实际上是透明的(存在数据监控和数据篡改的风险)
2.网络在发送过程中自然是不可靠的(数据不能保证发送给对方)
3.来回线路不一致
观察数据的包装过程:OS密封代码
观察包装数据:抓取包装工具wireshark
TCP:长连接 vs 短连接
一、长连接: 重复请求 响应,由于TCP客户流,
客户端: 请求1 请求 2 请求3
服务器:请求1 请求2前一半 请求2后一半请求3
在设计应用层时,为在设计应用层时需要考虑:请求中有明确的分界线,帮助服务器划分不同的界限。
方法:
1.采用定长的方式,比如,请求的固定长度是10个字节
2.采用变长的方式
1.先发送长度,在发送请求
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class 长连接Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080); // 拨号
//有两种方式实现:设置数量或者使用特殊字符,区分每次的请求
for (int i = 0; i < 3; i++) {
InputStream is = socket.getInputStream();
Scanner scanner = new Scanner(is, "UTF-8");
OutputStream os = socket.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
PrintWriter printWriter = new PrintWriter(writer);
// 发送请求
printWriter.printf("我是Java19班的\r\napple\r\n");
printWriter.flush();
// 读取响应
String header = scanner.nextLine(); // 好的
String word = scanner.nextLine(); // 苹果
System.out.println(word);
}
//socket.close(); // 挂断电话
//我们现在要做的长连接,是不挂断电话,请求新的响应
}
}
2.使用一个特殊字符,区分不同的请求
比如:每次读到两次\r\n 表示一个请求 我是软件工程的\r\napple\r\n
围绕长连接继续进化:客户端的单词来自用户输入,什么时候结束也来自用户输入。
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class 长连接Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080); // 拨号
Scanner systemInScanner = new Scanner(System.in);
// 通过 Ctrl + D 结束
// 通过 Ctrl + Z 结果
while (systemInScanner.hasNextLine()) {
String w = systemInScanner.nextLine();
InputStream is = socket.getInputStream();
Scanner socketScanner = new Scanner(is, "UTF-8");
OutputStream os = socket.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
PrintWriter printWriter = new PrintWriter(writer);
// 发送请求
printWriter.printf("我是Java19班的\r\n%s\r\n", w);
printWriter.flush();
// 读取响应
String header = socketScanner.nextLine(); // 好的
String word = socketScanner.nextLine(); // 苹果
System.out.println(word);
}
//socket.close(); // 挂断电话
}
}
二、短连接:挂断电话之后重新请求 响应
TCP:面向字节流
让A送信,但是一天只送一封信,可能会堆积一堆信
abc def ghi 保证顺序不动,但不保证会不会 断开 abcde fghi
UDP:面向数据报文
职责:在网络层的基础上,实现了进程到进程的通信
UDP协议的包头信息:UDP添加的封装(解包、分用)
检验和checksum:防止数据错误,类似Hash的算法
发送端:hash(有效数据) 计算哈希值h1
接收端:hash(有效数据) 计算哈希值h2
h1 ! = h2 数据一定出错了
h1 == h2:大概率的认为,数据就是原数据
发送abc 立即送 收到的就是abc
发送:
1.填充端口
2.计算长度+填充长度
3.计算检验和 +填充检验和
4.立即将数据交给网络层(UDP协议栈内部没有发送缓冲区)
接受:
1.从网络层接受数据
2.通过计算校验和,检查数据是否出错了。若出错,直接丢弃。
3.数据没有错,根据自己内部维护Map<port,pid>
找到对应的接收进程(找不到直接丢弃)
4.看对应的进程是否准备好一块内存
如果暂时没有,需要暂时把数据暂时保存一段时间
5.如果有,相当于把数据复制到对应的内存空间 byte[] buf
UDP的特点:不可靠的、无连接的、面向数据报文。
TCP协议:
1.职责:进程到进程
可靠:承诺可靠,没有承诺安全
2.什么是可靠性:
1.TCP会尽自己最大的努力,将数据发送给对方
2.如果真的遇到发送不过去的情况,TCP至少会告诉发送进程,数据发送失败了
3.保证不会受到错误的数据(通过checksum)
4.TCP能保证收到的数据一定是有序的(按照发送进程发送时的顺序)
5.TCP会根据对方的接受能力和网络线路承载能力,进行流量的控制
3.TCP做什么保证可靠
1.确认应答机制:接收方(对方的TCP)有责任对收到的数据进行确认(应答)
2.连接管理(为了做到确认应达)
三次握手
1.哪三次
2.标志位+序列号+状态
3.状态变化
4.三次握手和应用层代码的关系
确认段:一份数据可以起到发送数据的角色,也可以起到确认的角色
发送:携带有数据,填写正确序列号SN,就是发送SEGMENT
确认:标志位ACK置1,
发送端:我们没有上帝视角、
当我发送一个数据没有收到应答时,可能发生了什么
可能情况:1.对方没有收到
2.对方收到了,并且应答了,但是应答没有发送过来
真的遇到没有收到应答(超时重传机制):1.不应该一直等下去(超时机制timeout)
2.重新发送机制
主机B能否判断出数据有重复?
根据数据中的序号检查
TCP的发送端不用关心你没有收到应答的原因是什么,采用统一的超时机制即可。
如果接收端真的收到了重复的数据,直接丢弃即可。
超时重传是无限制的吗?
会有一定的上限!
达到上限之后,发送发TCP就认为本次数据线路出现重大问题了。
1.TCP会关闭本次连接
2.TCP会通知进程(在JAVAZ中,采用异常处理IOException)
3.TCP会发送一个reset segment出去
关于超时时间的设置:一般这个时间不是一个固定的长度,大多采用的是逐步变长
10S 20S 40S 80S
站在进程的角度思考:向一个有问题的TCP线路中发送数据,多久之后,我“线程”知道线路有问题
10 +20+40+80s之后发现。
作为TCP发送方,经过一段时间之后,是可以知道线路有问题的
作为TCP接收方,无法得知线路有问题的(无法确认对方是没有数据发送还是发送失败了)
小结:TCP提供了确认应答机制(数据编号机制、超时重传的机制)
1,保证了TCP尽自己所能,把数据发送给对方
2,TCP在最终发送失败以后,通知了进程发送失败的事实
涉及TCP报头
1.TCP一个segment既可以当 发送segment,也可以当 确认segment使用。
SN+携带数据payload 发送作用
ack 标志位+ASN 确认作用
SN:本次数据的第一个字节的编号
ASN:(确认序列号)希望下次发送的第一个字节编号
1.TCP有没有发送缓冲区send buffer 有!
发送之后,可以直接丢弃吗? 不可以,可能要重发。所以,至少需要一个地方保存这些数据。
2.TCP有接受缓冲区。
3.TCP得维护好发送时的序号 SN=x,才可以用于
TCP得维护一个已经接收的数据序号 ASN= y,才可以去重
为什么TCP要设立建立连接。
1.必须确认对方存在,才能可靠的传输
2.交换一些必要的数据
SN不是直接从1 开始的,双方各自随机生成,随后随机交换
正式通信之前,需要一个阶段
1.确认对方在线
2.同步Synchronize一些基本信息
TCP的状态:表示当前连接目前的情况
建立连接阶段:
主动连接方 (打电话的角色)大部分是Client 确认对方在线+告诉自己的信息
被动连接方(接电话的角色)大部分是Server 确认对方在线+告诉自己的信息
ack =1 确认 ack =0 没有确认
syn =1 同步
fin =1 挥手
closed:虚拟状态
listen:被动连接方
服务器已启动 ,但没有真正的连接建立
SYN_RCVD:收到了SYN
SYN_SEND:发送了SYN