Java是 Internet 上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
本笔记主要记录java的网络编程相关的API,对通信原理细节不做深究。
IP 地址,唯一标识 Internet 上的一台计算机。
InetAddress 类对象含有一个 Internet 主机地址的域名和IP地址,两个子类Inet4Address、Inet6Address。
InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取InetAddress实例。
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.junit.jupiter.api.Test;
public class TestInetAddress {
public static void main(String[] args) throws UnknownHostException {
InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress); // www.baidu.com/183.232.231.173
System.out.println(inetAddress.getHostName()); // www.baidu.com
System.out.println(inetAddress.getHostAddress()); // 183.232.231.173
InetAddress inetAddress2 = InetAddress.getByName("183.232.231.172");
System.out.println(inetAddress2); // /183.232.231.172
System.out.println(inetAddress2.getHostName()); // 183.232.231.172
System.out.println(inetAddress2.getHostAddress()); // 183.232.231.172
InetAddress inetAddress3 = InetAddress.getLocalHost();
System.out.println(inetAddress3); // DESKTOP-0CBDASS/192.168.111.1
System.out.println(inetAddress3.getHostName()); // DESKTOP-0CBDASS
System.out.println(inetAddress3.getHostAddress()); // 192.168.111.1
String[] ipSplit = "183.232.231.172".split("\\.");
byte[] bytes = new byte[] {(byte) Short.parseShort(ipSplit[0]), (byte) Short.parseShort(ipSplit[1]),
(byte) Short.parseShort(ipSplit[2]), (byte) Short.parseShort(ipSplit[3])};
InetAddress inetAddress4 = InetAddress.getByAddress(bytes);
System.out.println(inetAddress4); // /183.232.231.172
System.out.println(inetAddress4.getHostName()); // 183.232.231.172
System.out.println(inetAddress4.getHostAddress()); // 183.232.231.172
}
}
网络通信其实就是Socket间的通信。TCP的Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
package internet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import org.junit.jupiter.api.Test;
public class TestTCP2 {
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
InputStream is = null;
try {
// 1.创建一个Socket的对象,通过构造器指明服务端的IP地址,以及其接收程序的端口号
socket = new Socket(InetAddress.getByName("localhost"), 9090);
// 2.getOutputStream():方法返回OutputStream的对象
os = socket.getOutputStream();
// 3.具体的输出过程
os.write("来自客户端的数据".getBytes());
socket.shutdownOutput(); // 显式的告诉服务端发送完毕
// 4.接收来自服务端的数据
is = socket.getInputStream();
byte[] b = new byte[20];
int len;
while ((len = is.read(b)) != -1) {
String str = new String(b, 0, len);
System.out.print(str);
}
} catch (Exception e) {
// TODO: handle exception
} finally {
// 5.关闭流和socket
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
OutputStream os = null;
try {
// 1.创建一个ServerSocket的对象,通过构造器指明自身的端口号
ss = new ServerSocket(9090);
// 2.调用其accept()方法,返回一个Socket的对象,此时与客户端建立连接
socket = ss.accept();
// 3.调用Socket对象的getInputStream()获取一个从客户端发送过来的输入流
is = socket.getInputStream();
// 4.对获取的输入流进行的操作
byte[] b = new byte[20];
int len;
while((len = is.read(b)) != -1) {
String str = new String(b, 0, len);
System.out.print(str);
}
System.out.println("接收完毕");
// 5.向客户端发送数据
os = socket.getOutputStream();
os.write("来自服务端的数据:收到".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5.关闭相应的流以及Socket、ServerSocket的对象
if (os != null) {
try {
os.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();
}
}
}
}
}
总结:TCP的一个socket由源地址、源端口、目的地址、目的端口唯一确定,在客户端创建一个Socket对象时,需指定服务器的ip地址和端口,本身的ip地址和端口会自动生成,有了socket之后,就可以从中获取InputStream或OutputStream,以进行接收数据和发送数据的操作。由于TCP是面向连接的,在服务端要先创建一个ServerSocket对象,以”接待“所有发起连接的客户,并调用其accept方法为每个连接返回一个指定的Socket,这样每个客户在服务端就唯一对应一个Socket,并与这个Socket进行通信。
类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序。UDP数据报通过数据报套接字DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
package internet;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import org.junit.jupiter.api.Test;
public class TestUDP {
@Test
public void send() {
DatagramSocket ds = null;
try {
// 创建DatagramSocket对象
ds = new DatagramSocket();
// 创建DatagramPacket数据包,每一个数据报不能大于64k
byte[] b = "来自客户端的数据".getBytes();
DatagramPacket packet = new DatagramPacket(b, 0, b.length, InetAddress.getByName("localhost"), 9090);
// 调用DatagramSocket的send方法发送数据包
ds.send(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭DatagramSocket
if (ds != null) {
ds.close();
}
}
}
@Test
public void receive() {
DatagramSocket ds = null;
try {
// 创建DatagramSocket对象,需指明监听的端口
ds = new DatagramSocket(9090);
// 创建DatagramPacket数据包
byte[] b = new byte[1024];
DatagramPacket packet = new DatagramPacket(b, 0, b.length);
// 调用DatagramSocket的receive方法接收数据包
ds.receive(packet);
String string = new String(packet.getData(), 0, packet.getLength());
System.out.println(string);
} catch (IOException e) {
e.printStackTrace();
}finally {
// 关闭DatagramSocket
if (ds != null) {
ds.close();
}
}
}
}
总结:在代码上体现出的UDP与TCP的最大不同,在于TCP是面向连接的,而UDP没有,所以接收端也没有一个ServerSocket,而是直接使用DatagramSocket进行通信;并且也没有流的概念,直接调用方法发送或接收数据,且数据需要先封装成DatagramPacket。
URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上某一资源的地址。通过 URL 我们可以访问 Internet 上的各种网络资源,比如最常见的 www,ftp 站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。
URL的基本结构由5部分组成:
<传输协议>://<主机地址>:<端口号>/<文件路径>
例如: http://192.168.1.100:8080/helloworld/index.jsp
一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的方法来获取这些属性:
URL url = new URL("http://hao.91nzh.com:8080/static/images/jd.jpg?a=b");
System.out.println(url.getPath()); // 路径名:/static/images/jd.jpg
System.out.println(url.getProtocol()); // 协议名:http
System.out.println(url.getHost()); // 主机名: hao.91nzh.com
System.out.println(url.getPort()); // 端口号:8080
System.out.println(url.getFile()); // 文件路径:/static/images/jd.jpg?a=b
System.out.println(url.getRef()); // 该URL在文件中的相对位置:null
System.out.println(url.getQuery()); // 查询名:a=b
URL的方法 openStream():能从网络上读取数据。
若希望输出数据,例如向服务器端的 CGI (公共网关接口-Common Gateway Interface-的简称,是用户浏览器和服务器端的应用程序进行连接的接口)程序发送一些数据,则必须先与URL建立连接,然后才能对其进行读写,此时需要使用 URLConnection 。
URL url = null;
try {
url = new URL("http://hao.91nzh.com/static/images/jd.jpg");
InputStream is = null;
FileOutputStream fos = null;
try {
// 使用openStream()方法获取输入流,从服务端读取数据
is = url.openStream();
fos = new FileOutputStream("jingdong.jpg");
byte[] b = new byte[1024];
int len;
while((len = is.read(b)) != -1) {
fos.write(b, 0, len);
}
//如果既有数据的输入,又有数据的输出,则考虑使用URLConnection
URLConnection urlConn = url.openConnection();
InputStream is1 = urlConn.getInputStream();
OutputStream os = urlConn.getOutputStream();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
...
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
总结:类 URL 和 URLConnection 提供了最高级网络应用。URL 的网络资源的位置来同一表示 Internet 上各种网络资源。通过URL对象可以创建当前应用程序和 URL 表示的网络资源之间的连接,这样当前程序就可以读取网络资源数据,或者把自己的数据传送到网络上去。
以上笔记参考自尚硅谷