Java网络编程
本文章基于B站视频教程进行整理记录,仅用于个人学习/交流使用。
视频教程1:https://www.bilibili.com/video/BV1Kb411W75N
视频教程2:https://www.bilibili.com/video/BV1LJ411z7vY
参考资料:https://blog.csdn.net/PorkBird/article/details/113727633
01、网络编程概述
-
Java是Internet 上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。
-
Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在Java 的本机安装系统里,由JVM 进行控制。并且Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
-
计算机网络:
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
-
网络编程的目的:
直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯。
-
网络编程中有两个主要的问题:
- 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
- 找到主机后如何可靠高效地进行数据传输
02、网络通信要素概述
- 通信双方地址
- IP
- 端口号
- 一定的规则(即:网络通信协议。有两套参考模型)
- OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
- 网络通信协议
/**
* 一、网络编程中有两个主要的问题:
* 1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
* 2.找到主机后如何可靠高效地进行数据传输
*
* 二、网络编程中的两个要素:
* 1.对应问题一:IP和端口号
* 2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)
*/
03、IP和端口号
IP地址
- IP 地址:
InetAddress
- 唯一的标识Internet 上的计算机(通信实体)
- 本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
- IP地址分类方式1:IPV4和IPV6
- IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1
- IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
- IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)。192.168.开头的就是私有地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用
- 特点:不易记忆
- Internet上的主机有两种方式表示地址:
- 域名(hostName):www.atguigu.com
- IP地址(hostAddress):202.108.35.210
InetAddress
类主要表示IP地址,两个子类:Inet4Address、Inet6Address
。InetAddress
类对象含有一个Internet主机地址的域名和IP地址:www.atguigu.com和202.108.35.210。- 域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。-------域名解析
几个重点:
1.网络编程中的两个要素:IP和端口号
2.端口号与IP地址的组合得出一个网络套接字:Socket
InetAddress类
/**
* 一、网络编程中有两个主要的问题:
* 1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
* 2.找到主机后如何可靠高效地进行数据传输
*
* 二、网络编程中的两个要素:
* 1.对应问题一:IP和端口号
* 2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)
*
*
* 三、通信要素一:IP和端口号
*
* 1. IP:唯一的标识 Internet 上的计算机(通信实体)
* 2. 在Java中使用InetAddress类代表IP
* 3. IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
* 4. 域名: www.baidu.com www.mi.com www.sina.com www.jd.com
* www.vip.com
* 5. 本地回路地址:127.0.0.1 对应着:localhost
*
* 6. 如何实例化InetAddress:两个方法:getByName(String host) 、 getLocalHost()
* 两个常用方法:getHostName() / getHostAddress()
*
* 7. 端口号:正在计算机上运行的进程。
* 要求:不同的进程有不同的端口号
* 范围:被规定为一个 16 位的整数 0~65535。
*
* 8. 端口号与IP地址的组合得出一个网络套接字:Socket
*/
public class TestInetAddress {
public static void main(String[] args) throws UnknownHostException {
//查询本机的IP地址
InetAddress localhost = InetAddress.getByName("localhost");
System.out.println(localhost);//localhost/127.0.0.1
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);//DESKTOP-N6TR8E0/192.168.228.1
//查询网站的IP地址
InetAddress name = InetAddress.getByName("www.baidu.com");
System.out.println(name);//www.baidu.com/180.101.49.11
//其他常用方法
//0.返回此 InetAddress对象的原始IP地址。
System.out.println(Arrays.toString(name.getAddress()));//[-76, 101, 49, 12]
//1.获取主机IP地址
System.out.println(name.getHostAddress());//180.101.49.11
//2.获取域名
System.out.println(name.getHostName());//www.baidu.com
//3.获取规范的主机IP地址
System.out.println(name.getCanonicalHostName());//180.101.49.11
}
}
端口号
- 端口号标识正在计算机上运行的进程(程序)
- 不同的进程有不同的端口号
- 被规定为一个16 位的整数
0~65535
。 - 端口分类:
- 公认端口:
0~1023
。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23) - 注册端口:
1024~49151
。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。 - 动态/私有端口:
49152~65535
。
- 公认端口:
- 端口号与IP地址的组合得出一个网络套接字:
Socket
。
04、网络协议
-
网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
-
问题:网络协议太复杂
计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?
-
通信协议分层的思想
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
TCP和UDP
- 传输层协议中有两个非常重要的协议:
- 传输控制协议TCP(Transmission Control Protocol)
- 用户数据报协议UDP(User Datagram Protocol)。
- TCP/IP 以其两个主要协议:传输控制协议(TCP)和网络互联协议(IP)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
- IP(Internet Protocol)协议是网络层的主要协议,支持网间互连的数据通信。
- TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层、IP层、传输层和应用层。
- 使用TCP协议前,须先建立TCP连接
,形成传输数据通道
- 传输前,采用`“三次握手”`方式,点对点通信,是可靠的
- TCP协议进行通信的两个应用进程:客户端、服务端。
- 在连接中可进行大数据量的传输传输完毕,需释放已建立的连接,效率低
UDP协议:- 将数据、源、目的封装成数据包,不需要建立连接
- 每个数据报的大小限制在64K内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送
- 发送数据结束时无需释放资源,开销小,速度快
TCP协议:
- 使用TCP协议前,须先建立TCP连接,形成传输数据通道
- 传输前,采用
“三次握手”
方式,点对点通信,是可靠的 - TCP协议进行通信的两个应用进程:客户端、服务端。
- 在连接中可进行大数据量的传输传输完毕,需释放已建立的连接,效率低
UDP协议:
- 将数据、源、目的封装成数据包,不需要建立连接
- 每个数据报的大小限制在64K内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送
- 发送数据结束时无需释放资源,开销小,速度快
- 测试 测试测试
title,value,callBack可以缺省
05、TCP网络编程
单向消息通信
客户端发送信息给服务端,服务端将数据显示在控制台上
客户端
public class TcpClientDemo01 {
public static void main(String[] args) throws IOException {
//1.要连接的 服务器地址+端口
InetAddress serverIp = InetAddress.getByName("localhost");
int port = 9999;
//2.创建连接
Socket socket = new Socket(serverIp, port);
//3.发生消息 IO流 [客户端往服务器传递数据,客户端是输出流]
OutputStream os = socket.getOutputStream();
os.write("helloworld,你好世界".getBytes());
//4.关闭资源
os.close();
socket.close();
}
}
服务端
public class TcpServiceDemo01 {
public static void main(String[] args) throws IOException {
//1.地址localhost,指明自己的端口9999
ServerSocket serverSocket=new ServerSocket(9999);
//2.等待客户端连接
Socket socket = serverSocket.accept();
//3.读取数据
InputStream is = socket.getInputStream();
//4.管道流处理数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff=new byte[1024];
int len=-1;
while ((len=is.read(buff))!=-1){
baos.write(buff,0,len);
}
System.out.println(baos.toString());
//5.获取客户端的信息
System.out.println("信息来自:");
System.out.println(socket.getInetAddress().getHostAddress());
//6.关闭资源
baos.close();
is.close();
socket.close();
serverSocket.close();
}
}
传输文件
客户端发送文件给服务端,服务端将文件保存在本地。
服务器端
public class TcpServerDemo2 {
public static void main(String[] args) throws IOException {
//1.建立服务
ServerSocket serverSocket = new ServerSocket(9999);
//2.监听客户端连接
Socket accept = serverSocket.accept();
//3.获取输入流
InputStream is = accept.getInputStream();
//4.文件输出
FileOutputStream fos = new FileOutputStream(new File("F:\\newpng.png"));
byte[] buff=new byte[1024];
int len;
while ((len=is.read(buff))!=-1){
fos.write(buff,0,len);
}
//5.关闭资源
fos.close();
is.close();
accept.close();
serverSocket.close();
}
}
客户端
public class TcpClientDemo2 {
public static void main(String[] args) throws IOException {
//1.建立连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
//2.创建输出流
OutputStream os = socket.getOutputStream();
//3.读取文件
FileInputStream fis=new FileInputStream(new File("F:\\png1.png"));
//4.写文件
byte[] buff=new byte[1024];
int len;
while ((len=fis.read(buff))!=-1){
os.write(buff,0,len);
}
//5.关闭流
fis.close();
os.close();
socket.close();
}
}
传输文件带通知
从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端
服务器端
public class TcpServerDemo2 {
public static void main(String[] args) throws IOException {
//1.建立服务
ServerSocket serverSocket = new ServerSocket(9999);
//2.监听客户端连接
Socket accept = serverSocket.accept();
//3.获取输入流
InputStream is = accept.getInputStream();
//4.文件输出
FileOutputStream fos = new FileOutputStream(new File("F:\\newpng.png"));
byte[] buff=new byte[1024];
int len;
while ((len=is.read(buff))!=-1){
fos.write(buff,0,len);
}
//5.服务器端给客户端反馈
OutputStream os = accept.getOutputStream();
os.write("我接收完毕了,你可以断开了".getBytes());
//6.关闭资源
os.close();
fos.close();
is.close();
accept.close();
serverSocket.close();
}
}
客户端
public class TcpClientDemo2 {
public static void main(String[] args) throws IOException {
//1.建立连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
//2.创建输出流
OutputStream os = socket.getOutputStream();
//3.读取文件
FileInputStream is=new FileInputStream(new File("F:\\png1.png"));
//4.写文件
byte[] buff=new byte[1024];
int len;
while ((len=is.read(buff))!=-1){
os.write(buff,0,len);
}
//5.【关闭数据的输出】通知服务器,我己经结束了
socket.shutdownOutput();
//6.确认服务器接收完毕,才能断开连接
//接收服务器端的数据,并显示在控制台上
InputStream inputStream=socket.getInputStream();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
byte[] buffer=new byte[1024];
int len2;
while ((len2=inputStream.read(buffer))!=-1){
baos.write(buffer,0,len2);
}
System.out.println("##收到来自服务器的数据:");
System.out.println(baos.toString());
//7.关闭流
baos.close();
inputStream.close();
is.close();
os.close();
socket.close();
}
}
使用Tomcat服务器
1.开启Tomcat服务器
2.在Tomcat服务器下防止资源
3.访问资源
06、UDP编程
类DatagramSocket和DatagramPacket实现了基于UDP 协议网络程序。
UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。如同发快递包裹一样。
流程:
- DatagramSocket与DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用Socket的发送、接收方法
- 关闭Socket
发送消息
public class UdpClientDemo1 {
public static void main(String[] args) throws IOException {
//1.建立
DatagramSocket socket=new DatagramSocket();
//2.建个包
String msg="hello,world,你好,世界";
//3.发送给?
InetAddress address = InetAddress.getByName("127.0.0.1");
int port=9999;
int len=msg.getBytes().length;
//参数:发送的消息,开始位置,结束位置,发送的地址,发送的端口
DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,len,address,port);
//4.发送包
socket.send(packet);
}
}
public class UdpServerDemo1 {
public static void main(String[] args) throws IOException {
//1.开放的端口
DatagramSocket socket = new DatagramSocket(9999);
//2.接收数据包
byte[] buff=new byte[1024];
DatagramPacket packet = new DatagramPacket(buff, 0, buff.length);
//3.阻塞接收 [没有收到数据则会一直阻塞]
socket.receive(packet);
System.out.println(packet.getAddress());//发送方的地址
System.out.println(packet.getPort());//发送方的端口
System.out.println(new String(packet.getData(),0,packet.getLength()));
//关闭资源
socket.close();
}
}
单向通信
public class UdpSenderDemo01 {
public static void main(String[] args) throws IOException {
//1.建立连接
DatagramSocket socket=new DatagramSocket();
while (true){
//2.准备数据
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
String data=reader.readLine();
byte[] datas=data.getBytes();
//3.发送给?
InetAddress address = InetAddress.getByName("127.0.0.1");
int port=9999;
int len=datas.length;
DatagramPacket packet = new DatagramPacket(datas,0,len,address,port);
//4.发送数据
socket.send(packet);
if (data.equals("bye")){
break;
}
}
//5.关闭
socket.close();
}
}
public class UdpReceiveDemo01 {
public static void main(String[] args) throws IOException {
//1.开放的端口
DatagramSocket socket = new DatagramSocket(9999);
while (true){
//2.接收数据包
byte[] buff=new byte[1024];
DatagramPacket packet = new DatagramPacket(buff, 0, buff.length);
//3.阻塞接收 [没有收到数据则会一直阻塞]
socket.receive(packet);
//4.获取数据
String msg=new String(packet.getData(),0,packet.getLength());
System.out.println(msg);
if (msg.equals("bye")){
break;
}
}
//5.关闭资源
socket.close();
}
}
双向通讯
接收消息的线程
public class TalkReceive implements Runnable{
DatagramSocket socket=null;
private int port;
private String msg;
public TalkReceive(int port, String msg) {
this.port = port;
this.msg = msg;
try {
socket=new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
//2.接收数据包
byte[] buff=new byte[1024];
DatagramPacket packet = new DatagramPacket(buff, 0, buff.length);
//3.阻塞接收
socket.receive(packet);
byte[] data=packet.getData();
String news=new String(packet.getData(),0,packet.getLength());
System.out.println(this.msg+":"+news);
if (news.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//5.关闭资源
socket.close();
}
}
发送消息的线程
public class TalkSend implements Runnable{
DatagramSocket socket=null;
BufferedReader reader=null;
private String toIP;
private int toPort;
public TalkSend(String toIP, int toPort) {
this.toIP = toIP;
this.toPort = toPort;
try {
socket=new DatagramSocket();
reader=new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
String data=reader.readLine();
byte[] datas = data.getBytes();
//3.发送给?
InetSocketAddress socketAddress=new InetSocketAddress(this.toIP,this.toPort);
int len=datas.length;
DatagramPacket packet = new DatagramPacket(datas,0,len,socketAddress);
//4.发送数据
socket.send(packet);
if (data.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
学生
public class TalkStudent1 {
public static void main(String[] args) {
//我是学生
//建立发送链接:我的地址,我的端口号
new Thread(new TalkSend("localhost",7777)).start();
//建立接收老师消息的链接:老师的端口号,老师的姓名备注
new Thread(new TalkReceive(8888,"李老师")).start();
}
}
老师
public class TalkTeacher {
public static void main(String[] args) {
//我是老师
//
new Thread(new TalkSend("localhost",8888)).start();
new Thread(new TalkReceive(9999,"王学生")).start();
}
}
07、URL编程
URL网络编程
1.URL:统一资源定位符,对应着互联网的某一资源地址
2.格式:
http://127.0.0.1:8080/work/hello.jpg?username=zhangsan
协议 主机名 端口号 资源地址 参数列表
URL类
public class demo01 {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("https://i0.hdslb.com/bfs/face/420f6292ac387560709593479588674c6a319db6.jpg");
System.out.println(url.getProtocol());//获取该URL的协议名
System.out.println(url.getHost());// 获取该URL的主机名
System.out.println(url.getPort());//获取该URL的端口号
System.out.println(url.getPath());//获取该URL的文件路径
System.out.println(url.getQuery());//获取该URL的查询名
}
}
URL下载网络资源
public class demo02 {
public static void main(String[] args) throws IOException {
URL url=new URL("https://i0.hdslb.com/bfs/face/420f6292ac387560709593479588674c6a319db6.jpg");
HttpURLConnection urlConnection= (HttpURLConnection) url.openConnection();
urlConnection.connect();
InputStream is = urlConnection.getInputStream();
FileOutputStream fos=new FileOutputStream("hello.png");
byte[] buff=new byte[1024];
int len;
while ((len=is.read(buff))!=-1){
fos.write(buff,0,len);
}
System.out.println("下载完成");
fos.close();
is.close();
}
}