网络通信
网络通信的关键三要素
InetAddress
代表IP地址
常用方法
名称 | 说明 |
public static InetAddress getLocalHost() | 获取本机IP,会以一个inetAddress的对象返回 |
public static InetAddress getByName(String host) | 根据ip地址或者域名,返回一个inetAddress对象 |
public String getHostName() | 获取该ip地址对象对应的主机名 |
public String getHostAddress() | 获取该ip地址对象中的ip地址信息 |
public boolean isReachable(int timeout) | 在指定毫秒内,判断主机与该ip对应的主机是否能连通 |
/*
InetAddress
public static InetAddress getLocalHost() 获取本机IP,会以一个inetAddress的对象返回
public static InetAddress getByName(String host) 根据ip地址或者域名,返回一个inetAddress对象
public String getHostName() 获取该ip地址对象对应的主机名
public String getHostAddress() 获取该ip地址对象中的ip地址信息
public boolean isReachable(int timeout) 在指定毫秒内,判断主机与该ip对应的主机是否能连通
*/
public class Demo1 {
public static void main(String[] args) throws IOException {
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
InetAddress byName = InetAddress.getByName("127.0.0.1");
System.out.println(byName);
String hostName = localHost.getHostName();
System.out.println(hostName);
String hostAddress = localHost.getHostAddress();
System.out.println(hostAddress);
System.out.println(localHost.isReachable(10000));
}
}
端口
- 标记正在计算机设备上运行的应用程序的一个数字,范围是 0~65535。
分类
- 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
- 注册端口:1024~49151,分配给用户进程或某些应用程序。
- 动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
1、端口号的作用是什么?
唯一标识正在计算机设备上运行的进程(程序)
2、一个设备中,能否出现2个应用程序的端口号一样,为什么?
不可以,如果一样会出现端口冲突错误。
通信协议
网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
1、通信协议是什么?
- 计算机网络中,连接和通信数据的规则被称为网络通信协议。
2、TCP通信协议的特点是什么样的?
- 它是一种面向连接的可靠通信协议
- 传输前,采用“三次握手”方式建立连接,点对点的通信,所以可靠
- 在连接中可进行大数据量的传输
- 通信效率较低
3、UDP协议的特点是什么
- 面向无连接,不可靠传输的通信协议
- 速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据
UDP通信
- 特点:无连接、不可靠通信
- 不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了
- Java提供了一个java.net.DatagramSocket类来实现UDP通信
DatagramSocket: 用于创建客户端、服务端
构造器 | 说明 |
public DatagramSocket() | 创建客户端的Socket对象, 系统会随机分配一个端口号 |
public DatagramSocket(int port) | 创建服务端的Socket对象, 并指定端口号 |
方法 | 说明 |
public void send(DatagramPacket dp) | 发送数据包 |
public void receive(DatagramPacket p) | 使用数据包接收数据 |
DatagramPacket:创建数据包
构造器 | 说明 |
public DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 创建发出去的数据包对象 |
public DatagramPacket(byte[] buf, int length) | 创建用来接收数据的数据包 |
方法 | 说明 |
public int getLength() | 获取数据包,实际接收到的字节个数 |
入门案例:
/*
客户端
发数据
实现步骤
1. 创建DatagramSocket对象(客户端对象)---->扔韭菜的人
2. 创建DatagramPacket对象封装需要发送的数据(数据包对象)---->韭菜盘子
3. 使用DatagramSocket对象的send方法,传入DatagramPacket对象---->开始抛出韭菜
4. 释放资源
使用的API
DatagramSocket: 客户端、服务端
public DatagramSocket()创建客户端的Socket对象, 系统会随机分配一个端口号
public void send(DatagramPacket dp) 发送数据包
DatagramPacket:数据包
public DatagramPacket(byte[] buf, int length, InetAddress address, int port) 创建发出去的数据包对象
*/
public class Client {
public static void main(String[] args) throws Exception {
//1.创建DatagramSocket对象(客户端对象) ---> 扔韭菜的人
DatagramSocket client = new DatagramSocket();
//创建一个键盘监视对象
//2.创建DatagramPacket对象封装需要发送的数据(数据包对象) --->韭菜盘子
byte[] bytes = "一捆韭菜".getBytes(StandardCharsets.UTF_8);
InetAddress localHost = InetAddress.getLocalHost();
InetAddress byName = InetAddress.getByName("192.168.141.168");
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, localHost, 8888);
//3.使用DatagramSocket对象的send方法,传入DatagramPacket对象 ---> 开始抛出韭菜
client.send(datagramPacket);
//4.释放资源
client.close();
}
}
/*
服务端
接收数据(必须用8888)
实现步骤
1. 创建DatagramSocket对象并指定端口(服务端对象) ---->接韭菜的人
2. 创建DatagramPacket对象接收数据(数据包对象)---->韭菜盘子
3. 使用DatagramSocket对象的receive方法,传入DatagramPacket对象 ---->开始接收韭菜
4. 打印结果
5. 释放资源
使用的API
DatagramSocket: 客户端、服务端
public DatagramSocket(int port) 创建服务端的Socket对象, 并指定端口号
public void receive(DatagramPacket p) 使用数据包接收数据
DatagramPacket:数据包
public DatagramPacket(byte[] buf, int length) 创建用来接收数据的数据包
public int getLength() 获取数据包,实际接收到的字节个数
*/
public class Server {
public static void main(String[] args) throws Exception {
//1.创建DatagramSocket对象并指定端口(服务端对象) ---> 接韭菜的人
DatagramSocket server = new DatagramSocket(8888);
//2. 创建DatagramPacket对象接收数据(数据包对象)---->韭菜盘子
byte[] bytes = new byte[64 * 1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
//3. 使用DatagramSocket对象的receive方法,传入DatagramPacket对象 ---->开始接收韭菜
server.receive(datagramPacket);
//获取数据包的ip和端口
String hostAddress = datagramPacket.getAddress().getHostAddress();
int port = datagramPacket.getPort();
//4.TODO 打印结果
String str = new String(bytes, 0, datagramPacket.getLength());
System.out.println("hostAddress=" + hostAddress + ",port" + port + ",str=" + str);
//5.释放资源
server.close();
}
}
1、实现UDP通信,如何创建客户端、服务端对象?
public DatagramSocket():创建发送端的Socket对象
public DatagramSocket(int port):创建接收端的Socket对象
2、数据包对象是哪个?
DatagramPacket
3、如何发送、接收数据?
使用DatagramSocket的如下方法:
public void send(DatagramPacket dp):发送数据包
public void receive(DatagramPacket dp) :接收数据包
案例:多收多发
/*
客户端
发数据
实现步骤
1. 创建DatagramSocket对象(客户端对象)---->扔韭菜的人
2. 创建DatagramPacket对象封装需要发送的数据(数据包对象)---->韭菜盘子
3. 使用DatagramSocket对象的send方法,传入DatagramPacket对象---->开始抛出韭菜
4. 释放资源
使用的API
DatagramSocket: 客户端、服务端
public DatagramSocket()创建客户端的Socket对象, 系统会随机分配一个端口号
public void send(DatagramPacket dp) 发送数据包
DatagramPacket:数据包
public DatagramPacket(byte[] buf, int length, InetAddress address, int port) 创建发出去的数据包对象
*/
public class Client {
public static void main(String[] args) throws Exception {
//1.创建DatagramSocket对象(客户端对象) ---> 扔韭菜的人
DatagramSocket client = new DatagramSocket();
//创建一个键盘监视对象
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("请输入内容:");
String next = scanner.next();
//如果用户输入exit就退出输入
if (next.equals("exit")) {
//退出
break;
}
//2.创建DatagramPacket对象封装需要发送的数据(数据包对象) --->韭菜盘子
// byte[] bytes = "一捆韭菜".getBytes(StandardCharsets.UTF_8);
byte[] bytes = next.getBytes(StandardCharsets.UTF_8);
InetAddress localHost = InetAddress.getLocalHost();
InetAddress byName = InetAddress.getByName("192.168.141.168");
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, byName, 8888);
//3.使用DatagramSocket对象的send方法,传入DatagramPacket对象 ---> 开始抛出韭菜
client.send(datagramPacket);
}
//4.释放资源
client.close();
}
}
/*
服务端
接收数据(必须用8888)
实现步骤
1. 创建DatagramSocket对象并指定端口(服务端对象) ---->接韭菜的人
2. 创建DatagramPacket对象接收数据(数据包对象)---->韭菜盘子
3. 使用DatagramSocket对象的receive方法,传入DatagramPacket对象 ---->开始接收韭菜
4. 打印结果
5. 释放资源
使用的API
DatagramSocket: 客户端、服务端
public DatagramSocket(int port) 创建服务端的Socket对象, 并指定端口号
public void receive(DatagramPacket p) 使用数据包接收数据
DatagramPacket:数据包
public DatagramPacket(byte[] buf, int length) 创建用来接收数据的数据包
public int getLength() 获取数据包,实际接收到的字节个数
*/
public class Server {
public static void main(String[] args) throws Exception {
//1.创建DatagramSocket对象并指定端口(服务端对象) ---> 接韭菜的人
DatagramSocket server = new DatagramSocket(8888);
//2. 创建DatagramPacket对象接收数据(数据包对象)---->韭菜盘子
byte[] bytes = new byte[64 * 1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
while (true) {
//3. 使用DatagramSocket对象的receive方法,传入DatagramPacket对象 ---->开始接收韭菜
server.receive(datagramPacket);
//获取数据包的ip和端口
String hostAddress = datagramPacket.getAddress().getHostAddress();
int port = datagramPacket.getPort();
//4.TODO 打印结果
String str = new String(bytes, 0, datagramPacket.getLength());
System.out.println("hostAddress=" + hostAddress + ",port" + port + ",str=" + str);
}
//5.释放资源
// server.close();
}
}
1、UDP的接收端为什么可以接收很多发送端的消息?
接收端只负责接收数据包,无所谓是哪个发送端的数据包。
TCP通信
- 特点:面向连接、可靠通信。
- 通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。
- Java提供了一个java.net.Socket类来实现TCP通信。
TCP通信之-客户端开发
- 客户端程序就是通过java.net包下的Socket类来实现的
构造器 | 说明 |
public Socket(String host , int port) | 根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket |
方法 | 说明 |
public OutputStream getOutputStream() | 获得字节输出流对象 |
public InputStream getInputStream() | 获得字节输入流对象 |
案例:快速入门
/*
客户端
发数据
实现步骤
1. 创建客户端的Socket对象,请求与服务端的连接
2. 使用socket对象调用getOutputStream()方法得到字节输出流
3. 使用字节输出流完成数据的发送
4. 释放资源:关闭socket管道
使用的API
public Socket(String host , int port) 根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
public OutputStream getOutputStream() 获得字节输出流对象
public InputStream getInputStream() 获得字节输入流对象
*/
public class Client {
public static void main(String[] args) throws Exception {
//1. 创建客户端的Socket对象,请求与服务端的连接
Socket socket = new Socket("127.0.0.1", 8888);
//2. 使用socket对象调用getOutputStream()方法得到字节输出流
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
//3. 使用字节输出流完成数据的发送
dataOutputStream.writeUTF("水球");
//4. 释放资源:关闭socket管道
dataOutputStream.close();
outputStream.close();
socket.close();
}
}
TCP通信-服务端程序的开发
- 服务端是通过java.net包下的ServerSocket类来实现的。
ServerSocket
构造器 | 说明 |
public ServerSocket(int port) | 为服务端程序注册端口 |
方法 | 说明 |
public Socket accept() | 阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 |
/*
服务端
接收数据
实现步骤
1. 创建ServerSocket对象,注册服务端端口
2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
3. 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
4. 释放资源:关闭socket管道
使用的API
public ServerSocket(int port) 为服务端程序注册端口
public Socket accept() 阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象
*/
public class Server {
public static void main(String[] args) throws Exception {
//1. 创建ServerSocket对象,注册服务端端口
ServerSocket serverSocket = new ServerSocket(8888);
//2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
Socket socket = serverSocket.accept();
//3. 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
//获取客户端发送的数据
String str = dataInputStream.readUTF();
String hostAddress = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
System.out.println("客户端的ip:" + hostAddress + "客户端的port:" + port + ",内容:" + str);
//4. 释放资源:关闭socket管道
dataInputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
1、TCP通信,客户端的代表类是谁?
Socket类
2、TCP通信服务端用的类是谁?
ServerSocket类
3、TCP通信,如何使用Socket管道发送、接收数据 ?
OutputStream getOutputStream():获得字节输出流对象(发)
InputStream getInputStream():获得字节输入流对象(收)
案例:多发多收
/*
客户端
发数据
实现步骤
1. 创建客户端的Socket对象,请求与服务端的连接
2. 使用socket对象调用getOutputStream()方法得到字节输出流
3. 使用字节输出流完成数据的发送
4. 释放资源:关闭socket管道
使用的API
public Socket(String host , int port) 根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
public OutputStream getOutputStream() 获得字节输出流对象
public InputStream getInputStream() 获得字节输入流对象
*/
public class Client {
public static void main(String[] args) throws Exception {
//1. 创建客户端的Socket对象,请求与服务端的连接
Socket socket = new Socket("127.0.0.1", 8888);
//2. 使用socket对象调用getOutputStream()方法得到字节输出流
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
//获取用户键盘输入的内容
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入内容:");
String next = scanner.next();
//判断用户输入的内容
if (next.equals("exit")) {
break;
}
//3. 使用字节输出流完成数据的发送
// dataOutputStream.writeUTF("水球");
dataOutputStream.writeUTF(next);
}
//4. 释放资源:关闭socket管道
dataOutputStream.close();
outputStream.close();
socket.close();
}
}
/*
服务端
接收数据
实现步骤
1. 创建ServerSocket对象,注册服务端端口
2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
3. 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
4. 释放资源:关闭socket管道
使用的API
public ServerSocket(int port) 为服务端程序注册端口
public Socket accept() 阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象
*/
public class Server {
public static void main(String[] args) throws Exception {
//1. 创建ServerSocket对象,注册服务端端口
ServerSocket serverSocket = new ServerSocket(8888);
//2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
Socket socket = serverSocket.accept();
while (true) {
try {
//3. 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
//获取客户端发送的数据
String str = dataInputStream.readUTF();
String hostAddress = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
System.out.println("客户端的ip:" + hostAddress + "客户端的port:" + port + ",内容:" + str);
} catch (IOException e) {
e.printStackTrace();
break;
}
}
//4. 释放资源:关闭socket管道
// dataInputStream.close();
// inputStream.close();
// socket.close();
// serverSocket.close();
}
}
案例:多客户端通信
/*
客户端
发数据
实现步骤
1. 创建客户端的Socket对象,请求与服务端的连接
2. 使用socket对象调用getOutputStream()方法得到字节输出流
3. 使用字节输出流完成数据的发送
4. 释放资源:关闭socket管道
使用的API
public Socket(String host , int port) 根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
public OutputStream getOutputStream() 获得字节输出流对象
public InputStream getInputStream() 获得字节输入流对象
*/
public class Client {
public static void main(String[] args) throws Exception {
//1. 创建客户端的Socket对象,请求与服务端的连接
Socket socket = new Socket("127.0.0.1", 8888);
//2. 使用socket对象调用getOutputStream()方法得到字节输出流
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
//获取用户键盘输入的内容
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("请输入内容:");
String next = scanner.next();
//判断用户输入的内容
if (next.equals("exit")) {
break;
}
//3. 使用字节输出流完成数据的发送
// dataOutputStream.writeUTF("水球");
dataOutputStream.writeUTF(next);
//刷新缓冲区
dataOutputStream.flush();
}
//4. 释放资源:关闭socket管道
dataOutputStream.close();
outputStream.close();
socket.close();
}
}
/*
服务端
接收数据
实现步骤
1. 创建ServerSocket对象,注册服务端端口
2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
3. 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
4. 释放资源:关闭socket管道
使用的API
public ServerSocket(int port) 为服务端程序注册端口
public Socket accept() 阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象
*/
public class Server {
public static void main(String[] args) throws Exception {
//1. 创建ServerSocket对象,注册服务端端口
ServerSocket serverSocket = new ServerSocket(8888);
//创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
5,
10,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
while (true) {
Socket socket = serverSocket.accept();//放入循环当中为了建立多个连接
//创建任务对象
SocketTask socketTask = new SocketTask(socket);
//把任务交给子线程
// new Thread(socketTask).start();
poolExecutor.execute(socketTask);
}
//4. 释放资源:关闭socket管道
// dataInputStream.close();
// inputStream.close();
// socket.close();
// serverSocket.close();
}
}
class SocketTask implements Runnable{
private Socket socket;
public SocketTask() {
}
public SocketTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (//3. 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream)
){
//获取客户端发送的数据
while (true) {
try {
String str = dataInputStream.readUTF();
String hostAddress = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
System.out.println(Thread.currentThread().getName() + ",客户端的ip:" + hostAddress + "客户端的port:" + port + ",内容:" + str);
} catch (IOException e) {
// e.printStackTrace();
System.out.println("连接断开");
}
}
} catch (IOException e) {
System.out.println("获取内容失败");
// e.printStackTrace();
}
}
}
1、本次是如何实现服务端同时接收多个客户端的消息的?
主线程定义了循环负责接收客户端Socket管道连接
每接收到一个Socket通信管道后分配一个独立的线程负责处理它。
b/s程序开发
1、BS架构的基本原理是什么?
客户端使用浏览器发起请求(不需要开发客户端)
服务端必须按照HTTP协议响应数据。
public class MainServer {
public static void main(String[] args) throws IOException {
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
5,
10,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//创建服务端对象
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
//虚幻接受客户端浏览器的连接请求
Socket socket = serverSocket.accept();
//创建任务类
SocketRunnbale socketRunnbale = new SocketRunnbale(socket);
//把socket提交给线程池
poolExecutor.execute(socketRunnbale);
}
}
}
public class SocketRunnbale implements Runnable{
private Socket socket;
public SocketRunnbale(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
System.out.println("接收到浏览器的请求");
try {
String hostAddress = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
System.out.println(hostAddress + ":" + port);
OutputStream outputStream = socket.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.println("HTTP/1.1 200 OK");
printStream.println("Content-Type : text/html;charset=UTF-8");
printStream.println();
printStream.println("<div style=\"color: rgba(255, 0, 0, 0.8);;font-size: 120px;text-align: center\">");
printStream.println("白马程序员888");
printStream.println("</div>");
} catch (IOException e) {
e.printStackTrace();
}
}
}