网络编程
一、概述
Java是Internet上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在Java的本机安装系统中,由JVM进行控制,并且Java实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
1、计算机网络
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便的互相传递信息、共享硬件、软件、数据信息等资源。
2、网络编程的目的
- 直接或间接的通过网络协议与其他计算机实现数据交换、进行通讯。
3、主要问题
- 如何准确的定位网络上的一台或多台主机,定位主机上的特定的应用
- 找到主机后如何可靠高效的进行数据传输
4、两大要素
针对以上两个问题,分别存在两大要素从而解决以上问题。
-
IP和端口号
-
提供网络通信协议
- OSI 参考模型:共分为7层。模型过于理想化,未能在因特网上进行广泛推广
- TCP/IP 参考模型:共分为4层。TCP/IP协议,事实上的国际标准
二、IP和端口号
1、IP地址
IP地址(InetAddress)能唯一的标识Internet上的计算机(通信实体),其主要有两种分类方式:
- 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;
- 公网地址(万维网使用)和私有地址(局域网使用)
- 192.168. 开头的就是私有址址,范围即为192.168.0.0–192.168.255.255,专门为组织机 构内部使用。
在Java中使用InetAddress类代表IP,实例化InetAddress的两个方法为:
- getByName(String host)
- getLocalHost()
public static void main(String[] args) {
try {
// 通过IP地址获取InetAddress对象
InetAddress inet1 = InetAddress.getByName("192.168.10.14");
System.out.println(inet1);
// 通过域名获取InetAddress对象
InetAddress inet2 = InetAddress.getByName("www.atguigu.com");
System.out.println(inet2);
// 获取本地的InetAddress对象
InetAddress inet3 = InetAddress.getByName("127.0.0.1");
System.out.println(inet3);
//获取本地ip
InetAddress inet4 = InetAddress.getLocalHost();
System.out.println(inet4);
//getHostName():获得InetAddress对象对应的域名
System.out.println(inet2.getHostName());
//getHostAddress():获得InetAddress对象对应的IP地址
System.out.println(inet2.getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
2、端口号
端口号标识正在计算机上运行的进程(程序),不同的进程有着不同的端口号,其被规定为一个16位的整数0-65535.端口分类为:
- 公认端口:0-1023,被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用 端口23)
- 注册端口:1024-49151,分配给用户进程或应用程序(如Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521)
- 动态/私有端口:49152-65535
三、网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对**速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。**那问题来了,计算机网络通信涉及内容很多,比如指定源地址和目标地址。加密解密,压缩解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?
对于上述问题再网络通信协议中提出了通信协议分层思想。在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的符合方式就是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不再发生关系。各层互不影响,利于系统的开发和扩展。
1、TCP/IP协议簇
传输层协议中有两个非常重要的协议:
- 传输控制协议TCP(Transmission Control Protocol)
- 用户数据报协议UDP(User Datagram Protocol)
TCP/IP以其两个主要协议:传输控制协议(TCP)呵呵网络互联协议(IP)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
- IP(Internet Protocol)协议是网络层的主要协议,支持网间互连的数据通信;
- TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层、IP层、传输层和应用层
2、TCP和UDP
2.1、TCP协议
- 使用TCP协议前,需先建立TCP连接,形成传输数据通道;
- 传输前,采用“三次握手”的方式,点对点通信,这是可靠的;
- TCP协议进行通信的两个应用进程:客户端和服务端;
- 在连接中可进行大数据量的传输;
- 传输完毕,需要释放已建立的连接,效率较低。
2.2、TCP编码案例
下面将会以三个不同的案例进行演示,值得注意的是在运行时,需要先运行服务端再运行客户端,这就和你得先有手机才能发短信一样。
客户端
-
创建Socket对象,指明服务器端的IP和端口号
-
获取一个输出流,用于输出数据
-
写出数据的操作
-
关闭资源
/**
* @Author xBaozi
* @Description 客户端
* @Date 23:17 2022/1/2
* @Param []
* @return void
**/
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
// 1. 创建Socket对象,指明服务器端的IP和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet, 8899);
// 2. 获取一个输出流,用于输出数据
os = socket.getOutputStream();
// 3. 写出数据的操作
os.write("你好,我是客户端".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. 资源的关闭
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务端
- 创建服务器端的ServerSocket,指明自己的端口号
- 调用**accept()**表示接受来自于客户端的socket
- 获取输入流
- 读取输入流中的数据
- 关闭资源
/**
* @Author xBaozi
* @Description 服务端
* @Date 23:18 2022/1/2
* @Param []
* @return void
**/
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
// 1. 创建服务器端的ServerSocket,指明自己的端口号
ss = new ServerSocket(8899);
// 2. 调用accept()表示接受来自于客户端的socket
socket = ss.accept();
// 3. 获取输入流
is = socket.getInputStream();
//4.读取输入流中的数据
//不建议这样写,可能会有乱码
// byte[] buffer = new byte[1024];
// int len;
// while((len = is.read(buffer)) != -1){
// String str = new String(buffer,0,len);
// System.out.print(str);
// }
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while((len = is.read(buffer)) != -1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
System.out.println("收到了来自" + socket.getInetAddress().getHostAddress() +
} catch (IOException e) {
e.printStackTrace();
} finally {
if(baos != null){
//5.关闭资源
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(ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端
- 创建Socket对象,指明服务器端的IP和端口号
- 获取一个输出流,用于输出数据
- 获取一个文件流,用于读取文件
- 写出文件的操作
- 关闭资源
/**
* @Author xBaozi
* @Description 客户端
* @Date 15:27 2022/1/3
* @Param []
* @return void
**/
@Test
public void client() throws IOException {
// 1. 创建Socket对象,指明服务器端的IP和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
Socket socket = new Socket(inet, 9090);
// 2. 获取一个输出流,用于输出数据
OutputStream os = socket.getOutputStream();
// 3. 获取一个文件流,用于读取文件
FileInputStream fileInputStream = new FileInputStream(new File("wallhaven-1kqxkg.jpg"));
// 4. 写出文件的操作
byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 5. 关闭资源
fileInputStream.close();
os.close();
socket.close();
}
客户端
- 创建Socket对象,指明服务器端的IP和端口号
- 获取一个输出流,用于输出数据
- 获取一个文件流,用于读取文件
- 写出文件的操作
- 关闭文件的输出
- 接收来自于服务器端的数据,并显示到控制台上
- 关闭资源
/**
* @Author xBaozi
* @Description 客户端
* @Date 16:16 2022/1/3
* @Param []
* @return void
**/
@Test
public void client() throws IOException {
// 1. 创建Socket对象,指明服务器端的IP和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
Socket socket = new Socket(inet, 9090);
// 2. 获取一个输出流,用于输出数据
OutputStream os = socket.getOutputStream();
// 3. 获取一个文件流,用于读取文件
FileInputStream fis = new FileInputStream(new File("wallhaven-1kqxkg.jpg"));
// 4. 写出文件的操作
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 5. 关闭文件的输出
socket.shutdownOutput();
// 6. 接收来自于服务器端的数据,并显示到控制台上
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
// 7. 关闭资源
baos.close();
is.close();
fis.close();
os.close();
socket.close();
}
服务端
- 创建服务器端的ServerSocket,指明自己的端口号
- 调用accept()表示接受来自于客户端的socket
- 获取输入流
- 获取输出流
- 读取输入流中的数据
- 获得一个输出流,用于对客户端的反馈
- 关闭资源
/**
* @Author xBaozi
* @Description 服务端
* @Date 16:16 2022/1/3
* @Param []
* @return void
**/
@Test
public void server() throws IOException {
// 1.创建服务器端的ServerSocket,指明自己的端口号
ServerSocket ss = new ServerSocket(9090);
// 2. 调用accept()表示接受来自于客户端的socket
Socket socket = ss.accept();
// 3. 获取输入流
InputStream is = socket.getInputStream();
// 4. 获取输出流
FileOutputStream fos = new FileOutputStream(new File("sent2.jpg"));
// 5.读取输入流中的数据
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer);
}
System.out.println("图片传输完成");
// 6. 获得一个输出流,用于对客户端的反馈
OutputStream os = socket.getOutputStream();
os.write("图片已收到".getBytes());
// 7. 关闭资源
os.close();
fos.close();
is.close();
socket.close();
ss.close();
}
2.3、UDP协议
- 将数据、源、目的封装成数据包,不需要建立连接;
- 每个数据包的大小限制在64K内;
- 发送不管对方是否准备好,接收方收到也不确定,故是不可靠的;
- 可以广播发送;
- 发送数据结束时无需释放资源,开销小,速度快。
2.4、UDP编码案例
- DatagramSocket与DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用Socket的发送、接收方法
- 关闭Socket
/**
* @Author xBaozi
* @Description 发送端
* @Date 16:45 2022/1/3
* @Param []
* @return void
**/
@Test
public void sender() throws IOException {
// 创建socket对象
DatagramSocket socket = new DatagramSocket();
// 编写需要发送的数据
String str = "我是UDP方式发送的导弹";
byte[] data = str.getBytes();
// 获得本地的IP地址
InetAddress inet = InetAddress.getLocalHost();
// 建立数据包
DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
// 调用Socket的发送方法
socket.send(packet);
// 关闭资源
socket.close();
}
/**
* @Author xBaozi
* @Description 接收端
* @Date 16:45 2022/1/3
* @Param []
* @return void
**/
@Test
public void receiver() throws IOException {
// 创建socket
DatagramSocket socket = new DatagramSocket(9090);
// 建立数据包
byte[] buffer = new byte[100];
DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
// 调用Socket的接收方法
socket.receive(packet);
// 对数据进行打印输出
System.out.println(new String(packet.getData(),0,packet.getLength()));
// 关闭资源
socket.close();
}
3、URL
URL(Uniform Resource Locator)称统一资源定位符,它表示 Internet 上某一 资源的地址。它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate 这个资源。通过 URL 我们可以访问 Internet 上的各种网络资源,比如最常见的 www,ftp 站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。
URL的基本结构由5部分组成:<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
#片段名
:即锚点,例如看小说,直接定位到章节;参数列表格式
:参数名=参数值&参数名=参数值…
为了表示URL,java.net 中实现了类 URL。URL类的构造器都声明抛出非运行时异常,必须要对这一异常进行处理,通 常是用 try-catch 语句进行捕获。下面是URL类中的常用方法
public static void main(String[] args) {
try {
URL url = new URL("http://localhost:8080/examples/hello.txt?username=Tom");
// public String getProtocol( ) 获取该URL的协议名
System.out.println(url.getProtocol());
// public String getHost( ) 获取该URL的主机名
System.out.println(url.getHost());
// public String getPort( ) 获取该URL的端口号
System.out.println(url.getPort());
// public String getPath( ) 获取该URL的文件路径
System.out.println(url.getPath());
// public String getFile( ) 获取该URL的文件名
System.out.println(url.getFile());
// public String getQuery( ) 获取该URL的查询名
System.out.println(url.getQuery());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
同时利用Tomcat本地服务器模拟从网络上下载文件到本地上
@Test
public void test01() {
HttpURLConnection urlConnection = null;
InputStream is = null;
FileOutputStream fos = null;
try {
// 实例化URL对象,提供对应的地址
URL url = new URL("http://localhost:8080/examples/hello.txt");
// 获取一个http协议的URL连接
urlConnection = (HttpURLConnection) url.openConnection();
// 对URL进行连接
urlConnection.connect();
// 获取输入流
is = urlConnection.getInputStream();
// 实例化输出流
fos = new FileOutputStream("hello.txt");
// 将文件从URL地址下载到本地
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
System.out.println("下载完成");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(urlConnection != null){
urlConnection.disconnect();
}
}
}