<Java>19 网络编程
本文最后更新于:2023年6月27日 上午
19 网络编程
网络通信:两台设备之间通过网络实现数据传输。
java.net
包下提供了一系列类或接口,供程序员使用,完成网络通信
19.1 网络的相关概念
网络
网络:两台或多台设备通过一定物理设备连接起来构成了网络
根据网络覆盖范围的不同,对网络进行分类:
- 局域网:覆盖范围最小,仅覆盖一个教室·机房
- 城域网:覆盖范围较大,可覆盖一个城市
- 广域网:覆盖范围最大,可以覆盖全国,甚至全球。万维网 是广域网的代表
IP 地址
IP 地址:用于唯一标识网络中的每台计算机 / 主机
查看 IP 地址:ipconfig
IPv4 是 4 个字节(32位)表示。每个字节范围是 [0,255]
IP 地址的表示形式:点分十进制(xx.xx.xx.xx),每个十进制数范围是 [0,255]
IP 地址的组成 = 网络地址 + 主机地址
- A类:0 + 7 位网络号 + 24 位主机号(0.0.0.0 ~ 127.255.255.255)
- B类:1 + 0 + 14 位网络号 + 16 位主机号(128.0.0.0 ~ 191.255.255.255)
- C类:1 + 1 + 0 + 21 位网络号 + 8 位主机号(192.0.0.0 ~ 223.255.255.255)
- D类:1 + 1 + 1 + 0 + 28 位多播组号(224.0.0.0 ~ 239.255.255.255)
- E类:1 + 1 + 1 + 1 + 0 + 27 位(留待后用)(240.0.0.0 ~ 247.255.255.255)
IPv6 是互联网工程任务组设计的用于替代 IPv4 的下一代 IP 协议。其地址数量可以为全世界每一粒沙子编上一个地址
IPv4 最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPv6 的使用,不仅能解决网络地址资源数量的问题,也解决了多种接入设备接入互联网的障碍
IPv6 使用 16 个字节(128 位)表示地址。
表示形式有:
-
冒分十六进制表示法: (X:X:X:X:X:X:X:X)
:
之间的部分,出现 0 开头的场合,那些 0 可以省略 -
0 位压缩表示法:把连续的
0
压缩为::
,这个压缩只能出现一次(X::X:X) -
内嵌 IPv4 地址表示法:前 96位 用冒分十六进制表示,后面 32位 用 IPv4 的点分十进制(X:X:X:X:X:XX:d.d.d.d)
子网掩码
只用一个 IP 地址,无法分辨网络部分与主机部分的分界线。因此,使用子网掩码来表示分界线。
这个场合,对应的网络部分的子网掩码的二进制数字设为 1
(子网掩码图_19.1)
此外,还能把子网掩码与 IP 地址组合
- 在 IP 地址后加斜线及网络部分二进制数字数(IPV4):192.168.15.1/16
- IPv6:X:X:X:X:X:X:X:X/64
通过更改子网掩码,可以细分网络为多个子网。
(子网图_19.1)
保留地址
IP 还定义了一套特殊的地址格式,称为保留地址,这些保留地址不分配给任何主机。
网络号 | 主机号 | 地址类型 | 举例 | 用途 |
---|---|---|---|---|
全 0 | 全 0 | 本机地址 | 0.0.0.0 | 启动时使用 |
任意 | 全 0 | 网络地址 | 61.0.0.0 | 标识一个网络 |
任意 | 全 1 | 直接广播地址 | 129.21.255.255 | 在特定网络上广播 |
全 1 | 全 1 | 有线广播地址 | 255.255.255.255 | 在本网段上广播 |
第一段为 127 | 任意 | 回送地址 | 127.0.0.1 | 测试 |
私有地址
私有地址:与 IP 地址(全局地址)相比,在不同的网络中可以重复的地址。
私有地址是以下范围中的地址。这些地址不能作为全局地址使用:
- 10.0.0.0 ~ 10.255.255.255
- 172.16.0.0 ~ 172.31.255.255
- 192.168.0.0 ~ 192.168.255.255
将私有地址连接到全局地址的方法:
- NAT:一种私有地址与全局地址一一对应的机制
- NAPT:一种用一个全局地址连接多个计算机的机制
域名
示例:http://bbs.tianya.cn/post-house-252774-1.shtml
为了方便记忆,解决记忆 IP 的困难
IP 地址根据 HTTP 协议 映射成域名
通过 DNS(Domain Name System)服务将域名转化为 IP 地址
端口号
用于标识计算机上某个特定的网络程序
表示形式:以整数形式,范围 [0,65535]
0 ~ 1024 已经被占用,不要使用。比如 ssh 22、ftp 21、smtp 25、http 80
常见的网络程序端口号:
- tomcat:8080
- mysql:3306
- oracle:1521
- sqlserver:1433
网络通信协议
协议(TCP/IP)
TCP/IP:传输控制协议 / 因特网互联协议(Transmission Control Protocol / Internet Protocol),又叫 网络通讯协议。这个协议是 Internet 最基本的协议、Internet 国际互联网络的基础。简单来讲,就是由 网络层的 IP 协议 和传输层的 TCP 协议 组成
(数据封装图_19.1)
OSI 模型(理论) | TCP/IP 模型(实际使用) | TCP/IP 模型各层对应协议 |
---|---|---|
应用层 | 应用层 | HTTP、ftp、telent、DNS…… |
表示层 | 应用层 | 同上 |
会话层 | 应用层 | 同上 |
传输层 | 传输层(TCP) | TCP、UDP…… |
网络层 | 网络层(IP) | IP、ICMP、ARP…… |
数据链路层 | 物理 + 数据链路层 | Link |
物理层 | 物理 + 数据链路层 | 同上 |
19.1.1 TCP 和 UDP
TCP
传输控制协议
- 使用 TCP 协议前,须先建立 TCP 连接,形成传输数据通道。TCP 通信是一对一通信
- 传输前,采用 “三次握手” 方式,是可靠的
- TCP 协议进行通信的两个应用进程:客户端、服务端
- 在连接中可进行大数据量的传输。传输前,先确认要交流的数据量。那个数据量、数据窗口取较小方的数值。
- 发送方没有收到接收方的确认应答时,(在一定次数内)会再次发送数据包
- 传输完毕,需释放已建立的连接,效率低
UDP
用户数据协议
- 将 数据、源、目的 封装成数据包,不需要建立连接。可以同时向多个接收方发送
- 每个数据包大小限制在 64K 以内,不适合传输大量数据
- 因无需连接,所以是不可靠的
- 接收方无需发送确认应答
- 发送数据结束时无需释放资源(因为不是面向连接的),速度快
19.2 InetAddress
类
相关方法
getLocalHost
:获取本机InetAddress
对象getByName
:根据指定主机名 / 域名获取 IP 地址对象getHostName
:获取InetAddress
对象的主机名getHostAddress
:获取InetAddress
对象的地址
19.3 Socket
- 套接字(Socket)开发网络应用程序被广泛采用,以至于成为了事实上的标准
- 通信的两端都要有 Socket,是两台机器间通信的端点
- 网络通信其实就是 Socket 间的通信
- Socket 允许程序把网络连接当成一个流,数据在两个 Socket 间通过 IO 传输
- 一般主动发起通信的应用程序属于客户端,等待通信请求的为服务端
19.3.1 TCP 网络通信编程
- 基于客户端——服务端的网络通信
- 底层使用的是 TCP / IP 协议
- 应用场景距离:客户端发送数据,服务端接收并显示
- 基于 Socket 的 TCP 编程
下面,示范一个 服务端
public void server() throws IOException{
ServerSocket serverSocket = new ServerSocket(9000); //[1]
Socket clientSocket = serverSocket.accept(); //[2]
//下面是输入流,不解释了
InputStream inputStream = clientSocket.getInputStream();
System.out.println(clientSocket.getInetAddress());
int n;
byte[] b = new byte[1024];
byte[] B = new byte[0];
while ((n = inputStream.read(b, 0, 1024)) != -1) {
B = Arrays.copyOf(B, B.length + n);
for (int i = 0; i < n; i++) {
B[B.length - n + i] = b[i];
}
}
serverSocket.close(); //[3]
System.out.println(new String(B));
}
-
ServerSocket serverSocket = new ServerSocket(9000);
这个语句用以监听 9000 这个端口
细节:这里要求该端口没有被其他服务占用。
-
Socket clientSocket = serverSocket.accept();
这个语句用以接收连接的
Socket
。没有连接时,程序会阻滞在这里细节:此处
accept()
可以返回多个Socket
,即多并发 -
serverSocket.close();
结束后,务必关闭!
下面,示范一个客户端
public void client() throws IOException{
String serverIP = "192.168.3.16"; //[1]
Socket socket = new Socket(serverIP, 9000); //[2]
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,Server!".getBytes(StandardCharsets.UTF_8));
socket.shutdownOutput(); //[3]
outputStream.close();
socket.close();
}
-
这个 IP 是我的本机地址。代表的是 服务端 地址
-
Socket socket = new Socket(serverIP, 9000);
表示访问指定 IP 的 9000 端口
-
socket.shutdownOutput();
这里是输出一个结束标记。若不如此做,socket 就不知道是否数据发送完成
特别的,由 字节流 输出的场合,
writer.newLine()
可以替代结束标记。但是这个场合,接收必须是reader.readLine()
#19.3.1.1 netstat 指令
-
netstat -an
可以查看当前主机网络情况,包括端口监听情况和网络连接情况 -
netstat -an | more
可以分页显示 -
netstat -anb
可以显示占用端口的应用 -
要求在 dos 控制台下执行
-
Listening 表示某个端口在监听。
如果有一个外部程序连接到该端口,就会显示一条连接信息 Established
#19.3.1.2 TCP 连接秘密
当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的。这个端口由 TCP/IP 来分配,是不确定的,随机的。
19.3.2 UDP 网络通信编程
- 类
DatagramSocket
和DatagramPacket
实现了基于 UDP 协议网络程序 - 没有明确的服务端和客户端,演变成数据的发送端和接收端
- UDP 数据报通过数据报套接字
DatagramSocket
发送和接收。系统不保证 UDP 数据报一定能安全送到目的地,也不能确定什么时候能抵达 DatagramPacket
对象封装了 UDP 数据报,在数据报中包含了发送端的 IP 地址和端口号以及接收端的 IP 地址和端口号- 接收到
DtagramPacket
对象时,需要进行拆包,取出数据 DatagramSocket
可以指定在哪个端口接收数据- UDP 协议中每个数据报都给出了完整的地址信息,因此无需发送方和接收方的连接
下面,示范一个接收端
DatagramSocket ds = new DatagramSocket(9000); //[1]
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length); //[2]
System.out.println("萝茵 聆听中……");
ds.receive(dp); //[3]
int len = dp.getLength();
bytes = dp.getData();
System.out.println("萝茵听到了如下内容:\n" + new String(bytes, 0, len));
ds.close(); //[4]
-
DatagramSocket ds = new DatagramSocket(9000);
以 9000 这个端口作为监听端口
-
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
构建
DatagramPacket
对象,准备接收数据 -
ds.receive(dp);
监听信息,放到刚刚创建的
DatagramPacket
对象 -
ds.close()
要记得关闭呦 ★ ~
下面,示范一个发送端
System.out.println("萝茵,大声喊道:你好,世界!");
DatagramSocket ds = new DatagramSocket(8000); //[1]
InetAddress ia = InetAddress.getByName(serverIP);
byte[] bytes = "你好,世界".getBytes(StandardCharsets.UTF_8);
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, ia, 9001);
//[2]
ds.send(dp); //[3]
System.out.println("声音在虚无中回荡着……");
ds.close(); //[4]
-
DatagramSocket ds = new DatagramSocket(8000);
以 8000 这个端口作为发送端口
-
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, ia, 9001);
把要发送的数据、数据长度、对象地址、对象端口 放到包里
-
ds.send(dp);
走你 ★ ~
-
ds.close();
鸟尽弓藏
附录
项目开发流程
#1 需求分析
需求分析师(懂技术 + 懂行业)
- 需求分析报告
- 项目功能
- 客户要求
#2 设计阶段
架构师 / 项目经理
-
设计工作
- UML 类图
- 流程图
- 模块设计
- 数据库设计
- 架构
-
原型开发
-
组建团队
#3 实现阶段
程序员 / 码农
- 完成架构师的模块功能
- 测试自己的模块
#4 测试阶段
测试工程师
- 单元测试
- 测试用例
- 白盒测试
- 黑盒测试
- 集成测试
#5 实施阶段
实施工程师(开发能力 / 环境配置部署能力)
- 把项目正确地部署到客户的平台,并保证运行正常
- 身体好
#6 维护阶段
- 发现 bug 并解决
- 项目升级