发送: 封装 接受:解包+分用
封装:OS的代码进行封装
2观察封装后的数据(抓包工具——wireshark)
端口只能绑定到一个进程上
目录
使用TCP协议
传输层TCP协议:进程TO进程 +可靠性
什么是可靠性??
比起UDP
当数据没有发送出去时 TCP会告诉进程数据发送失败了
保证不会收到错误的数据(通过checksum)
TCP能够保证收到的数据一定是有序的(按照发送进程发送时的顺序)
TCP根据对方接受的能力和网络线路的承载能力 流量控制
TCP通过哪些机制来保证可靠性的
确认应答机制(数据编号机制+超时重传机制) 接收方(对方的TCP)有责任对收到的数据进行确认应答
给数据进行编号 编号放在TCP协议段的32位序号中
包头 segment sn ack ASN等信息就保证了可靠性
比起UDP TCP有了缓冲区
1在数据发送之后数据并没有被直接丢弃 而是被保存了下来 保存在缓存区
2TCP也有一个接受缓冲区
3 TCP得维护发送或接受时的序号 可以得到去重的效果
网络分层
发送数据报时,发送端主机都需要先根据网络分层从上到下封装:
层次 | 主要职责 | 重点协议 | 相关概念 |
应用层 | 处理业务逻辑 | HTTP(S) DNS | 请求/响应 |
传输层 | 跨主机 进程TO进程 | TCP UDP | 段/报文/端口 |
网络层 | 跨LAN 主机TO主机 | IP | 包/IP地址 |
数据链路层 | LAN内 主机TO主机 | 帧/MAC地址 |
应用层:应用程序基于HTTP协议的封装 |
传输层:操作系统基于TCP协议的封装 |
网络层:操作系统基于IP协议的封装 |
数据链路层:操作系统基于以太网帧封装 |
源主机和目的主机在同一个网段时,下一跳设备就是目的主机;
发送端主机和接收端主机在不同网段时,发送端主机是无法知道目的主机在哪,此时会设置下一跳
设备为网关设备;也就是当跨LAN时 数据就会经过很多路由器接力传到目的主机
跨机请求下:
请求——响应模式
1同一个主机下进程(Server)TO进程(Client)
package hututu.about_join.tcp;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Scanner;
// UDP: 无连接 写信
// TCP: 有连接 打电话
public class Server {
private static final HashMap<String, String> map = new HashMap<>();
static {
map.put("apple", "苹果");
map.put("banana", "香蕉");
}
public static void main(String[] args) throws IOException {
// 1. 开店(创建 socket)
Login.println("服务器启动,监听在 TCP:8079 端口");
ServerSocket serverSocket = new ServerSocket(8079);
// 进行循环
while (true) {
// 1. 接通连接(电话) —— accept
Login.println("等待新的客户端连接上来");
Socket socket = serverSocket.accept(); // 阻塞
Login.println("有新的客户端连接上来");
InetAddress inetAddress = socket.getInetAddress();
Login.println("对方的地址: " + inetAddress);
int port = socket.getPort();
Login.println("对方的端口: " + port);
// is: 用于读数据
InputStream is = socket.getInputStream();
// os: 用于写数据
OutputStream os = socket.getOutputStream();
// 2. 读取请求
Scanner scanner = new Scanner(is, "UTF-8");
String header = scanner.nextLine(); // "我是 Java19班的"
// TODO: 做请求格式检查
String englishWord = scanner.nextLine();
Login.println("英文单词: "+ englishWord);
// 3. 处理业务
String chineseWord = map.getOrDefault(englishWord, "不认识");
Login.println("中文单词: "+ chineseWord);
// 4. 发送响应
// 好的\r\n苹果\r\n
// OutputStream -> OutputStreamWriter -> PrintWriter
OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
PrintWriter printWriter = new PrintWriter(writer);
Login.println("服务器进行发送");
printWriter.printf("好的\r\n%s\r\n", chineseWord);
printWriter.flush(); // 不要忘记 flush
Login.println("服务器发送成功");
socket.close();
}
}
}
package hututu.about_join.tcp;
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", 8079);
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();
}
}
跨主机
可以把server设置在LINUX云服务器上服务器 client运行在自己电脑上
TCP 和UDP的区别
TCP | UDP |
面向字节流(可能累计数据一起发送) | 面向数据报文(有了数据 立即发送) |
可靠 | 不可靠 无连接 |
(失败会有响应 按进程顺序发送 根据对方接受能力网络线路的承载能力 流量控制) | (只能保证数据发送了 不能保证数据有没有接收到) |
TCP协议段格式
源/目的端口号:表示数据是从哪个进程来,到哪个进程去;
32位序号/32位确认号:
4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是15 * 4 = 60
6位标志位:
URG:紧急指针是否有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
16位窗口大小:后面再说
16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光
包含TCP首部,也包含TCP数据部分。
16位紧急指针:标识哪部分数据是紧急数据;
40字节头部选项:暂时忽略;
TCP原理
TCP对数据传输提供的管控机制,主要体现在两个方面:安全和效率。
这些机制和多线程的设计原则类似:保证数据传输安全的前提下,尽可能的提高传输效率。
确认应答机制
TCP将每个字节的数据都进行了编号 ——序列号
每一个ACK都有对应的序列号 意思就是告诉发送者我收到了哪些数据
超时重传机制
所以当B收到这些重复数据 TCP就会识别序列号 然后达到去重的效果