一、Java网络编程
一)概述
-
计算机网络
- 连接分散计算机设备以实现信息传递的系统。
- 作用是将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统、网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递
-
网络编程的目的
- 使用套接字来达到进程间相互通信
-
网络编程的要素
- IP地址
- 找到一个主机
- 端口号
- 定位到该主机的某个资源
- 网络通信协议,UDP/TCP
- IP地址
-
两种架构
- 网页编程:B/S架构
- 网络编程:TCP/IP C/S架构
-
网络通信的协议
- TCP/IP参考模型
二)IP
-
InetAddress类
-
作用
- 唯一定位网络中一台计算机的位置
- 本机
- 127.0.0.1 或 localhost
-
IP地址的分类
- ipv4/ipv6
- ipv4,4个字节
- ipv6,6个字节
- 广域网/局域网
- 广域网,就是互联网
- 局域网,公司或组织内部自己使用的网络
- ipv4/ipv6
-
域名
- 因为IP地址记忆起来不方便,所以使用域名代替IP地址,便于记忆
-
代码练习
- 使用InetAddress类查询本机及百度的IP地址
import java.net.InetAddress; import java.net.UnknownHostException; public class InetAddressTest { public static void main(String[] args) { try { //查询默认主机(本机)的IP地址并打印 InetAddress address1 = InetAddress.getByName("127.0.0.1");//根据ip地址的到一个ip地址对象 System.out.println(address1);//打印ip对象的存储的ip //查询本机的ip地址并打印 InetAddress localHost1 = InetAddress.getByName("localhost"); System.out.println(localHost1); //查询本机的ip地址并打印 InetAddress localHost2 = InetAddress.getLocalHost(); System.out.println(localHost2); //查询百度的ip地址并打印 InetAddress baiduAddress = InetAddress.getByName("www.baidu.com"); System.out.println(baiduAddress); //常用的一些方法 System.out.println(baiduAddress.getCanonicalHostName());//得到规范的主机名 System.out.println(baiduAddress.getHostAddress());//得到ip地址 System.out.println(baiduAddress.getHostName());//得到域名 } catch (UnknownHostException e) { e.printStackTrace(); } } }
四)端口
-
InetSocketAddress
-
作用
- 表示计算机上的一个程序的进程,不同的进程有不同的端口号,用来区分软件
- 规定TCP协议下有065535个,UDP协议下有065535个。不同协议间端口号可以重复,单个协议间,端口号不能冲突
-
端口分类
- 公有端口
- HTTP:80
- HTTPS:443
- FTP:21
- Telent:23
- 程序注册端口
- MySQL:3306
- Oracel:1521
- 动态、私有端口
- 49152~65535
- 公有端口
-
在DOS窗口查看端口
- netstat -ano
- 查看所有的端口
- netstat -ano|findstr “端口号”
- 查看指定的端口
- tasklist|findstr “端口号”
- 查看指定端口的进程
- netstat -ano
-
代码练习
- 套接字地址的练习
import java.net.InetSocketAddress; public class InetSocketAddressTest {//端口号,套接字地址的练习 public static void main(String[] args) { InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080);//使用默认IP创建套接字地址对象 System.out.println(socketAddress.toString());//打印套接字地址的主机号+端口号 InetSocketAddress localSocketAddress = new InetSocketAddress("local", 8080);//使用本机名创建套接字地址对象 System.out.println(localSocketAddress);//打印套接字地址的主机号+端口号 System.out.println(socketAddress.getAddress());//打印套接字地址的地址 System.out.println(socketAddress.getHostName());//打印套接字地址的主机名 System.out.println(socketAddress.getPort());//打印套接字地址的端口号 } }
五)通信协议
-
作用
- 其实就是一种传输信息的一种约定,实现传输数据与接收数据的规范化
- 涉及到速率、传输码率、代码结构、传输控制等
-
TCP/IP参考模型
-
TCP与UDP对比
-
TCP:用户传输协议
- 类比于打电话,传输双方必须连接通,确认开始之后才能通信
- 稳定,要连接
- 有严格的连接与断开连接:三次握手、四次挥手
三次握手:保证稳定连接 A:我要连接了 B:你连接吧 A:好的,我连接上了 四次挥手:断开连接 A:我要断开连接了 B:你断吧 B:你断开了吗(再次确认一下) A:我已经断开了
- 有明确的客户端和服务端
- 传输完成时断开连接,效率低
-
UDP:用户数据报协议
- 类比于发短信,只管发就行
- 不稳定、不连接
- 客户端与服务端无明确的区分
- 不用等待连接,直接发
-
六)TCP
1. 传输消息
-
服务器端
- 建立服务的端口,ServerSocket类
- 等待用户的连接,accept()方法
- 接受用户的消息,getInputStream()方法
- 代码练习
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class MessageServer {//接收消息的服务器端 public static void main(String[] args) { //定义局部变量 ServerSocket serverSocket = null; Socket socket = null; InputStream inputStream = null; ByteArrayOutputStream outputStream = null; try { serverSocket = new ServerSocket(9999);//创建一个服务器套接字对象,打开对应的端口 while (true) { socket = serverSocket.accept();//得到客户端的连接 inputStream = socket.getInputStream();//得到客户端的输入流 outputStream = new ByteArrayOutputStream();//创建一个字节数组输出流 //将客户端的输入流读取到字节数组输出流中 byte[] bytes = new byte[1024 * 8]; int len; while ((len = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, len); } System.out.println(outputStream); //将输出流中的内容打印到控制台上 } } catch (IOException e) { e.printStackTrace(); } finally {//从下往上依次关闭资源 if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStream != null) { try { inputStream.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(); } } } } }
-
客户端
- 连接服务器,Socket类,需要服务器的ip与端口号
- 发送消息,getOutputStream()方法
- 代码练习
import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; public class MessageClient {//传输消息的客户端 public static void main(String[] args) { InetAddress serverAddress = null;//服务器地址对象 Socket socket = null;//套接字连接对象 OutputStream outputStream = null;//输出流对象 try { serverAddress = InetAddress.getByName("127.0.0.1");//服务器地址对象 int port = 9999;//服务器端口号 socket = new Socket(serverAddress, port);//根据指定的IP地址与端口号创建一个套接字连接 outputStream = socket.getOutputStream();//输出流对象 outputStream.write("客户端发来的消息".getBytes());//客户端发出的消息 } catch (IOException e) { e.printStackTrace(); } finally {//资源释放 if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
2. 文件上传
- 服务器端
- 代码实现
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class FileServer {//文件传输服务器
public static void main(String[] args) {
ServerSocket serverSocket = null;//声明变量
Socket socket = null;
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
serverSocket = new ServerSocket(9090);//创建服务器,打开端口
socket = serverSocket.accept();//得到客户端的连接
inputStream = socket.getInputStream();//得到客户端的文件输入流
outputStream = new FileOutputStream(new File("receive.png"));//创建文件输出流
byte[] bytes = new byte[1024 * 8];//将客户端输入流中的数据读取到输出流中
int len;
while ((len = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
//告知客户端已接受完成
socket.getOutputStream().write("我已经接收完了,你可以断开了".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {//释放资源
try {
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 客户端
- 代码实现
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class FileClient {//文件传输客户端
public static void main(String[] args) {
Socket socket = null;//声明变量
OutputStream outputStream = null;
FileInputStream fileInputStream = null;
InputStream inputStream = null;
ByteArrayOutputStream arrayOutputStream = null;
try {
socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090);//创建与服务器指定端口的连接
outputStream = socket.getOutputStream();//得到连接的输出流
//得到要传输文件的输入流
fileInputStream = new FileInputStream(new File("1575180473779.png"));
//将要传输文件写出到连接的输出流中
byte[] bytes = new byte[1024 * 8];
int len;
while ((len = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
socket.shutdownOutput();//告知服务器,客户端已经传输完了
inputStream = socket.getInputStream();//得到服务器的消息输入流
arrayOutputStream = new ByteArrayOutputStream();//创建输出流
byte[] bytes1 = new byte[1024 * 8];//将服务器的消息输入流写入输出流中
int len1;
while ((len1 = inputStream.read(bytes1)) != -1) {
arrayOutputStream.write(bytes1, 0, len1);
}
System.out.println(arrayOutputStream);//打印输出流
} catch (IOException e) {
e.printStackTrace();
} finally {//关闭资源
try {
arrayOutputStream.close();
inputStream.close();
socket.close();
outputStream.close();
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. Tomcat
七)UDP
- 不需要连接,需要知道对方的Ip地址和端口号
1. 发送消息
- 发送端
- 代码示例
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class MessageSender {//消息发送端
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();//创建一个连接
InetAddress localhost = InetAddress.getByName("127.0.0.1");//连接地址
int port = 9090;//连接端口
String msg = "你好啊,服务器";//发送消息的内容
//将要发送的消息内容、消息长度、接收方ip地址和端口号打包起来
DatagramPacket packet = new DatagramPacket(msg.getBytes(), msg.getBytes().length, localhost, port);
socket.send(packet);//将包发送出去
socket.close();//关闭资源
}
}
- 接收端
- 代码示例
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class MessageReceiver {//消息接收端
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(9090);//将端口号9090开放
byte[] bytes = new byte[1024 * 8];
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);//创建数据接受包
socket.receive(packet);//将端口中接收的数据放进数据接受包中
System.out.println(packet.getAddress().getHostAddress());//打印消息发送方信息
System.out.println(new String(packet.getData()));//打印数据接受包中的消息
socket.close();//关闭连接
}
}
2. 咨询
-
双发都可以收发消息
-
发送方
- 代码练习
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class ConsultingSender {//咨询发送方
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(8080);//创建一个套接字,打开8080端口
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));//从键盘读取数据
while (true) {
synchronized ("A") {
InetSocketAddress socketAddress = new InetSocketAddress("localhost", 9090);//创建连接
//发消息
byte[] senderBytes = reader.readLine().getBytes();//从键盘读取数据,将数据转换为字节
DatagramPacket senderPacket = new DatagramPacket(senderBytes, 0, senderBytes.length, socketAddress);//将要发送的数据打包
socket.send(senderPacket);//发送包
if (reader.readLine().equals("bye")) {//当输入"bye"时,聊天结束
break;
}
//接收消息
byte[] ReceiveBytes = new byte[1024 * 8];
DatagramPacket ReceivePacket = new DatagramPacket(ReceiveBytes, 0, ReceiveBytes.length, socketAddress);//创建自己的接受包
socket.receive(ReceivePacket);//将接收到的包放进自定义的包中
String receiveData = new String(ReceivePacket.getData());//得到包中的数据
System.out.println("医生:\t" + receiveData);//打印包中的数据
if (receiveData.equals("bye")) {
break;
}
}
}
socket.close();//关闭资源
}
}
- 接收方
- 代码练习
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class ConsultingReceiver {//咨询接收方(医生)
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(9090);//打开9090端口
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));//从键盘读取数据
while (true) {
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080);//创建连接
//发送消息
byte[] readerBytes = reader.readLine().getBytes();//得到键盘的输入
DatagramPacket senderPacket = new DatagramPacket(readerBytes, 0, readerBytes.length, socketAddress);//将键盘输入的数据打包
socket.send(senderPacket);//发送包
if (reader.readLine().equals("bye")) {
break;
}
//接收消息
byte[] ReceiveBytes = new byte[1024 * 8];
DatagramPacket ReceivePacket = new DatagramPacket(ReceiveBytes, 0, ReceiveBytes.length, socketAddress);//创建一个接受包,用来接收另一方传来的数据
socket.receive(ReceivePacket);//将接收到的包放进packed包中
String receiveData = new String(ReceivePacket.getData());//得到包中的数据
System.out.println("咨询者:\t" + new String(ReceivePacket.getData()));//打印包中的数据
if (receiveData.equals("bye")) {
break;
}
}
socket.close();
}
}
八)URL
- 作用
- 统一资源定位器,用来定位互联网上的额某一个资源
- 格式
- 协议: //ip协议: 端口/项目名/资源
- 代码练习
- 下载网络上的某一个音频/视频资源
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class URLExercises {//URL练习,使用URL下载网络上的某一个资源
public static void main(String[] args) throws Exception {
//网络资源URL
String fileURL = "https://m701.music.126.net/20191205191532/241ad6725dfd6021a4860765cd9e4b34/jdyyaac/040c/515a/0053/3fac72c8b9f0dd09f05879e7155052fa.m4a";
URL url = new URL(fileURL);//封装网络资源URL
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();//得到URL连接
InputStream inputStream = urlConnection.getInputStream();//得到网络资源的输入流
String finalPath = "C:\\Users\\北风\\Desktop\\像风一样3.m4a";//目标路径
FileOutputStream outputStream = new FileOutputStream(new File(finalPath));//目标路径输出流
//将网络资源输入流写入目标路径输出流
byte[] bytes = new byte[1024 * 8];
int len;
while ((len = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
//释放资源
outputStream.close();
inputStream.close();
urlConnection.disconnect();
}
}