网络编程
1. 概述
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程的目的:通信,交换数据。
想要达到这个效果:1)通过准确定位网络上的一台主机,并定位这个计算机上的某个资源(IP和端口号);2)找到主机后,要实现数据的传输。
2. 网络通信的要素
实现网络通信:1)通信双方的IP和端口号;2)网络通信的协议
3. IP地址
IP地址的作用:唯一定位一台网络上的计算机。特殊的IP地址:127.0.0.1表示本机主机的IP,local host。
IP地址可分为:
- IPv4:由4个字节组成,每个字节的范围是0~255;
- IPv6:由8个字节组成,每个字节由16进制组成:0~ 9和a~f。
根据使用的范围还可以分为:公网和私网(局域网)。
A类:1.0.0.0–127.255.255.255
B类:128.0.0.0–191.255.255.255
C类:192.0.0.0–223.255.255.255
D类:224.0.0.0–239.255.255.255
E类:240.0.0.0–255.255.255.255
InetAddress
在java.net下,此类专注表示Internet协议(IP)地址,但这个类无构造方法,所以不能实例化(new)。
InetAddress类常用的方法,并返回InetAddress对象的结果
InetAddress.getByName();//获得IP地址
InetAddress.getLocalHost();//获得本地主机名和IP地址
InetAddress对象.getHostAddress();//获得IP
InetAddress对象.getCanonicalHostName();//获得规范IP
InetAddress对象.getHostName());//获得域名或者本机电脑的名字
public static void main(String[] args) {
// new InetAddress();//报错,InetAddress类没有构造方法,不能实例化
//解决方法:通过其静态方法返回IP对象
try {
//查询本机的IP地址
InetAddress address1 = InetAddress.getByName("127.0.0.1");//:/127.0.0.1
System.out.println(address1);
InetAddress address2 = InetAddress.getByName("localhost");//:localhost/127.0.0.1
System.out.println(address2);
InetAddress address3 = InetAddress.getLocalHost();//:LAPTOP-LBHGL6O7/10.133.147.66
System.out.println(address3);
//查询百度网址的IP地址
InetAddress address = InetAddress.getByName("www.baidu.com");//:www.baidu.com/36.152.44.96
System.out.println(address);
//IP对象的常用方法
// System.out.println(address.getAddress());//[B@7ef20235,该方法没有用
System.out.println(address.getHostAddress());//36.152.44.96,IP
System.out.println(address.getCanonicalHostName());//36.152.44.96,获得规范的IP
System.out.println(address.getHostName());//www.baidu.com,获得域名或者本机电脑的名字
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
4. 端口Port
端口表示计算机上的一个程序的进程。不同的进程有不同的端口号,以此区分软件。端口被规定为:0-65535的编号,并且分为TCP端口号和UDP端口号,所以端口号有655356 * 2个。TCP端口号和UDP端口号不会存在冲突,但同一个协议下端口号相同会出现冲突。
端口分类:
- 公有端口:0~1023,特殊的协议端口号:HTTP80,HTTPS443,FTP21,Telent23;
- 程序注册端口:1024~49151,主要用于分配给用户或者程序,特殊的协议端口号:Tomcat8080,MySQL3306,Oracle1521;
- 动态、私有端口:49152~65535
常用的DOS 命令:
netstat -ano #查看所有端口
netstat -ano | findstr “端口号” #查看指定的端口号
tasklist | findstr “8696” #查看指定端口的进程
InetSocketAddress
类,包含了一个InetAddress
对象,代表了IP地址和端口号,专门用于socket
网络通信,用于需要IP地址和端口号的场景。
常用的方法:
InetSocketAddress对象.getAddress();//获得全部的:域名/IP:端口号
InetSocketAddress对象.getHostName();//获得IP地址
InetSocketAddress对象.getPort();//获得端口号
public static void main(String[] args) {
//参数:IP,端口号
InetSocketAddress inetSocketAddress1 = new InetSocketAddress("127.0.0.1", 8080);
InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost", 8080);
System.out.println(inetSocketAddress1);//:/127.0.0.1:8080
System.out.println(inetSocketAddress2);//:localhost/127.0.0.1:8080
//常用的方法
System.out.println(inetSocketAddress1.getAddress());//:/127.0.0.1
System.out.println(inetSocketAddress1.getHostName());//127.0.0.1
System.out.println(inetSocketAddress1.getPort());//8080
}
5. 通信协议
协议就是一种约定。
TCP/IP协议簇:实际上是一组协议。其中传输层的TCP和UDP很重要。
TCP:用户传输协议,连接稳定,传输完成后释放连接、效率低,客户端/服务端模式(C/S)。类似于打电话。
UDP:用户数据报协议,不连接,不稳定,直接发送,客户端、服务端:没有明确的界限,属于浏览器/服务器模式(B/S)。类似于发短息。
TCP的三次握手(连接):
A:请求发送数据;
B:接收请求,也请求发送数据;
A:接收请求
—>建成连接
TCP的四次挥手(断开连接):
A:请求断开连接;
B:同意断开连接,但B还可以发送数据;
B:发送完成,请求断开连接
A:同意断开
—>断开连接
TCP实现聊天
客户端:1. 知道服务器的地址:IP+端口号;2. 创建一个socket连接;3. 发送消息(IO流),写数据;4. 关闭资源。
服务端:1. ServerSocket创建一个地址;2. 等待客户端与服务器的连接;3. 读取消息(IO流)4. 管道输出流ByteArrayOutputStream(),写数据;5. 关闭资源。
客户端
public static void main(String[] args) throws Exception {
Socket socket = null;
OutputStream os = null;
//1. 知道服务器的地址:IP+端口号
InetAddress serverIP = InetAddress.getByName("localhost");
int port = 9999;
//2. 创建一个socket连接
socket = new Socket(serverIP, port);
//3. 发送消息(IO流),写数据
os = socket.getOutputStream();
os.write("今天是个好天气".getBytes(StandardCharsets.UTF_8));
//关闭资源
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端:
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
//1. 创建一个地址
serverSocket = new ServerSocket(9999);
//2. 等待客户端与服务器的连接
socket = serverSocket.accept();
//3. 读取消息(IO流)
is = socket.getInputStream();
//管道输出流,写数据
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
if(baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP连接上传文件
创建文件输入流和输出流进行读取和写文件数据。
完善:
- 客户端读取文件,并上传给服务端,上传之后还要通知服务端,socket.shutdownOutput();
- 服务端接收文件,并保存,接收完成之后要通知客户端可以断开连接。
客户端
public static void main(String[] args) throws Exception {
//1. 知道服务器的地址:IP+端口号
InetAddress serverIP = InetAddress.getByName("localhost");
int port = 9000;
//2. 创建一个socket连接
Socket socket = new Socket(serverIP, port);
//3. 创建一个输出流,写文件
OutputStream os = socket.getOutputStream();
//4. 文件输入流,读取文件
FileInputStream fis = new FileInputStream("E:\\code\\JAVACode\\66.jpg");
//5. 写文件
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) != -1){
os.write(bytes, 0, len);
}
//完善
//客户端在上传数据完成之后要通知服务端
socket.shutdownOutput();//通知已经上传完成
//客户端要确定服务器接收完数据,才可以断开连接
InputStream is = socket.getInputStream();//接收,读取数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();//管道流写数据
byte[] bytes2 = new byte[1024];
int len2;
while((len2 = is.read(bytes2)) != -1){
baos.write(bytes2, 0, len2);
}
System.out.println(baos.toString());
//6. 关闭资源
baos.close();
is.close();
fis.close();
os.close();
socket.close();
}
服务端:
public static void main(String[] args) throws Exception {
//1. 创建一个地址
ServerSocket serverSocket = new ServerSocket(9000);
//2. 等待客户端与服务器的连接
Socket accept = serverSocket.accept();//阻塞式监听,会一直等待客户端的连接
//3. 创建一个输入流,读取文件
InputStream is = accept.getInputStream();
//4. 文件输出流,写文件
FileOutputStream fos = new FileOutputStream("E:\\code\\JAVACode\\68.jpg");
//5. 写文件
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1){
fos.write(bytes, 0, len);
}
//完善
//服务端通知客户端,自己接收完数据
OutputStream os = accept.getOutputStream();//写数据
os.write("数据接收完毕,申请断开".getBytes(StandardCharsets.UTF_8));//得到的是byte数组
//6. 关闭资源
os.close();
fos.close();
is.close();
accept.close();
serverSocket.close();
}
UDP实现发送信息:不需要连接
发送端,也为客户端、浏览器,使用DatagramSocket()
创建数据报连接。1.创建一个DatagramSocket数据报连接;2. 建一个包DatagramPacket();3. 发送包;4. 关闭流
public static void main(String[] args) throws SocketException, Exception {
//1.创建一个DatagramSocket数据报连接
DatagramSocket socket = new DatagramSocket();
//2. 建一个包
String msg = "你好!";
//知道服务器的地址
InetAddress ip = InetAddress.getByName("localhost");
int port = 9090;
//DatagramPacket()构造方法的五个参数:字节数组类型的数据,数据的开始和结束的长度,服务器的地址(IP和端口号)
DatagramPacket dgp = new DatagramPacket(msg.getBytes(StandardCharsets.UTF_8), 0, msg.getBytes(StandardCharsets.UTF_8).length, ip, port);
//3. 发送包
socket.send(dgp);
//4. 关闭流
socket.close();
}
接收端:1. 开放端口; 2. 接收数据包;3. 关闭流。
public static void main(String[] args) throws Exception {
//开放端口
DatagramSocket socket = new DatagramSocket(9090);
//接收数据包
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
socket.receive(packet);
//输出包裹来的地址和里面的内容
System.out.println(packet.getAddress().getHostAddress());//127.0.0.1
System.out.println(new String(packet.getData(), 0, packet.getLength()));//你好!
//关闭流
socket.close();
}
UDP实现聊天
发送端
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(8888);
//数据:从控制台读取
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while(true){
//读取
String data = reader.readLine();
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
InetAddress ip = InetAddress.getByName("localhost");
int port = 6666;
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress("localhost",6666));
// DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, ip,port);
//结束接收数据
if(data.equals("bye")){
break;
}
socket.send(packet);
}
socket.close();
}
接收端
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6666);
while (true){
//接收数据报
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
socket.receive(packet);//阻塞式接收包裹
byte[] data = packet.getData();
String data1 = new String(data, 0, data.length);
//打印接收到的数据
System.out.println(data1);
//当接收到bye,断开连接
if(data1.equals("bye")){
break;
}
}
socket.close();
}
UDP通过多线程实现互相聊天
发送端:
public class ChatSendDemo01 implements Runnable{
DatagramSocket socket = null;
BufferedReader reader = null;
private int fromPort;//发送方的端口号
private String toIP;//接收端的ip
private int toPort;//接收端的端口号
//构造方法
public ChatSendDemo01(int fromPort, String toIP, int toPort){
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
//socket连接
try {
socket = new DatagramSocket(fromPort);
//数据:从控制台读取
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
//读取
String data = null;
try {
data = reader.readLine();
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(toIP, toPort));
//结束接收数据
if (data.equals("bye")) {
break;
}
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
接收端:
public class ChatReceiveDemo01 implements Runnable{
DatagramSocket socket = null;
private int port;
private String msgFrom;
public ChatReceiveDemo01(int port, String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
socket = new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
//接收数据报
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
socket.receive(packet);//阻塞式接收包裹
byte[] data = packet.getData();
String data1 = new String(data, 0, data.length);
//打印接收到的数据
System.out.println(msgFrom + ":" + data1);
//当接收到bye,断开连接
if(data1.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
public class ChatTeacher01 {
public static void main(String[] args) {
//开启两个线程
new Thread(new ChatSendDemo01(5555, "localhost",8888)).start();
new Thread(new ChatReceiveDemo01(9999, "老师")).start();
}
}
public class ChatStudent01 {
public static void main(String[] args) {
//开启两个线程
new Thread(new ChatSendDemo01(7777, "localhost", 9999)).start();
new Thread(new ChatReceiveDemo01(8888, "学生")).start();
}
}
6. URL
统一资源地位符:定位资源,定位互联网上的某一个资源。
定义:协议://ip:端口号/项目名/资源
DNS域名解析:将URL解析为IP地址。
URL常用的方法
url.getProtocol();//获得url的协议
url.getHost();//获得IP
url.getPort();//获得端口号
url.getPath();//获得文件地址/hello/world.html
url.getFile();//获得文件全路径/hello/world.html?username=ran
url.getQuery();//获得参数username=ran
通过URL下载网络资源:
public static void main(String[] args) throws IOException {
//1. 下载地址
URL url = new URL("https://blog.csdn.net/qq_44826240/article/details/126732522?spm=1001.2014.3001.5501");
//2. 连接资源
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
//3. 下载
InputStream is = urlConnection.getInputStream();
byte[] bytes = new byte[1024];
int len;
FileOutputStream fos = new FileOutputStream("E:\\code\\JAVACode\\url.txt");
while ((len = is.read(bytes)) != -1){
fos.write(bytes, 0, len);
}
//4.关闭资源
fos.close();
is.close();
urlConnection.disconnect();
}