You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

536 lines
26 KiB

3 years ago
---
title: 网络编程
author: TianZD
top: true
cover: true
toc: true
mathjax: false
summary: 网络通信模型、tcp、udp编程学习
tags:
- 网络编程
- TCP
- UDP
categories:
- java
- 网络编程
reprintPolicy: cc_by
abbrlink: f6491cfb
date: 2022-04-29 11:01:29
coverImg:
img:
password:
---
[toc]
# 网络基础
## 网络编程的目的
直接或间接地通过网络协议与其他计算机实现数据交换,进行通讯
## 网络编程中两个主要问题
* 如何准确地定位网络上一台或多台主机,定位主机上的特定应用
* 找到住后如何可靠高效地进行数据传输
## 网络层级
了使不同计算机厂家生产的计算机能够相互通信,以便在更大的范围内建立计算机网络,国际标准化组织(ISO)在1978年提出了“开放系统互联参考模型”,即著名的OSI/RM模型(Open System Interconnection/Reference [Model](https://so.csdn.net/so/search?q=Model&spm=1001.2101.3001.7020))。它将计算机网络体系结构的通信协议划分为七层,自下而上依次为:物理层(Physics Layer)、数据链路层(Data Link Layer)、网络层(Network Layer)、传输层(Transport Layer)、会话层(Session Layer)、表示层(Presentation Layer)、应用层(Application Layer)。其中第四层完成数据传送服务,上面三层面向用户。
  除了标准的OSI七层模型以外,常见的[网络层](https://so.csdn.net/so/search?q=网络层&spm=1001.2101.3001.7020)次划分还有TCP/IP四层协议以及TCP/IP五层协议,它们之间的对应关系如下图所示:
![img](https://gitee.com/tianzhendong/img/raw/master//images/764050-20150904094019903-1923900106.jpg)
![img](https://gitee.com/tianzhendong/img/raw/master//images/764050-20150904095142060-1017190812.gif)
**应用层:**
Message
**传输层:**
Message+源端口+目的端口
**网络层:**
Message+源端口+目的端口+源IP地址+目的IP地址
**数据链路层:**
Message+源端口+目的端口+源IP地址+目的IP地址+源MAC地址+目的MAC地址
**物理层:**
1、负责传输0和1这样的物理信号
| 协议层 | 关键元素 | 作用 |
| ---------- | -------------- | -------------------------------------------------------- |
| 数据链路层 | MAC地址 | 依靠MAC地址,构建同子网主机到主机的数据包传输链路 |
| 网络层 | IP地址 | 依靠IP地址,构建源子网到目标子网的数据包传输链路 |
| 传输层 | 端口 | 依靠端口,构建源进程到目标进程的传输链路 |
| 应用层 | 应用自定义规则 | 依靠客户端与服务端共同定义的规则完成客户端与服务端的交互 |
从“看”的角度----传输层、网络层、数据链路层的内容是一堆不容易看懂的十六进制数;而应用层是一些可读的字符串
从“写”的角度----在手动构造MAC头/IP头/TCP头时,一个IP、一个端口要进行半天的转换和拼接;而应用层都是string+=xxxx就完事了
简单而言造成这个问题的原因是:传输层/网络层/数据链路层的内容多用二进制代号或者数值类型,而应用层使用的是ASCII码。
按照OSI的模型,上层利用相邻下层的服务,与通信对端的同一层建立对等的服务,最高一层叫**应用层**,最下一层叫**物理层**。
比如你说你要告诉你的朋友,你的兰花开了,“兰花开了”是一条信息,是“**应用层**”需要传递的。你可以选择传递一句话,或者一张照片,将信息“表示为”一串数字或者物理实体,这是“**表示层**”该干的。如果你选择把这件事写在纸上交给对方,你得了解对方不是盲人、不是文盲、懂你写下的语言(比如中文),这是你选择**表示层**时候必须考虑的。你可以与对方成文或不成文的约定你们采用的语言,这叫**协议**。
确定了表示方法,你就需要选择把这个东西传给谁,怎么传。这次信息传送是孤立的,还是一大串信息传递(比如笔友还是生意往来)中的一部分。这叫**会话**。
确定了会话,你要确定怎么找到你的朋友。你的朋友,大明王二三,小名三仔,昵称二子,俗称二三娃,网名3123,是王一万的儿子,是A公司职员,住在B地址,是C群体的会员,每个称呼都代表着不同的**传输方法**,成本、速度、可靠性都不同,你选哪一个?选择不同,你就需要和不同的中间人说一些不同的话,最终目的是把那一串数字或者物理实体传递给正确的人。这叫**传输层**。
如果表示方法是一张照片,打算通过邮局邮寄给你的朋友,相应的会话层确定是单次通信,传输层的工作就是找到一个信封,把照片放在里面,封上封口,在信封上写上地址和名字。你必须找到你的朋友的邮寄地址。有了地址和你朋友的名字,你就试着用**会话层**提供的服务。地址代表着**网络层**的服务,名字代表着**传输层**的服务。虽然OSI的规定上传输层和[网络层](https://so.csdn.net/so/search?q=网络层&spm=1001.2101.3001.7020)互相独立,但是实际上是很难分开的。注意,这里的选项是你决定的,或者说是你和你的通信对象选择的会话方法决定的。你和你的通信对象完全可以另起炉灶约定一种谁也没见过的方法,不会伤害到任何人。你选择了对方地址,写在信封上,将照片装在信封里面不给邮递员看,这叫**封装**。经过封装后,邮政局传送的是信,而不是照片。有些封装(比如明信片)可以被邮递员看到并串改,这在数字世界很不常见。将封装好的新建丢进信筒或者交给快递员,都是你开始使用“**网络层**”服务了。
在这个例子里,**邮政局为你提供网络层服务**。一般说来,网络层按照对方地址来决定如何将信息传递到对方。你看,同样的网络层完全可以提供不同的服务,比如平信、快信和专递(当然收费也不同)。IP在这里,你要通过IP网通信,你需要知道对方的IP地址。邮政局不会专门为你把你的信专门送到对方手中,而是通过不同层次的分拣站逐层解析地址逐层传送。在收信局,每封信都要分拣,走同样路径的信件可能会被打成邮包并标记上对方分拣站的地址,这叫**路由**(翻译自route,是个动词,表示寻找路径的意思)。邮局会将“目的地分拣站”相同的信件装在邮包里快速传递,这叫**数据链路层**。数据链路层不需要知道用户信件的最终地址,只按照固定往来路径传送邮包。在OSI语境下,数据链路层可以有地址,但是这个地址只有链路层互联的终端之间有意义,而网络层地址必须是全局(全网)唯一的。往来用户收发信件的邮递员知道你的地址和交通方式,而且只需要记住他负责的片区的信息,如果有片区内的信件往来他可能直接就替你送了(在邮政局是不允许的,对快递员可能会收一点小费后就接受了),这叫**局域网**,是数据链路层的特例。注意,分拣局之间收发邮包的路径(数据链路层)无论是否遵循公开的标准都是邮政局(网络)的选择,不受任何用户干扰,和传输层。干扰邮包收发是犯罪(网络黑客)行为。你是公司职员,为公事发信,信件被前台秘书统一交寄,寄出的信件只有公司的地址和你的名字,收到的信件由前台秘书统一签收,并叫你自取或(如果你和TA关系足够好或职位足够高)送到你的办公桌上,这位秘书就是NAT。无论如何,信件还是邮包,总是装在自行车/卡车/火车/轮船/飞机上运输的,这些运输工具可能属于也可能不属于邮政局,但是总是按照自己的固定路线转运货物,甚至不知道传送的货物中有“邮包”或者“信件”这类东西,这叫**物理层**。如果你们公司是个跨国公司,对内部信件规定了特别的方法,以上所说每一件事都有人干但是并没有严格依照上述方法来传递,这叫**内部网**(PrivateNetwork, Intranet)。有时候还会将自己内部信件打包交给快递公司传送,这叫VPN。最后,按照OSI模型的语境,你不能是人,也不能是狗,甚至不能是计算机,你必须是一台计算机上运行的一个程序。
网络层具体就是指IP(Internet Protocol),因特网协议,应用层一般有HTTP,FTP等,中间还有个传输层,具体一般是TCP和UDP
那网络层和应用层是什么关系呢
应用层(HTTP)建立在传输层(TCP)基础上,传输层建立在网络层(IP)基础上
### 物理层
激活、维持、关闭通信端点之间的机械特性、电气特性、功能特性以及过程特性。**该层为上层协议提供了一个传输数据的可靠的物理媒体。简单的说,物理层确保原始的数据可在各种物理媒体上传输。**物理层记住两个重要的设备名称,中继器(Repeater,也叫放大器)和集线器。
### 数据链路层
数据链路层在物理层提供的服务的基础上向网络层提供服务,其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层。为达到这一目的,数据链路必须具备一系列相应的功能,主要有:如何将数据组合成数据块,在数据链路层中称这种数据块为帧(frame),帧是数据链路层的传送单位;如何控制帧在物理信道上的传输,包括如何处理传输差错,如何调节发送速率以使与接收方相匹配;以及在两个网络实体之间提供数据链路通路的建立、维持和释放的管理。数据链路层在不可靠的物理介质上提供可靠的传输。该层的作用包括:物理地址寻址、数据的成帧、流量控制、数据的检错、重发等。
  有关数据链路层的重要知识点:
  **1> 数据链路层为网络层提供可靠的数据传输;**
  **2> 基本数据单位为帧;**
  **3> 主要的协议:以太网协议;**
  **4> 两个重要设备名称:网桥和交换机。**
### 网络层
网络层的目的是实现两个端系统之间的数据透明传送,具体功能包括寻址和路由选择、连接的建立、保持和终止等。它提供的服务使传输层不需要了解网络中的数据传输和交换技术。如果您想用尽量少的词来记住网络层,那就是“路径选择、路由及逻辑寻址”。
  网络层中涉及众多的协议,其中包括最重要的协议,也是TCP/IP的核心协议——IP协议。IP协议非常简单,仅仅提供不可靠、无连接的传送服务。IP协议的主要功能有:无连接数据报传输、数据报路由选择和差错控制。与IP协议配套使用实现其功能的还有地址解析协议ARP、逆地址解析协议RARP、因特网报文协议ICMP、因特网组管理协议IGMP。具体的协议我们会在接下来的部分进行总结,有关网络层的重点为:
  **1> 网络层负责对子网间的数据包进行路由选择。此外,网络层还可以实现拥塞控制、网际互连等功能;**
  **2> 基本数据单位为IP数据报;**
  **3> 包含的主要协议:**
  **IP协议(Internet Protocol,因特网互联协议);**
  **ICMP协议(Internet Control Message Protocol,因特网控制报文协议);**
  **ARP协议(Address Resolution Protocol,地址解析协议);**
  **RARP协议(Reverse Address Resolution Protocol,逆地址解析协议)。**
  **4> 重要的设备:路由器。**
### 传输层
第一个端到端,即主机到主机的层次。传输层负责将上层数据分段并提供端到端的、可靠的或不可靠的传输。此外,传输层还要处理端到端的差错控制和流量控制问题。
  传输层的任务是根据通信子网的特性,最佳的利用网络资源,为两个端系统的会话层之间,提供建立、维护和取消传输连接的功能,负责端到端的可靠数据传输。在这一层,信息传送的协议数据单元称为段或报文。
  网络层只是根据网络地址将源结点发出的数据包传送到目的结点,而传输层则负责将数据可靠地传送到相应的端口。
  有关网络层的重点:
  **1> 传输层负责将上层数据分段并提供端到端的、可靠的或不可靠的传输以及端到端的差错控制和流量控制问题;**
  **2> 包含的主要协议:TCP协议(Transmission Control Protocol,传输控制协议)、UDP协议(User Datagram Protocol,用户数据报协议);**
  **3> 重要设备:网关。**
### 会话层
  会话层管理主机之间的会话进程,即负责建立、管理、终止进程之间的会话。会话层还利用在数据中插入校验点来实现数据的同步
### 表示层
  表示层对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解。表示层的数据转换包括数据的加密、压缩、格式转换等。
### 应用层
  为操作系统或网络应用程序提供访问网络服务的接口。
  会话层、表示层和应用层重点:
  **1> 数据传输基本单位为报文;**
  **2> 包含的主要协议:FTP(文件传送协议)、Telnet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议),POP3协议(邮局协议),HTTP协议(Hyper Text Transfer Protocol)。**
## Socket
> **socket 其实就是操作系统提供给程序员操作「网络协议栈」的接口,说人话就是,你能通过socket 的接口,来控制协议找工作,从而实现网络通信,达到跨主机通信。**
socket 一般分为 **TCP 网络编程**和 **UDP 网络编程。**
### TCP网络编程
![img](https://gitee.com/tianzhendong/img/raw/master//images/202201260932687.jpeg)
基于 TCP 协议的客户端和服务器工作
- 服务端和客户端初始化 `socket`,得到文件描述符;
- 服务端调用 `bind`,将绑定在 IP 地址和端口;
- 服务端调用 `listen`,进行监听;
- 服务端调用 `accept`,等待客户端连接;
- 客户端调用 `connect`,向服务器端的地址和端口发起连接请求;
- 服务端 `accept` 返回用于传输的 `socket` 的文件描述符;
- 客户端调用 `write` 写入数据;服务端调用 `read` 读取数据;
- 客户端断开连接时,会调用 `close`,那么服务端 `read` 读取数据的时候,就会读取到了 `EOF`,待处理完数据后,服务端调用 `close`,表示连接关闭。
这里需要注意的是,服务端调用 `accept` 时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。
所以,监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作**监听 socket**,一个叫作**已完成连接 socket**。
成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。
# 网络通信要素
1. 通信双方地址
* IP
* 端口
2. 一定的规则(网络通信协议)
* OSI参考模型:模型过于理想化,分为七层:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
* TCP/IP参考模型(TCP/IP协议),分为:应用层(HTTP、FTP、Telnet、DNS等协议)、传输层(TCP、UDP等协议)、网络层(IP、ICMP、ARP等协议)、物理+数据链路层(Link协议)
## ip地址-InetAddress类
* 唯一的标识Internet上的计算机(通信实体)
* 本地地址(hostAddress):127.0.0.1,主机名(hostName);localhost
* IPV4和IPV6
* IPV4:4个字节组成,4个0-255
* IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示
* 公网地址和私有地址:192.168.开头的对应的是私有地址
* 域名:如:www.baidu.com,域名先找本机hosts文件查找对应的ip地址,如果未找到会经过域名解析服务器DNS进行解析成IP地址,从而访问网络服务器
```java
public class InetAdressTest {
public static void main(String[] args) {
try {
//经过ip地址
InetAddress byAddress1 = InetAddress.getByName("192.168.1.1");
System.out.println(byAddress1);
//通过域名
InetAddress byName = InetAddress.getByName("www.baidu.com");
System.out.println(byName);
//本地回路地址127.0.0.1 域名localhost
InetAddress localhost = InetAddress.getByName("localhost");
System.out.println(localhost);
//直接获取本机地址、本机回路地址、本机域名
System.out.println(InetAddress.getLocalHost());
System.out.println(localhost.getHostAddress());
System.out.println(localhost.getHostName());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
//输出:/192.168.1.1
// www.baidu.com/180.101.49.11
// localhost/127.0.0.1
// DESKTOP-JRHETO1/192.168.1.13
// 127.0.0.1
// localhost
```
## 端口
* 端口号标识正在计算机上运行的进程(程序)
* 不同的进程有不同的端口号
* 端口号为一个16位的整数
* 端口分类:
* 公认端口:0-1023,被预先定义的服务通信端口占用(如HTTP占用80,FTP占用21,Telnet占用23)
* 注册端口:1024-49151,分配给用户进程或应用程序(如Tomcat占用8080,MySQL占用3306,Oracle占用1521)
* 动态/私有端口:59152-65535
**端口号和IP地址组合得到一个网络套接字:Socket**
## 网络协议
> 计算机网络中实现通信必须遵守的约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等指定标准
**通信协议分层思想**:在指定协议时,把复杂成分分解成一些简单的成分,再进行复合。常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层、隔层不能通信。各层互不影响
**传输层两个重要协议:**
1. 传输控制协议TCP:Transmission Control Protocol
2. 用户数据报协议UDP:User Datagram Protocol
## TCP/IP协议簇
TCP/IP是两个主要协议:传输层协议TCP和网络互联协议IP命名,实际上是一组协议,包括多个具有不同功能且互为关联的协议
1. 传输控制协议TCP:Transmission Control Protocol
2. IP(Internet Protocol):网络层的主要协议,支持网络间互联的数据通信
TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构:
1. 物理链路层
2. IP层(网络层)
3. 传输层
4. 应用层
# TCP和UDP
## TCP
### TCP特点
* 使用TCP前,需要建立TCP连接,形成传输数据通道
* 采用**“三次握手”**方式,点对点通信,是可靠的通信
![三次握手](https://gitee.com/tianzhendong/img/raw/master//images/image-20210727222452305.png)
* TCP协议进行通信的两个应用进程:客户端、服务端
* 在连接中可进行大数据量的传输
* 传输完毕,需要释放已经建立的连接,效率低,**“四次挥手”**
![](https://gitee.com/tianzhendong/img/raw/master//images/image-20210727222400368.png)
客户端和服务端均可以发起挥手动作(一般是客户端),在socket编程中,任何一方执行close()即可产生挥手操作
### TCP网络编程实例
```java
package com.tian.javastudy.InetAddressDemo;
/*
* TCP网络编程
* 客户端发送数据给服务端,服务端在控制台显示输出
* */
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPTest1 {
//服务端
@Test
public void server(){
ServerSocket serverSocket = null;
Socket accept = null;
InputStream inputStream = null;
ByteArrayOutputStream baos = null;
try {
//创建服务端的serversocket,指明自己的端口号
serverSocket = new ServerSocket(8899);
//调用accept()方法,接受来自客户端的socket
accept = serverSocket.accept();
//获取一个输入流,并读取数据
inputStream = accept.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while ((len = inputStream.read(buffer)) != -1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
//获取发送方的ip
System.out.println("发送方为:"+ accept.getInetAddress().getHostName());
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if(baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(accept != null){
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//客户端
@Test
public void client() {
Socket socket = null;
OutputStream outputStream = null;
try {
//创建socket对象,指明服务器端的ip和端口号
InetAddress inet = InetAddress.getByName("127.0.0.111");
socket = new Socket(inet, 8899);
//获取一个输出流,并写出数据
outputStream = socket.getOutputStream();
outputStream.write("你好,我是客户端".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/*
你好,我是客户端
发送方为:127.0.0.1
*/
```
### 客户端-服务端
#### 客户端
* 自定义
* 浏览器
#### 服务端
* 自定义
* Tomcat服务器
## UDP
#### 特点
* 将数据、源、目的地封装成数据包,不需要建立连接
* 每个数据报的大小限制再64K内
* 发送不管对方是否准备好,接收方收到也不确认,是不可靠的
* 可以广播发送
* 发送数据结束时无需释放资源,开销小,速度快
#### UDP网络编程
* 类DatagramSocket和DataGramPacket实现了基于UDP协议网络程序
* UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不确定什么时候可以送到
* DataGramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
* UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接,如同发快递包裹一样
```java
package com.tian.javastudy.InetAddressDemo;
import org.junit.Test;
import java.io.IOException;
import java.net.*;
public class UDPTest1 {
//发送端
@Test
public void sender() throws IOException {
//socket,无需包括接收方信息
DatagramSocket socket = new DatagramSocket();
//定义发送的信息
String s = "我是发送方";
//发送的字节长度
byte[] bytes = s.getBytes();
int len = bytes.length;
//目的ip,这里发送到本机
InetAddress inet = InetAddress.getLocalHost();
//DataGramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
DatagramPacket packet = new DatagramPacket(bytes, 0, len, inet, 8899);
//发送
socket.send(packet);
//关闭
socket.close();
}
//接收方
@Test
public void receiver() throws IOException {
//接收方端口
DatagramSocket socket = new DatagramSocket(8899);
//DatagramSocket
byte[] bytes = new byte[100];
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length);
//接受
socket.receive(packet);
//保存数据,实际上已经存在了bytes数组中,但是不知道实际的长度
String s = new String(packet.getData(),0,packet.getLength());
//输出到控制台
System.out.println(s);
}
}
/*
我是发送方
*/
```
# URL网络编程
## 特点
* URL(Uniform Resource Locator):统一资源定位符,表示Internet上某一资源的地址(种子)
* 通过URL我们可以访问Internet上的各种网络资源,比如最常见的www、ftp站点,浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源
## 基本结构
由五个部分组成:<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
如:http://localhost:8080/examples/beauty.jpg
* 传输协议:http
* 主机名:localhost
* 端口号:8080
* 文件名:examples/beauty.jpg
* 片段名:即锚点,例如看小说,直接定位到章节
* 参数列表格式:参数名=参数值&参数名=参数值
## 方法
![image-20210728000101287](https://gitee.com/tianzhendong/img/raw/master//images/image-20210728000101287.png)