JAVA------ 网络编程

一、Java 网络编程
网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。

?        java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。

java.net 包中提供了两种常见的网络协议的支持:

- **TCP**:TCP(英语:Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP 层是位于 IP 层之上,应用层之下的中间层。TCP 保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。
- **UDP**:UDP (英语:User Datagram Protocol,用户数据报协议),位于 OSI 模型的传输层。一个无连接的协议。提供了应用程序之间要发送数据的数据报。由于UDP缺乏可靠性且属于无连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。

## 1、Socket 编程

?        套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

?        以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:
?                1、服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
?                2、服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
?                3、服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
?                4、Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够        与服务器进行通信。
?                5、在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

?        连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。

?        TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。

?        Java的网络编程主要涉及到的内容是Socket编程。Socket,套接字,就是两台主机之间逻辑连接的端点。TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的协议端口。

?        应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。

?        Socket,实际上是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。实际上,Socket跟TCP/IP协议没有必然的关系,Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现,只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、accept、send、read和write等等。网络有一段关于socket和TCP/IP协议关系的说法比较容易理解:

?        “TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。”

?        实际上,传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。socket是对端口通信开发的工具,它要更底层一些。

## 2、Socket整体流程

?        Socket编程主要涉及到客户端和服务端两个方面,首先是在服务器端创建一个服务器套接字(ServerSocket),并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。

?        客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

<img src="11-网络编程(Socket).assets\image-20221113140429361.png" alt="image-20221113140429361" style="zoom:80%;" />

## 2、ServerSocket 类的方法

?        服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。ServerSocket 类有四个构造方法:

| 方法                                                         | **方法描述**                                                 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| **public ServerSocket(int port) throws IOException**         | 创建绑定到特定端口的服务器套接字。                           |
| **public ServerSocket(int port, int backlog) throws IOException** | 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
| **public ServerSocket(int port, int backlog, InetAddress address) throws IOException** | 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
| **public ServerSocket() throws IOException**                 | 创建非绑定服务器套接字。                                     |

?        创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。

?        这里有一些 ServerSocket 类的常用方法:

| 方法                                                  | **方法描述**                                        |
| ----------------------------------------------------- | --------------------------------------------------- |
| **public int getLocalPort()**                         | 返回此套接字在其上侦听的端口。                      |
| **public Socket accept() throws IOException**         | 侦听并接受到此套接字的连接。                        |
| **public void setSoTimeout(int timeout)**             | 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。  |
| **public void bind(SocketAddress host, int backlog)** | 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 |

------

## 3、Socket 类的方法

?        java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。

?        Socket 类有五个构造方法.

| 方法                                                         | **方法描述**                                             |
| ------------------------------------------------------------ | -------------------------------------------------------- |
| **public Socket(String host, int port) throws UnknownHostException, IOException.** | 创建一个流套接字并将其连接到指定主机上的指定端口号。     |
| **public Socket(InetAddress host, int port) throws IOException** | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。   |
| **public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException.** | 创建一个套接字并将其连接到指定远程主机上的指定远程端口。 |
| **public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException.** | 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
| **public Socket()**                                          | 通过系统默认类型的 SocketImpl 创建未连接套接字           |

?        当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。

?        下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。

| 方法                                                         | **方法描述**                                          |
| ------------------------------------------------------------ | ----------------------------------------------------- |
| **public void connect(SocketAddress host, int timeout) throws IOException** | 将此套接字连接到服务器,并指定一个超时值。            |
| **public InetAddress getInetAddress()**                      | 返回套接字连接的地址。                                |
| **public int getPort()**                                     | 返回此套接字连接到的远程端口。                        |
| **public int getLocalPort()**                                | 返回此套接字绑定到的本地端口。                        |
| **public SocketAddress getRemoteSocketAddress()**            | 返回此套接字连接的端点的地址,如果未连接则返回 null。 |
| **public InputStream getInputStream() throws IOException**   | 返回此套接字的输入流。                                |
| **public OutputStream getOutputStream() throws IOException** | 返回此套接字的输出流。                                |
| **public void close() throws IOException**                   | 关闭此套接字。                                        |

------

## 4、InetAddress 类的方法

?        这个类表示互联网协议(IP)地址。下面列出了 Socket 编程时比较有用的方法:

| 方法                                                         | **方法描述**                                        |
| ------------------------------------------------------------ | --------------------------------------------------- |
| **static InetAddress getByAddress(byte[] addr)**             | 在给定原始 IP 地址的情况下,返回 InetAddress 对象。 |
| **static InetAddress getByAddress(String host, byte[] addr)** | 根据提供的主机名和 IP 地址创建 InetAddress。        |
| **static InetAddress getByName(String host)**                | 在给定主机名的情况下确定主机的 IP 地址。            |
| **String getHostAddress()**                                  | 返回 IP 地址字符串(以文本表现形式)。              |
| **String getHostName()**                                     | 获取此 IP 地址的主机名。                            |
| **static InetAddress getLocalHost()**                        | 返回本地主机。                                      |
| **String toString()**                                        | 将此 IP 地址转换为 String。                         |

------

## 5、Socket 客户端实例

?        如下的 GreetingClient 是一个客户端程序,该程序通过 socket 连接到服务器并发送一个请求,然后等待一个响应。

```java
import java.net.*;
import java.io.*;
 
public class GreetingClient
{
   public static void main(String [] args)
   {
      String serverName = args[0];
      int port = Integer.parseInt(args[1]);
      try
      {
         System.out.println("连接到主机:" + serverName + " ,端口号:" + port);
         Socket client = new Socket(serverName, port);
         System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
         OutputStream outToServer = client.getOutputStream();
         DataOutputStream out = new DataOutputStream(outToServer);
 
         out.writeUTF("Hello from " + client.getLocalSocketAddress());
         InputStream inFromServer = client.getInputStream();
         DataInputStream in = new DataInputStream(inFromServer);
         System.out.println("服务器响应: " + in.readUTF());
         client.close();
      }catch(IOException e)
      {
         e.printStackTrace();
      }
   }
}
```

## 6、Socket 服务端实例
如下的GreetingServer 程序是一个服务器端应用程序,使用 Socket 来监听一个指定的端口。

```java
import java.net.*;
import java.io.*;
 
public class GreetingServer extends Thread
{
   private ServerSocket serverSocket;
   
   public GreetingServer(int port) throws IOException
   {
      serverSocket = new ServerSocket(port);
      serverSocket.setSoTimeout(10000);
   }
 
   public void run()
   {
      while(true)
      {
         try
         {
            System.out.println("等待远程连接,端口号为:" + serverSocket.getLocalPort() + "...");
            Socket server = serverSocket.accept();
            System.out.println("远程主机地址:" + server.getRemoteSocketAddress());
            DataInputStream in = new DataInputStream(server.getInputStream());
            System.out.println(in.readUTF());
            DataOutputStream out = new DataOutputStream(server.getOutputStream());
            out.writeUTF("谢谢连接我:" + server.getLocalSocketAddress() + "\nGoodbye!");
            server.close();
         }catch(SocketTimeoutException s)
         {
            System.out.println("Socket timed out!");
            break;
         }catch(IOException e)
         {
            e.printStackTrace();
            break;
         }
      }
   }
   public static void main(String [] args)
   {
      int port = Integer.parseInt(args[0]);
      try
      {
         Thread t = new GreetingServer(port);
         t.run();
      }catch(IOException e)
      {
         e.printStackTrace();
      }
   }
}
```

Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,先了解两个概念,**编译期**和**运行期**。

?        **编译期**是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。

?        **运行期**是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。

?        Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。

?        Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。

<img src="09-反射.assets\image-20221027183441969.png" alt="image-20221027183441969" style="zoom:80%;" />

?        Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect包。
?                1、在运行时判断任意一个对象所属的类。
?                2、在运行时构造任意一个类的对象。
?                3、在运行时判断任意一个类所具有的成员变量和方法。
?                4、在运行时调用任意一个对象的方法。
?                5、生成动态代理。

?        要想知道一个类的属性和方法,必须先获取到该类的字节码文件对象。获取类的信息时,使用的就是 Class 类中的方法。所以先要获取到每一个字节码文件(.class)对应的 Class 类型的对象.

?        众所周知,所有 Java 类均继承了 Object 类,在 Object 类中定义了一个 getClass() 方法,该方法返回同一个类型为 Class 的对象。例如,下面的示例代码:

```java
Class demo = new Demo01().getClass();    
```

?        利用 Class 类的对象 demo 可以访问 Demo01 对象的描述信息、Demo01 类的信息以及基类 Object 的信息。下表列出了通过反射可以访问的信息。

| 类型                      | 访问方法            | 返回值类型                 | 说明                                              |
| ------------------------- | ------------------- | -------------------------- | ------------------------------------------------- |
| 包路径                    | getPackage()        | Package 对象               | 获取该类的存放路径                                |
| 类名称                    | getName()           | String 对象                | 获取该类的名称                                    |
| 继承类                    | getSuperclass()     | Class 对象                 | 获取该类继承的类                                  |
| 实现接口                  | getlnterfaces()     | Class 型数组               | 获取该类实现的所有接口                            |
| 构造方法                  | getConstructors()   | Constructor 型数组         | 获取所有权限为 public 的构造方法                  |
| getDeclaredContruectors() | Constructor 对象    | 获取当前对象的所有构造方法 |                                                   |
| 方法                      | getMethods()        | Methods 型数组             | 获取所有权限为 public 的方法                      |
| getDeclaredMethods()      | Methods 对象        | 获取当前对象的所有方法     |                                                   |
| 成员变量                  | getFields()         | Field 型数组               | 获取所有权限为 public 的成员变量                  |
| getDeclareFileds()        | Field 对象          | 获取当前对象的所有成员变量 |                                                   |
| 内部类                    | getClasses()        | Class 型数组               | 获取所有权限为 public 的内部类                    |
| getDeclaredClasses()      | Class 型数组        | 获取所有内部类             |                                                   |
| 内部类的声明类            | getDeclaringClass() | Class 对象                 | 如果该类为内部类,则返回它的成员类,否则返回 null |

?        如上表所示,在调用 getFields() 和 getMethods() 方法时将会依次获取权限为 public 的字段和变量,然后将包含从超类中继承到的成员变量和方法。而通过 getDeclareFields() 和 getDeclareMethod() 只是获取在本类中定义的成员变量和方法。

**Java 反射机制的优缺点**

**优点:**
        能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
        与 Java 动态编译相结合,可以实现无比强大的功能。
        对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

**缺点:**

?        反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
?        反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。

Java 反射机制在一般的 Java 应用开发中很少使用,即便是 Java EE 阶段也很少使用。

# 二、反射机制API

?        实现 Java 反射机制的类都位于 java.lang.reflect 包中,java.lang.Class 类是 Java 反射机制 API 中的核心类。

## 1、java.lang.Class 类

?        java.lang.Class 类是实现反射的关键所在,Class 类的一个实例表示 Java 的一种数据类型,包括类、接口、枚举、注解(Annotation)、数组、基本数据类型和 void。Class 没有公有的构造方法,Class 实例是由 JVM 在类加载时自动创建的。

?        在程序代码中获得 Class 实例可以通过如下代码实现:

```java
// 1. 通过类型class静态变量
Class clz1 = String.class;

String str = "Hello";

// 2. 通过对象的getClass()方法
Class clz2 = str.getClass();
```

?        每一种类型包括类和接口等,都有一个 class 静态变量可以获得 Class 实例。另外,每一个对象都有 getClass() 方法可以获得 Class 实例,该方法是由 Object 类提供的实例方法。

?        Class 类提供了很多方法可以获得运行时对象的相关信息,下面的程序代码展示了其中一些方法。

```java
public class ReflectionTest01 {
    public static void main(String[] args) {
        // 获得Class实例
        // 1.通过类型class静态变量
        Class clz1 = String.class;
        
        String str = "Hello";
        
        // 2.通过对象的getClass()方法
        Class clz2 = str.getClass();
        
        // 获得int类型Class实例
        Class clz3 = int.class;
        
        // 获得Integer类型Class实例
        Class clz4 = Integer.class;
        
        System.out.println("clz2类名称:" + clz2.getName());
        System.out.println("clz2是否为接口:" + clz2.isInterface());
        System.out.println("clz2是否为数组对象:" + clz2.isArray());
        System.out.println("clz2父类名称:" + clz2.getSuperclass().getName());
        System.out.println("clz2是否为基本类型:" + clz2.isPrimitive());
        System.out.println("clz3是否为基本类型:" + clz3.isPrimitive());
        System.out.println("clz4是否为基本类型:" + clz4.isPrimitive());
    }
}
```

?        运行结果如下:

```
clz2类名称:java.lang.String
clz2是否为接口:false
clz2是否为数组对象:false
clz2父类名称:java.lang.Object
clz2是否为基本类型:false
clz3是否为基本类型:true
clz4是否为基本类型:false
```

?        注意上述代码第 10 行和第 12 行的区别。int 是基本数据类型,所以输出结果为 true;Integer 是类,是引用数据类型,所以输出结果为 false。

## 2、java.lang.reflect 包

java.lang.reflect 包提供了反射中用到类,主要的类说明如下:

- Constructor 类:提供类的构造方法信息。
- Field 类:提供类或接口中成员变量信息。
- Method 类:提供类或接口成员方法信息。
- Array 类:提供了动态创建和访问 Java 数组的方法。
- Modifier 类:提供类和成员访问修饰符信息。


示例代码如下:

```java
public class ReflectionTest02 {
    public static void main(String[] args) {
        
        try {
            // 动态加载xx类的运行时对象
            Class c = Class.forName("java.lang.String");
            
            // 获取成员方法集合
            Method[] methods = c.getDeclaredMethods();
            
            // 遍历成员方法集合
            for (Method method : methods) {
                // 打印权限修饰符,如public、protected、private
                System.out.print(Modifier.toString(method.getModifiers()));
                // 打印返回值类型名称
                System.out.print(" " + method.getReturnType().getName() + " ");
                // 打印方法名称
                System.out.println(method.getName() + "();");
            }
            
        } catch (ClassNotFoundException e) {
            System.out.println("找不到指定类");
        }
        
    } 
}
```

?        上述代码第 5 行是通过 Class 的静态方法`forName(String)`创建某个类的运行时对象,其中的参数是类全名字符串,如果在类路径中找不到这个类则抛出 ClassNotFoundException 异常,见代码第 17 行。

?        代码第 7 行是通过 Class 的实例方法 getDeclaredMethods() 返回某个类的成员方法对象数组。代码第 9 行是遍历成员方法集合,其中的元素是 Method 类型。

?        代码第 11 行的`method.getModifiers()`方法返回访问权限修饰符常量代码,是 int 类型,例如 1 代表 public,这些数字代表的含义可以通过`Modifier.toString(int)`方法转换为字符串。代码第 13 行通过 Method 的 getReturnType() 方法获得方法返回值类型,然后再调用 getName() 方法返回该类型的名称。代码第 15 行`method.getName()`返回方法名称。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值