概述
通过通信线路(有线或无线)可以把不同地理位置且相互独立的计算机连同其外部设备连接起来,组成计算机网络。在操作系统、网络管理软件及网络通信协议的管理和协调下,可以实现计算机之间的资源共享和信息的传递。
网络编程
是指用来实现网络互联的不同计算机上运行的程序间可以进行数据交换。对我们来说即如何用编程语言 java 实现计算机网络中不同计算机之间的通信。
网络通信三要素
IP 地址
网络中计算机的唯一标识;
32bit(4 字节),一般用“点分十进制”表示,如 192.168.1.158;
IP 地址=网络地址+主机地址 可分类:
- A 类:第 1 个 8 位表示网络地址。剩下的 3 个 8 位表示主机地址
- B 类:前 2 个 8 位表示网络地址。剩下的 2 个 8 位表示主机地址
- C 类:前 3 个 8 位表示网络地址。剩下的 1 个 8 位表示主机地址
- D 类地址用于在 IP 网络中的组播
- E 类地址保留作研究之用
Java 编程中可使用 InetAddress 类来操纵 IP 地址。
public static void main(String[] args) throws UnknownHostException {
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost.getHostAddress());
System.out.println(localHost.getHostName());
}
端口号
用于标识进程的逻辑地址,不同进程的标识;
有效端口:0-65535,其中 0-1024 系统使用或保留端口。
传输协议
通讯的规则
常见协议: UDP(用户数据报协议)、 TCP(传输控制协议)
网络模型
计算机网络之间以何种规则进行通信,就是网络模型所研究的问题。
网络模型一般是指 OSI 七层参考模型和 TCP/IP 五层参考模型。
每一层实现各自的功能和协议,并且都为上一层提供业务功能。为了提供这种业务功能,下一层将上一层中的数据并入到本层的数据域中,然后通过加入报头或报尾来实现该层业务功能,该过程叫做数据封装
。用户的数据要经过一次次包装,最后转化成可以在网络上传输的信号,发送到网络上。当到达目标计算机后,再执行相反的数据拆包
过程。
-
物理层
主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。
主要作用是将数据最终编码为用 0、1 标识的比特流,通过物理介质传输。
这一层的数据叫做比特。 -
数据链路层
主要将接收到的数据进行 MAC 地址(网卡地址)的封装与解封装。
常把这一层的数据叫做帧。这一层常工作的设备是交换机。 -
网络层
主要将接收到的数据进行 IP 地址的封装与解封装。
常把这一层的数据叫做数据包。这一层设备是路由器。 -
传输层
定义了一些数据传输的协议和端口号。
主要将接收的数据进行分段和传输,到达目的地址后在进行重组。
常把这一层的数据叫做段。 -
会话层
通过传输层建立数据传输的通路。
主要在系统之间发起会话或者接收会话请求。 -
表示层
主要进行对接收数据的解释、加密与解密、压缩与解压缩。
确保一个系统的应用层发送的数据能被另一个系统的应用层识别。 -
应用层
主要是为一些终端应用程序提供服务。直接面对着用户的。
Socket
Socket 概述
Socket,又称为套接字,用于描述 IP 地址和端口
。应用程序通常通过 socket向网络发出请求或者应答网络请求。Socket 就是为网络编程提供的一种机制:
- 通信两端都有 socket;
- 网络通信其实就是 socket 之间的通信;
- 数据在两个 socket 之间通过 IO 传输。
- 网络编程也称作为 Socket 编程,套接字编程。
- Socket 通信是
Client/Server
模型。
基于 UDP 协议的 Socket 通信
核心类:DatagramSocket
发送端:
public static void main(String[] args) throws Exception {
// 创建发送端的socket对象 DatagramSocket
DatagramSocket client = new DatagramSocket();
// 把发送的数据打包
String message = "hello udp?";
InetAddress address = InetAddress.getLocalHost();
int port = 12321;
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, address, port);
// 发送打包好的数据
client.send(packet);
// 资源释放
client.close();
}
接收端:
public static void main(String[] args) throws Exception {
// 创建服务端socket对象 DatagramSocket
DatagramSocket server = new DatagramSocket(12321);
// 创建接收数据的数据包(数据接收的容器)
byte[] bys = new byte[1024];
DatagramPacket packet = new DatagramPacket(bys, bys.length);
// 接收数据
server.receive(packet);
// 针对数据包进行解析
String host = packet.getAddress().getHostAddress();
byte[] data = packet.getData();
System.out.println("接收来自" + host + "信息:" + new String(data));
// 释放资源
server.close();
}
基于 TCP 协议的 Socket 通信
核心 API:ServerSocket
服务端:
public static void main(String[] args) throws Exception {
// 建立服务端socket服务 ServerSocket 并且监听本机的一个端口
ServerSocket server = new ServerSocket(12321);
// 监听连接 阻塞方法 有请求过来 建立连接 返回与之对应的socket来通信
Socket s = server.accept();
// 获取输入流 读取数据
InputStream inputStream = s.getInputStream();
byte[] bys = new byte[1024];
int len = inputStream.read(bys);
System.out.println(new String(bys, 0, len));
// 释放资源
s.close();
server.close();
}
客户端:
public static void main(String[] args) throws Exception {
// 创建客户端的socket服务 Socket
// 在socket指定传输数据的目的ip和端口
Socket client = new Socket("127.0.0.1", 12321);
// 通过socket建立的通道输出数据
// 取得输出流 ,写数据
OutputStream outputStream = client.getOutputStream();
outputStream.write("hello,this is tcp".getBytes());
// 释放资源
client.close();
}
IO 通信模型
网络通信的本质是网络间的数据 IO。只要有 IO,就会有阻塞或非阻塞的问题,无论这个 IO 是网络的,还是硬盘的。原因在于程序是运行在系统之上的,任何形式的 IO 操作发起都需要系统的支持。
BIO(阻塞模式)
BIO
即 blocking IO
,是一种阻塞式的 IO
。
jdk1.4 版本之前 Socket 即 BIO 模式。
BIO 的问题在于 accept()、read()的操作点都是被阻塞的。
服务器线程发起一个 accept 动作,询问操作系统是否有新的 socket 信息从端口 X 发送过来。注意,是询问操作系统
。如果操作系统没有发现有 socket 从指定的端口 X 来,那么操作系统就会等待。这样 serverSocket.accept()方法就会一直等待。这就是为什么 accept()方法为什么会阻塞。
如果想让 BIO 同时处理多个客户端请求,就必须使用多线程,即每次 accept阻塞等待来自客户端请求,一旦收到连接请求就建立通信,同时开启一个新的线程来处理这个套接字的数据读写请求,然后立刻又继续 accept 等待其他客户端连接请求,即为每一个客户端连接请求都创建一个线程来单独处理。
NIO(非阻塞模式)
NIO
即 non-blocking IO
,是一种非阻塞式的 IO
。jdk1.4 之后提供。
NIO 三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器)。
- Buffer:容器对象,包含一些要写入或者读出的数据。在 NIO库,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的;在写入数据时,也是写入到缓冲区中。任何时候访问 NIO中的数据,都是通过缓冲区进行操作。
- Channel:通道对象,对数据的读取和写入要通过 Channel,它就像水管一样。通道不同于流的地方就是通道是双向的,可以用于读、写和同时读写操作。 Channel 不会直接处理字节数据,而是通过 Buffer对象来处理数据。
- Selector:多路复用器,选择器。提供选择已经就绪的任务的能力。Selector 会不断轮询注册在其上的 Channel,如果某个Channel 上面发生读或者写事件, 这个 Channel 就处于就绪状态,会被 Selector 轮询出来,进行后续的 I/O 操作。这样服务器只需要一两个线程就可以进行多客户端通信。
阻塞/非阻塞、同步/非同步
阻塞 IO 和非阻塞 IO 这两个概念是程序级别的。
主要描述的是程序请求操作系统 IO 操作后,如果 IO 资源没有准备好,那么程序该如何处理的问题:前者等待;后者继续执行(并且使用线程一直轮询,直到有 IO 资源准备好了)。
同步 IO 和非同步 IO,这两个概念是操作系统级别的。
主要描述的是操作系统在收到程序请求 IO 操作后,如果 IO 资源没有准备好,该如何响应程序的问题:前者不响应,直到 IO 资源准备好以后;后者返回一个标记(好让程序和自己知道以后的数据往哪里通知),当 IO 资源准备好以后,再用事件机制返回给程序。