文章目录
一、InetAddress类
为了方便我们对IP地址的获取和操作,Java提供了一个InetAddress类,该类表示Internet协议(IP)地址。
IP可以唯一的标识互联网上的计算机(通信实体)。InetAddress用来代表IP地址一个InetAdress的对象就代表着一个IP地址。InetAddress的构造函数不是公开的(public),所以需要通过它提供的静态方法来获取。
package APIExercise;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddress_ {
public static void main(String[] args) throws UnknownHostException {
// 获取本机的InetAddress对象
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
// 根据主机名获取InetAddress对象
InetAddress host1 = InetAddress.getByName("DESKTOP-U7QOAM5");
System.out.println(host1);
// 根据域名获取InetAddress对象
InetAddress host2 = InetAddress.getByName("www.baidu.com");
System.out.println(host2);
// 通过InetAddress对象获取IP地址和域名
String host3 = host2.getHostAddress();
String host4 = host2.getHostName();
System.out.println(host3);
System.out.println(host4);
}
}
输出:
DESKTOP-U5QOAM8/172.17.94.144
DESKTOP-U5QOAM8/172.17.94.144
www.baidu.com/220.181.38.149
220.181.38.149
www.baidu.com
二、Socket套接字
- 套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准
- 通信的两端都要有Socket,是两台机器间通信的端点
- 网络通信其实就是Socket间的通信。
- Socket允许程序把网络连接当成一个流数据在两个Socket间通过IO传输
- 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端
- 方式:TCP编程(可靠),UDP编程(不可靠)
- 在开发过程中记得关闭Socket
三、TCP编程
案例一:(传输消息,使用字节流)
package APIExercise;
import org.junit.jupiter.api.Test;
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;
/**
* @author 神代言
* @version 1.0
* 要求:(使用字节流)
* 1.编写一个服务器端和客户端
* 2.服务端在9999端口监听
* 3.客户端连接服务器发送"hello,server",
* 并接受服务器发送的"hello client",然后退出
* 4.服务器端接收到客户端发送的信息输出,
* 并发送"hello client",退出
*/
public class SocketTCP {
public static void main(String[] args) {
}
@Test
public void SocketTCPServer() throws IOException {
/* 服务器端 */
// 1.在本机的9999端口监听,等待连接
// 注意:保证本机无其他服务监听9999
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在9999端口监听,等待连接...");
// 2.当无客户端连接9999端口时,程序会堵塞,等待连接
// 若有客户端连接,会返回Socket对象
Socket socket = serverSocket.accept();
System.out.println("服务端 socket = " + socket.getClass());
// 5. 确定输入流对象,从数据通道中获取数据
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf, 0 ,readLen));
}
// 6. 获取socket的输出流
// 注意:要设置一个结束标记,表示:后面没有内容!!!
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello client".getBytes());
socket.shutdownOutput();
// 8. 关闭流和socket,注意:必须关闭!!!
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务器退出...");
}
@Test
public void SocketTCPClient() throws IOException {
/* 客户端 */
// 3. 连接服务器
// 解读:连接本机的9999端口
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
// 4. 连接上后,确定输出流对象,将数据写入数据通道
// 注意:要设置一个结束标记,表示:后面没有内容!!!
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,server".getBytes());
socket.shutdownOutput();
// 7. 确定输入流对象,从数据通道中获取数据
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf, 0 ,readLen));
}
// 8. 关闭流对象和Socket,注意:必须关闭!!!
inputStream.close();
outputStream.close();
socket.close();
System.out.println("客户端退出...");
}
}
服务器:
服务端,在9999端口监听,等待连接...
服务端 socket = class java.net.Socket
hello,server
服务器退出...
客户端:
hello client
客户端退出..
案例二:(传输消息,使用字符流)
package APIExercise;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 神代言
* @version 1.0
* 要求:(使用字符流)
* 1.编写一个服务器端和客户端
* 2.服务端在9999端口监听
* 3.客户端连接服务器发送"hello,server",
* 并接受服务器发送的"hello client",然后退出
* 4.服务器端接收到客户端发送的信息输出,
* 并发送"hello client",退出
*/
public class SocketTCP02 {
public static void main(String[] args) {
// 这里使用了转换流
}
@Test
public void SocketTCPServer() throws IOException {
/* 服务器端 */
// 1.在本机的9999端口监听,等待连接
// 注意:保证本机无其他服务监听9999
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在9999端口监听,等待连接...");
// 2.当无客户端连接9999端口时,程序会堵塞,等待连接
// 若有客户端连接,会返回Socket对象
Socket socket = serverSocket.accept();
System.out.println("服务端 socket = " + socket.getClass());
// 5. 确定输入流对象,从数据通道中获取数据
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);
// 6. 获取socket的输出流
// 注意:要设置一个结束标记,表示:后面没有内容
OutputStream outputStream = socket.getOutputStream();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello client 字符流");
bufferedWriter.newLine();// 插入换行表示结束标记,注意:要求对方使用readLine()读数据!!!
bufferedWriter.flush();// 注意:使用字符流,需要手动刷新,否则不会写入数据!!!
// 8. 关闭流和socket,注意:必须关闭
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务器退出...");
}
@Test
public void SocketTCPClient() throws IOException {
/* 客户端 */
// 3. 连接服务器
// 解读:连接本机的9999端口
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
// 4. 连接上后,确定输出流对象,将数据写入数据通道
// 这里使用转换流OutputStreamWriter将字节-》字符
// 注意:要设置一个结束标记,表示:后面没有内容!!!
OutputStream outputStream = socket.getOutputStream();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello server 字符流");
bufferedWriter.newLine();// 插入换行表示结束标记,注意:要求对方使用readLine()读数据!!!
bufferedWriter.flush();// 注意:使用字符流,需要手动刷新,否则不会写入数据!!!
// 7. 确定输入流对象,从数据通道中获取数据
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);
// 8. 关闭流对象和Socket,注意:必须关闭
inputStream.close();
outputStream.close();
socket.close();
System.out.println("客户端退出...");
}
}
服务器:
服务端,在9999端口监听,等待连接...
服务端 socket = class java.net.Socket
hello server 字符流
服务器退出...
客户端:
hello client 字符流
客户端退出...
案例三:(文件上传)
package APIExercise;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 要求:(使用字节流)
* * 1.编写一个服务器端和客户端
* * 2.服务端在8888端口监听
* * 3.客户端连接服务器发送一张图片,
* * 并接受服务器发送的"收到图片",然后退出
* * 4.服务器端接收到客户端发送的图片,保存在src目录下,
* * 并发送"收到图片",退出
*/
public class SocketTCP03 {
public static void main(String[] args) {
}
@Test
public void TCPServer() throws IOException {
// 服务器监听8888端口,并等待连接
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端,在8888端口监听,等待连接...");
Socket socket = serverSocket.accept();
// 读取数据,并将输入流转成字节数组
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
String dest = "src\\图片1.jpg";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
bos.write(bytes);
// 发送"收到图片"消息
OutputStream outputStream = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
bw.write("服务器端收到图片");
bw.newLine();//结束标记
bw.flush();//必须要刷新
// 关闭流和socket
bos.close();
bis.close();
socket.close();
serverSocket.close();
System.out.println("服务器退出...");
}
@Test
public void TCPClient() throws IOException {
// 客户端连接服务器8888
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
// 将磁盘文件转成字节数组
String filePath = "C:\\Users\\DELL\\Desktop\\电能表1.jpg";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
byte[] data = StreamUtils.streamToByteArray(bis);
// 将数据发送
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(data);
socket.shutdownOutput();//结束标记
// 接受消息
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);
// 关闭流和socket
bos.close();
bis.close();
socket.close();
System.out.println("客户端退出...");
}
}
服务器:
服务端,在8888端口监听,等待连接...
服务器退出...
客户端:
服务器端收到图片
客户端退出...
案例四:(文件下载)
package APIExercise;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 神代言
* @version 1.0
*/
public class Homework03 {
public static void main(String[] args) {
}
@Test
public void Server() throws IOException {
// 1.服务器监听端口,等待连接
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
// 4.获取客户端的请求
InputStream inputStream = socket.getInputStream();
byte[] data = new byte[1024];
int len = 0;
String downloadFileName = "";
while ((len = inputStream.read(data)) != -1) {
downloadFileName += new String(data, 0, len);
}
System.out.println("客户端希望下载的文件" + downloadFileName);
// 5.将服务器的文件转成字节流
String resFileName = "";
if("图片1.jpg".equals(downloadFileName)){
resFileName = "src\\图片1.jpg";
} else{
resFileName = "src\\图片2.jpg";
}
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resFileName));
data = StreamUtils.streamToByteArray(bufferedInputStream);
// 6.发送数据
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(data);
socket.shutdownOutput();
// 7.关闭流和socket
bos.close();
bufferedInputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务器端退出...");
}
@Test
public void Client() throws IOException {
// 2.客户端连接服务器
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
// 3.向服务器发送要下载的文件名
OutputStream outputStream = socket.getOutputStream();
String downloadFileName = "图片1.jpg";
outputStream.write(downloadFileName.getBytes());
socket.shutdownOutput();
// 7.读取客户端返回的文件
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] data = StreamUtils.streamToByteArray(bis);
// 8.写入到本地磁盘
String filePath = "C:\\Users\\DELL\\Desktop\\" + downloadFileName;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
bos.write(data);
// 9.关闭相关资源
bos.close();
bis.close();
outputStream.close();
socket.close();
System.out.println("客户端退出...");
}
}
四、netstat指令
五、UDP编程(了解)
基本介绍
- 类 DatagramSocket 和 DatagramPacket[数据包/数据报] 实现了基于 UDP协议网络程序;
- UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达;
- DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号;
- UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。
基本流程
- 核心的两个类/对象 DatagramSocket与DatagramPacket;
- 建立发送端,接收端(没有服务端和客户端概念);
- 发送数据前,建立数据包/报 DatagramPacket对象;
- 调用DatagramSocket的发送、接收方法;
- 关闭DatagramSocket。
package APIExercise;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.*;
/**
* 1.编写一个接收端A和一个发送端B
* 2.接收端A在 9999端口等待接收数据(receive)
* 3.发送端B向接收端A 发送 数据"hello,明天吃火锅"
* 4.接收端B接收到 发送端A发送的数据,
* 回复“好的,明天见”,再退出
* 5.发送端接收 回复的数据,再退出
*/
public class SocketUDP {
public static void main(String[] args) {
}
@Test
public void ReceiverA() throws IOException {
//1. 创建一个DatagramSocket对象,准备在9999端口处接收数据
DatagramSocket socket = new DatagramSocket(9999);
//2. 构建一个DatagramPacket对象,准备接收数据
byte[] buf = new byte[64*1024];//UDP数据包最大64k
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//3. 接收数据,将数据传入DatagramPacket对象中
// 若无数据则阻塞
socket.receive(packet);
//4. 拆包,取出数据并显示
int length = packet.getLength();
byte[] data = packet.getData();
String string = new String(data, 0, length);
System.out.println(string);
//5. 发送数据"好的,明天见"
data = "好的,明天见".getBytes();
packet = new DatagramPacket(data,data.length,InetAddress.getByName("192.168.1.104"),9998);
socket.send(packet);
//6. 关闭流
socket.close();
System.out.println("接收端结束");
}
@Test
public void SenderB() throws IOException {
//1. 创建一个DatagramSocket对象,准备在9998端口处接收/发送数据
DatagramSocket socket = new DatagramSocket(9998);
//2. 构建一个DatagramPacket对象,准备发送数据
byte[] data = "hello,明天吃火锅".getBytes();
// 说明:
// 参数三:接收方主机(IP) 参数四:接收方端口号
DatagramPacket packet =
new DatagramPacket(data, data.length, InetAddress.getByName("192.168.1.104"), 9999);
socket.send(packet);
//3. 接收数据
data = new byte[64*1024];
packet = new DatagramPacket(data,data.length);
socket.receive(packet);
//4. 拆包,取出数据并显示
int length = packet.getLength();
data = packet.getData();
String string = new String(data, 0, length);
System.out.println(string);
//5. 关闭流
socket.close();
System.out.println("发送端结束");
}
}
接收端:
hello,明天吃火锅
接收端结束
发送端:
好的,明天见
发送端结束
六、实操:模拟多用户即时通讯系统
特别说明
本文章是个人整理的学习笔记,参考b站韩顺平老师的课程(【零基础 快速学Java】韩顺平 零基础30天学会Java)。老师讲的非常好,有兴趣的可以去看一下。