@[toc]
一、网络编程概述
-
Java是 Internet 上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序
-
Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一 的网络编程环境。
-
计算机网络:把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、 共享硬件、软件、数据信息等资源。
-
网络编程的目的:直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯
-
网络编程中有两个主要的问题
:
-
如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
-
找到主机后如何可靠高效地进行数据传输
二、网络通信要素概述
如何实现网络中的主机互相通信?
:o::IP和端口号 、网络通信协议
-
通信双方地址:
-
一定的规则:(即:网络通信协议。有两套参考模型)
-
OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
-
TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
三、通信要素1: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,专门为组织机构内部使用
-
本地回路地址:127.0.0.1 对应着 :localhost
-
特点:不易记忆
InetAdress类
如何实例化InetAddress
两个常用方法:getHostName()和getHostAddress()
import java.net.InetAddress;
public class java1 {
public static void main(String[] args) throws Exception{
InetAddress inet1 = InetAddress.getByName("192.168.10.14");
System.out.println(inet1);
InetAddress inet2 = InetAddress.getByName("hbt666.top");
System.out.println(inet2);
InetAddress inet3 = InetAddress.getByName("127.0.0.1");//
System.out.println(inet3);
InetAddress localHost = InetAddress.getLocalHost();//查询本机IP地址
System.out.println(localHost);//LAPTOP-B6AL0VIH/192.168.97.136
System.out.println(localHost.getHostName());
System.out.println(localHost.getAddress());
}
端口号
-
端口号
:正在计算机上运行的进程(程序)
-
不同的进程有不同的端口号
-
被规定为一个 16 位的整数 0~65535
-
端口分类:
-
公认端口
:0~1023。被预先定义的服务通信占用(如:HTTP占用端口 80,FTP占用端口21,Telnet占用端口23)
-
注册端口
:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
-
动态/私有端口
:49152~65535。
-
不同的进程要用不同的端口号,不然会端口冲突
-
端口号与IP地址的组合得出一个网络套接字:
Socket
四、通信要素2:网络协议
-
网络通信协议
:计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代 码结构、传输控制步骤、出错控制等制定标准。
-
问题
:
网络协议太复杂
:计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩 解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?
-
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
TCP/IP协议簇
-
传输层协议中有两个非常重要的协议:
-
TCP/IP 以其两个主要协议:传输控制协议(TCP)和网络互联协议(IP)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
-
IP协议是网络层的主要协议,支持网间互连的数据通信
-
TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即 物理链路层、IP层、传输层和应用层。
TCP 和 UDP
三次握手
注:其实是可以进行多次的,但是三次就百分之99可以保证这次通信是有效的了
四次挥手
Socket
TCP网络编程练习题举例
例题1:客户端发送内容给服务端,服务端将内容打印到控制台上
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
实现tcp的网络编程:
例子1:客户端发生送信息给服务端,服务端把信息显示在控制台上
public class TcpTest {
//客户端
@Test
public void client() {
Socket socket = null;//端口号1234
OutputStream os = null;
try {
//1.创建Socket对象,指明服务器端的ip和端口号
InetAddress byName = InetAddress.getByName("127.0.0.01");
socket = new Socket(byName,18899);
//2.获取一个输出流,用于输出数据
os = socket.getOutputStream();
//3.写出数据的操作
os.write("我是客户端".getBytes());
} catch (IOException e) {
e.printStackTrace();
//4.资源的关闭
} finally {
try {
if (os!=null)
os.close();
} catch (IOException e) {
e.printStackTrace();
try {
if (socket!=null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
//服务端(接收来自客户端的数据)
@Test
public void server() throws Exception{
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream aos = null;
try {
//1.创建服务器段的ServerSocket,指明自己的端口号
serverSocket = new ServerSocket(18899);
//2,调用accept()表示接收来自客户端的socket
socket = serverSocket.accept();
//3.获取输入流
is = socket.getInputStream();
//不建议这么写,可能会乱码
byte[] bytes = new byte[200];
int len;
while ((len=is.read(bytes))!=-1){
String string = new String(bytes,0,len);
System.out.println(string);
//4.读取输入流的数据
aos = new ByteArrayOutputStream();
byte[] bytes = new byte[25];
int len;
while ((len=is.read(bytes))!=-1){
aos.write(bytes,0,len);
System.out.println(aos.toString());
System.out.println("收到了来自于:"+socket.getInetAddress().getHostName()+"的数据");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (aos!=null)
aos.close();
} catch (IOException e) {
e.printStackTrace();
try {
if (is!=null)
is.close();
} catch (IOException e) {
e.printStackTrace();
try {
if (socket!=null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
try {
if (serverSocket!=null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
例题2:客户端发送文件给服务端,服务端将文件保存到本地
:o::这里为了代码的观赏性,异常用throws抛出,实际应该和上面一样使用try-catch-finally
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpTest2 {
//客户端
@Test
public void client() throws Exception {
//1.创建socket对象指定服务器的ip和端口号
InetAddress ip1 = InetAddress.getByName("127.0.0.1");
Socket socket = new Socket(ip1,18898);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("beauty.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer))!=-1) {
os.write(buffer, 0, len);
fis.close();
os.close();
socket.close();
//服务端
@Test
public void server() throws Exception{
ServerSocket serverSocket = new ServerSocket(18898);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream(new File("beauty1.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
fos.close();
is.close();
socket.close();
serverSocket.close();
}
注意:socket也需要关闭
五、TCP网络编程
基于Socket的TCP编程
-
Java语言的基于套接字编程分为服务端编程和客户端编程,其通信模型如图所示:
客户端Socket的工作过程
:o::客户端Socket的工作过程包含以下四个基本的步骤
-
创建 Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端 响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
-
打开连接到 Socket 的输入/出流: 使用 getInputStream()方法获得输入流,使用 getOutputStream()方法获得输出流,进行数据传输
-
按照一定的协议对 Socket 进行读/写操作:通过输入流读取服务器放入线路的信息 (但不能读取自己放入线路的信息),通过输出流将信息写入线程。
-
关闭 Socket:断开客户端到服务器的连接,释放线路
客户端创建Socket对象
-
客户端程序可以使用Socket类创建对象,创建的同时会自动向服务器方发起连 接。Socket的构造器是:
-
Socket(String host,int port)throws UnknownHostException,IOException:向服务器(域名是 host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。
-
Socket(InetAddress address,int port)throws IOException:根据InetAddress对象所表示的 IP地址以及端口号port发起连接。
-
客户端建立socketAtClient对象的过程就是向服务器发出套接字连接请求
-
Socket s = new
Socket(“192.168.40.165”,9999);
OutputStream out = s.getOutputStream();
out.write(" hello".getBytes());
s.close();
服务器程序的工作过程
:o::服务器程序的工作过程包含以下四个基本的步骤:
-
调用 ServerSocket(int port) :创建一个服务器端套接字,并绑定到指定端口 上。用于监听客户端的请求
-
调用 accept():监听连接请求,如果客户端请求连接,则接受连接,返回通信 套接字对象
-
调用 该Socket类对象的 getOutputStream() 和 getInputStream ():获取输出 流和输入流,开始网络数据的发送和接收
-
关闭ServerSocket和Socket对象:客户端访问结束,关闭通信套接字
服务器建立 ServerSocket 对象
-
ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口 中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字 连接的ServerSocket对象。
-
所谓“接收”客户的套接字请求,就是accept()方法会返回一个 Socket 对象
-
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();
客户端—服务端
:o::客户端:
:o::服务端:
六、UDP网络编程
基本了解
-
类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序
-
UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证 UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达
-
DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP 地址和端口号以及接收端的IP地址和端口号
-
UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和 接收方的连接。如同发快递包裹一样
DatagramSocket 类的常用方法
public DatagramSocket(int port):创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被绑定到通配符地址,IP 地址由内核来选择。
public DatagramSocket(int port,InetAddress laddr):创建数据报套接字,将其绑定到指定的本地地址。本地端口必须在 0 到 65535 之间(包括两者)。如果 IP 地址为 0.0.0.0,套接字将被绑定到通配符地址,IP 地址由内核选择。
public void close():关闭此数据报套接字。
public void send(DatagramPacket p):从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。
public void receive(DatagramPacket p):从此套接字接收数据报包。当此方法返回时,DatagramPacket的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法在接收到数据报前一直阻塞。数据报包对象的 length 字段包含所接收信息的长度。如果信息比包的长度长,该信息将被截短。
public InetAddress getLocalAddress():获取套接字绑定的本地地址。
public int getLocalPort():返回此套接字绑定的本地主机上的端口号。
public InetAddress getInetAddress():返回此套接字连接的地址。如果套接字未连接,则返回 null。
public int getPort():返回此套接字的端口。如果套接字未连接,则返回 -1。
DatagramPacket类的常用方法
public DatagramPacket(byte[] buf,int length):构造 DatagramPacket,用来接收长度为 length 的数据包。 length 参数必须小于等于 buf.length。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port):构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length参数必须小于等于 buf.length。
public InetAddress getAddress():返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
public int getPort():返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
public byte[] getData():返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量 offset 处开始,持续 length 长度。
public int getLength():返回将要发送或接收到的数据的长度。
UDP网络通信
-
流程:
-
DatagramSocket与DatagramPacket
-
建立发送端,接收端
-
建立数据包
-
调用Socket的发送、接收方法
-
关闭Socket
-
发送端与接收端是两个独立的运行程序
UDP通信代码举例
package com.jsm.java2;
import org.junit.Test;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;
public class UDPTest {
public void send(DatagramPacket p):从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、
其长度、远程主机的 IP 地址和远程主机的端口号。
//发送端
@Test
public void sender() throws IOException {
DatagramSocket ds = new DatagramSocket();//不用参数
String string ="UDP方式发送数据";
byte[] bytes1 = string.getBytes();
InetAddress byName = InetAddress.getByName("127.0.0.1");
// InetAddress byName2 = InetAddress.getLocalHost();
DatagramPacket dp = new DatagramPacket(bytes1,0,bytes1.length,byName,9092);
//封装完之和发送
ds.send(dp);
//发出去之和关闭socket
ds.close();
//接收端
@Test
public void receiver() throws Exception{
DatagramSocket ds = new DatagramSocket(9092);
byte[] bytes2 = new byte[100];
DatagramPacket dp = new DatagramPacket(bytes2,0,bytes2.length);
//接收数据
ds.receive(dp);
System.out.println(new String(dp.getData(),0,dp.getLength()));
ds.close();
}
七、URL编程
URL类
URL类构造器
URL类常用方法
URLConnection类
:o::针对HTTP协议的URLConnection类
URI、URL和URN的区别
-
URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个 资源。
-
URL是uniform resource locator,统一资源定位符,它是一种具体 的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
-
URN,uniform resource name,统一资源命名,是通过名字来标识资源, 比如mailto:java-net@java.sun.com。也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL 和URN都是一种URI
-
在Java的URI中,一个URI实例可以代表绝对的,也可以是相对的,只要它符合URI的语法规则。而URL类则不仅符合语义,还包含了定位该资源的信息, 因此它不能是相对的。
URL类的理解和实例化★
//学习内容:
//开发时间:10月27日 15:20
package com.jsm.java2;
import java.net.MalformedURLException;
import java.net.URL;
统一资源定位符
常用方法:
public class URLTest {
public static void main(String[] args) {
URL url = null;
try {
url = new URL("https://kingsman.blog.csdn.net/article/details/127148429?spm=1001.2014.3001.5502");
} catch (MalformedURLException e) {
e.printStackTrace();
// * public String getProtocol( ) :获取该URL的协议名
String protocol = url.getProtocol();
System.out.println(protocol);
// * public String getHost( ) :获取该URL的主机名
String host = url.getHost();
System.out.println(host);
// * public String getPort( ) :获取该URL的端口号
int port = url.getPort();
System.out.println(port);
// * public String getPath( ) :获取该URL的文件路径
String path = url.getPath();
System.out.println(path);
// * public String getFile( ) :获取该URL的文件名
String file = url.getFile();
System.out.println(file);
// * public String getQuery( ) :获取该URL的查询名
String query = url.getQuery();
System.out.println(query);
}
URL类实现用url下载资源★
:o::这里为了代码的观赏性,异常用throws抛出,实际应该和上面一样使用try-catch-finally
//学习内容:
//开发时间:10月27日 15:29
package com.jsm.java2;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
public class URLDownload {
public static void main(String[] args) throws IOException {
URL url = new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fd.paper.i4.cn%2Fmax%2F2017%2F06%2F29%2F11%2F1498706170987_406948.jpg&refer=http%3A%2F%2Fd.paper.i4.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1669447888&t=c1f071ef076a7ffb77e84290df133baf");
HttpURLConnection urlConnection =(HttpURLConnection) url.openConnection();//强转
urlConnection.connect();//访问服务器获取连接
//读取和写入
InputStream is = urlConnection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream("下载的照片.jpg");
byte[] bytes = new byte[1024];
int len;
while((len=is.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
//关闭资源
is.close();
fileOutputStream.close();
urlConnection.disconnect();//断开连接
}
八、小 结
:o::位于网络中的计算机具有唯一的IP地址,这样不同的主机可以互相区分。
:o::客户端-服务器是一种最常见的网络应用程序模型。服务器是一个为其客户端提供某种特定 服务的硬件或软件。客户机是一个用户应用程序,用于访问某台服务器提供的服务。端口号 是对一个服务的访问场所,它用于区分同一物理计算机上的多个服务。套接字用于连接客户 端和服务器,客户端和服务器之间的每个通信会话使用一个不同的套接字。TCP协议用于实 现面向连接的会话。
:o::Java 中有关网络方面的功能都定义在 java.net 程序包中。Java 用 InetAddress 对象表示 IP 地址,该对象里有两个字段:主机名(String) 和 IP 地址(int)。 类 Socket 和 ServerSocket 实现了基于TCP协议的客户端-服务器程序。Socket是客户端 和服务器之间的一个连接,连接创建的细节被隐藏了。这个连接提供了一个安全的数据传输 通道,这是因为 TCP 协议可以解决数据在传送过程中的丢失、损坏、重复、乱序以及网络 拥挤等问题,它保证数据可靠的传送。
最近刷抖音发现上线了 **IP** 属地的功能,小伙伴在发表动态、发表评论以及聊天的时候,都会显示自己的 **IP** 属地信息,其核心意义是让用户更具有真实性,减少虚假欺骗事件。正好最近本人开发获取客户端ip,做一些接口限流,黑白名单等需求功能,顺路就研究了一下怎么解析**IP**获取归属地问题。
本文是采用Java配置类的方式配置微服务的Nacos服务注册,并动态的配置spring.cloud.discovery.ip属性以打到微服务内网穿透注册至Nacos注册中心。