Java网络通信协议、UDP、TCP类加载整理

时间:2023-12-16 08:23:50

网络通信协议

网络通信协议

网络通信协议有很多种,目前应用最广泛的是TCP/IP协议(Transmission Control Protocal/Internet Protoal传输控制协议/英特网互联协议),它是一个包括TCP协议和IP协议,UDP(User Datagram Protocol)协议和其它一些协议的协议组,在学习具体协议之前首先了解一下TCP/IP协议组的层次结构。

Java网络通信协议、UDP、TCP类加载整理

1.1      InetAddress

Java网络通信协议、UDP、TCP类加载整理

package com.oracle.InetAddress;

import java.net.InetAddress;

import java.net.UnknownHostException;

public class Demo01 {

public static void main(String[] args) throws UnknownHostException {

//获取本机地址

InetAddress inet=InetAddress.getLocalHost();

System.out.println(inet);

//根据主机名获取inet对象

/*  InetAddress inet1=InetAddress.getByName("DESKOP-NR4E1C5");

//获取主机名

System.out.println(inet1.getHostName());*/

//获取ip地址

String ip=inet.getHostAddress();

System.out.println(ip);

}

}

运行结果:

Java网络通信协议、UDP、TCP类加载整理

UDP与TCP协议

在介绍TCP/IP结构时,提到传输层的两个重要的高级协议,分别是UDP和TCP,其中UDP是User Datagram Protocol的简称,称为用户数据报协议,TCP是Transmission Control Protocol的简称,称为传输控制协议。

2.1      UDP协议

UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。

但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。

UDP传输数据被限制在64K以内

Java网络通信协议、UDP、TCP类加载整理

2.2      TCP协议

TCP协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。

Java网络通信协议、UDP、TCP类加载整理

由于TCP协议的面向连接特性,它可以保证传输数据的全性安,所以是一个被广泛采用的协议,例如在下载文件时,如果数据接收不完整,将会导致文件数据丢失而不能被打开,因此,下载文件时必须采用TCP协议。

UDP通信

3.1      DatagramPacket

UDP是一种面向无连接的协议,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据

接下来根据API文档的内容,对DatagramPacket的构造方法进行逐一详细地讲解。

Java网络通信协议、UDP、TCP类加载整理

使用该构造方法在创建DatagramPacket对象时,指定了封装数据的字节数组和数据的大小,没有指定IP地址和端口号。很明显,这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。

Java网络通信协议、UDP、TCP类加载整理

用该构造方法在创建DatagramPacket对象时,不仅指定了封装数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。该对象通常用于发送端,因为在发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址一样。

面我们讲解了DatagramPacket的构造方法,接下来对DatagramPacket类中的常用方法进行详细地讲解,

Java网络通信协议、UDP、TCP类加载整理

3.2      DatagramSocket

DatagramPacket数据包的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包,发送数据的过程如下图所示。

Java网络通信协议、UDP、TCP类加载整理

在创建发送端和接收端的DatagramSocket对象时,使用的构造方法也有所不同,下面对DatagramSocket类中常用的构造方法进行讲解。

Java网络通信协议、UDP、TCP类加载整理

该构造方法用于创建发送端的DatagramSocket对象,在创建DatagramSocket对象时,并没有指定端口号,此时,系统会分配一个没有被其它网络程序所使用的端口号。

Java网络通信协议、UDP、TCP类加载整理

该构造方法既可用于创建接收端的DatagramSocket对象,又可以创建发送端的DatagramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个端口号,这样就可以监听指定的端口。

Java网络通信协议、UDP、TCP类加载整理

3.3      UDP网络程序

Java网络通信协议、UDP、TCP类加载整理

要实现UDP通信需要创建一个发送端程序和一个接收端程序,很明显,在通信时只有接收端程序先运行,才能避免因发送端发送的数据无法接收,而造成数据丢失。因此,首先需要来完成接收端程序的编写。

package com.oracle.InetAddress;

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetAddress;

import java.util.Scanner;

//UDPend协议发送端

public class UDPend {

public static void main(String[] args) throws IOException {

Scanner  sc=new Scanner(System.in);

String str=sc.next();

//1.创建数据包对象,封装要发送的信息、接收端的ip以及端口号

byte[] bytes=str.getBytes();

InetAddress inet=InetAddress.getByName("127.0.0.1");

DatagramPacket dp=new DatagramPacket(bytes, bytes.length,inet,8888);

//2.创建DatagramSocket对象

DatagramSocket ds=new DatagramSocket();

//3.发送数据包

ds.send(dp);

//4.释放资源

ds.close();

}

}

package com.oracle.InetAddress;

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.SocketException;

//UDPRecieve接收端

public class UDPRecieve {

public static void main(String[] args) throws IOException {

//创建DatagramSocket对象明确的端口号

DatagramSocket ds=new DatagramSocket(8888);

//创建字节数组接收数据

byte[] bytes=new byte[1024];

//创建数据包对象

DatagramPacket dp=new DatagramPacket(bytes,bytes.length);

//接收数据包

ds.receive(dp);

//拆包

//获取发送端的ip地址

String ip=dp.getAddress().getHostAddress();

//获取发送端的端口号

int post=dp.getPort();

//获取实际数据的长度

int length=dp.getLength();

System.out.println("ip地址为:"+ip+",端口号为:"+post+",发送内容为:"+new String(bytes,0,length));

//关闭资源

ds.close();

}

}

TCP通信

TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建socket对象。

区别

UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据

而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。

在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。

4.1      ServerSocket

JDK的java.net包中提供了一个ServerSocket类,该类的实例对象可以实现一个服务器段的程序。通过查阅API文档可知,ServerSocket类提供了多种构造方法

Java网络通信协议、UDP、TCP类加载整理

使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上(参数port就是端口号)。

ServerSocket的常用方法:

Java网络通信协议、UDP、TCP类加载整理

ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。

4.2      Socket

查阅API文档可知Socket类同样提供了多种构造方法

Java网络通信协议、UDP、TCP类加载整理

使用该构造方法在创建Socket对象时,会根据参数去连接在指定地址和端口上运行的服务器程序,其中参数host接收的是一个字符串类型的IP地址。

Java网络通信协议、UDP、TCP类加载整理

该方法在使用上与第二个构造方法类似,参数address用于接收一个InetAddress类型的对象,该对象用于封装一个IP地址。

在以上Socket的构造方法中,最常用的是第一个构造方法。

Socket的常用方法:

方法声明

功能描述

int getPort()

该方法返回一个int类型对象,该对象是Socket对象与服务器端连接的端口号

InetAddress getLocalAddress()

该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成InetAddress类型的对象返回

void close()

该方法用于关闭Socket连接,结束本次通信。在关闭socket之前,应将与socket相关的所有的输入/输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资源

InputStream getInputStream()

该方法返回一个InputStream类型的输入流对象,如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,反之,用于读取服务器端发送的数据

OutputStream getOutputStream()

该方法返回一个OutputStream类型的输出流对象,如果该对象是由服务器端的Socket返回,就用于向客户端发送数据,反之,用于向服务器端发送数据

在Socket类的常用方法中,getInputStream()和getOutStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信。

Java网络通信协议、UDP、TCP类加载整理

4.3      简单的TCP网络程序

/*

* 实现TCP 服务器程序

* 表示服务器程序的类 java.net.ServerSocket

* 构造方法:

*     ServerSocket(int port)传递端口号

*     很重要的事情:必须要获得客户端的套接字对象Socket

*     Socket accept()

*

* 1,创建服务器ServerSocket对象(指定服务器端口号)

* 2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象

* 3,给客户端反馈信息

* 4,关闭流资源

*/

//服务器程序

package com.oracle.Tcp;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.ServerSocket;

import java.net.Socket;

public class TCPServer {

public static void main(String[] args) throws IOException {

//创建ServiceScoket对象,接收客户端

ServerSocket server=new ServerSocket(8080);

//调用accept方法,获取连接我的客户端的Socket对象

Socket socket=server.accept();

//获取输入字节流

InputStream in=socket.getInputStream();

byte[] bytes=new byte[1024];

int len=in.read(bytes);

String ip=socket.getLocalAddress().getHostAddress();

int port=socket.getPort();

System.out.println("客户端地址为:"+ip+",端口号为:"+port+"发送内容为:"+new String(bytes,0,len));

//回复客户端

OutputStream out=socket.getOutputStream();

out.write("哈哈".getBytes());

//释放资源

server.close();

}

}

/*

* 实现TCP 客户端,连接到服务器

* 和服务器实现数据交换

* 实现TCP客户端程序的类 java.net.Soket

* 构造方法

*    Socket(String host,int port) 传递服务器IP和端口号

*    注意:构造方法只要运行,就会和服务器进行连接,连接失败,抛出异常

*    OutputStream   getOutputStream() 返回套接字的输出流

*    作用:将数据输出,输出到服务器

*    InputStream   getInputStream() 返回套接字的输入流

*    作用:从服务器端读取数据

*    客户端服务器数据交换,必须使用套接字对象Socket中的获取的IO流,自己new流,不行

* 1,创建客户端Socket对象,(指定要连接的服务器地址与端口号)

* 2,获取服务器端的反馈回来的信息

* 3,关闭流资源

*/

//客户端程序

package com.oracle.Tcp;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

public class TCPClient {

public static void main(String[] args) throws IOException {

//创建Socket对象,连接服务器

Socket scoket=new Socket("127.0.0.1",8080);

//获取输出字节流

OutputStream out=scoket.getOutputStream();

//发送数据

out.write("嘤嘤嘤".getBytes());

//接收服务器端的回复

InputStream in=scoket.getInputStream();

byte[] bytes=new byte[1024];

String ip=scoket.getLocalAddress().getHostAddress();

int port=scoket.getPort();

int len=in.read(bytes);

System.out.println("服务端ip地址为:"+ip+",端口号为:"+port+",内容为:"+new String(bytes,0,len));

scoket.close();

}

}

4.4      文件上传案例多线程版本

目前大多数服务器都会提供文件上传的功能,由于文件上传需要数据的安全性和完整性,很明显需要使用TCP协议来实现。接下来通过一个案例来实现图片上传的功能

Java网络通信协议、UDP、TCP类加载整理

Java网络通信协议、UDP、TCP类加载整理

//服务器程序

package com.oracle.WenJian;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Random;

public class TCPServer {

public static void main(String[] args) throws IOException {

//创建ServerSocket对象,接收客户端

ServerSocket server=new ServerSocket(8080);

//调用accpt方法 创建连接并获取客户端socket对象

Socket socket=server.accept();

//获取字节输入流

InputStream in=socket.getInputStream();

//明确目的地

File file=new File("E:\\test1");

if(!file.exists()){

file.mkdirs();

}

//文件路径

//文件名:域名+毫秒值+6位随机数

String filename="oracle"+System.currentTimeMillis()+new Random().nextInt(999999);

String path=file.getAbsolutePath()+File.separator+filename+".png";

FileOutputStream fos=new FileOutputStream(path);

//开始复制

byte[] bytes=new byte[1024];

int len=0;

while ((len=in.read(bytes))!=-1) {

fos.write(bytes,0,len);

}

//回复客户端

OutputStream out=socket.getOutputStream();

out.write("上传成功!!".getBytes());

//释放资源

fos.close();

server.close();

}

}

//客户端程序

package com.oracle.WenJian;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

public class TCPClient {

public static void main(String[] args) throws IOException {

//创建Socket对象,连接服务器

Socket socket=new Socket("192.168.1.171",8080);

//获取字节流输出

OutputStream out=socket.getOutputStream();

//创建字节输入流,明确数据源

FileInputStream fis=new FileInputStream("E:\\test\\b.png");

byte[] bytes=new byte[1024];

//开始复制

int len=0;

while ((len=fis.read(bytes))!=-1) {

out.write(bytes,0,len);

}

//告诉服务器 添加一个结束标记

socket.shutdownOutput();

//接收服务器的回复

InputStream in=socket.getInputStream();

len=in.read(bytes);

System.out.println(new String(bytes,0,len));

//释放资源

fis.close();

socket.close();

}

}

类加载器

5.1      类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

加载

就是指将class文件读入内存,并为之创建一个Class对象。

任何类被使用时系统都会建立一个Class对象

连接

验证 是否有正确的内部结构,并和其他类协调一致

准备 负责为类的静态成员分配内存,并设置默认初始化值

解析 将类的二进制数据中的符号引用替换为直接引用

初始化

就是我们以前讲过的初始化步骤

5.2      类初始化时机

1. 创建类的实例

2. 类的静态变量,或者为静态变量赋值

3. 类的静态方法

4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

5. 初始化某个类的子类

6. 直接使用java.exe命令来运行某个主类

5.3      类加载器

负责将.class文件加载到内存中,并为之生成对应的Class对象。

虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行

5.4      类加载器的组成

Bootstrap ClassLoader 根类加载器也被称为引导类加载器,负责Java核心类的加载

比如System,String等。在JDK中JRE的lib目录下rt.jar文件中Extension ClassLoader 扩展类加载器

负责JRE的扩展目录中jar包的加载。

在JDK中JRE的lib目录下ext目录

System ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

通过这些描述就可以知道我们常用的类,都是由谁来加载完成的。