https://www.cnblogs.com/swordfall/p/10781281.html

1. InetAddress 类概述

InetAddress 类对象代表了一个 IP 地址信息。其提供了一系列获取 IP 地址等相关信息的方法。

  1. static InetAddress getLocalHost():获得本地主机IP地址对象。
  2. static InetAddress getByName(String host):根据IP地址字符串或主机名获得对应的IP地址对象。
  3. String getHostName():获得主机名。
  4. String getHostAddress():获得IP地址字符串。

2. Java UDP 通信

UDP协议相关的两个类

  1. DatagramPacket,数据包对象。作用:用来封装要发送或要接收的数据,比如:集装箱
  2. DatagramSocket,发送对象。作用:用来发送或接收数据包,比如:码头

2.1 DatagramPacket

类构造器:

  1. public DatagramPacket(byte buf[], int length, InetAddress address, int port):创建发送端数据包对象,发送端用
    1. buf:要发送的内容,字节数组
    2. length:要发送内容的长度,单位是字节
    3. address:接收端的IP地址对象
    4. port:接收端的端口号
  2. public DatagramPacket(byte buf[], int length):创建接收端的数据包对象,接收端用
    1. buf:用来存储接收到内容
    2. length:能够接收内容的长度

常用方法:

  1. int getLength():获得实际接收到的字节个数。

2.2 DatagramSocket

构造方法:

  1. public DatagramSocket(): 创建发送端的 Socket 对象,系统会随机分配一个端口号。
  2. public DatagramSocket(int port): 创建接收端的 Socket 对象并指定端口号

常用方法:

  1. public void send(DatagramPacket p):发送数据包
  2. public synchronized void receive(DatagramPacket p):接收数据包

2.3 实现客户端发消息,服务端接收消息

  1. 客户端: ```java public class UDPClient { public static void main(String[] args) throws Exception {

    1. System.out.println("===启动客户端===");
    2. // 1.创建一个集装箱对象,用于封装需要发送的数据包!
    3. /**
    4. new DatagramPacket(byte[] buf, int length, InetAddress address, int port)
    5. 参数一:封装数据的字节数组。
    6. 参数二:发送数据的长度!
    7. 参数三:服务端的IP地址
    8. 参数四:服务端程序的端口号码。
    9. */
    10. byte[] buffer = "今晚,约吗?".getBytes();
    11. DatagramPacket packet = new DatagramPacket(buffer, buffer.length
    12. , InetAddress.getLocalHost(), 6666);
    13. // 2.创建一个码头对象
    14. // 参数可以申明客户端端口,可以有可以没有,默认会给一个端口。
    15. DatagramSocket socket = new DatagramSocket();
    16. // 3.开始发送数据包对象
    17. socket.send(packet);
    18. socket.close();

    } }



2. 服务端:
```java
public class UDPServer {
    public static void main(String[] args) throws Exception {
        System.out.println("==启动服务端程序==");
        // 1.创建一个接收客户都端的数据包对象(集装箱)
        /**
         * new DatagramPacket(byte[] buffer ,int lenght):
         * 参数一:接收数据的数组。
         * 参数二:接收数据的数组的长度!
         */
        byte[] buffer = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        // 2.创建一个接收端的码头对象
        DatagramSocket socket = new DatagramSocket(6666);

        // 3.开始接收
        socket.receive(packet);

        // 4.从集装箱中获取本次读取的数据量
        int len = packet.getLength();

        // 5.输出数据
        String rs = new String(buffer, 0, len);
        System.out.println(rs);

        // 6.服务端还可以获取发来信息的客户端的IP和端口。
        String ip = packet.getAddress().getHostAddress();
        int port = packet.getPort();
        System.out.println("对方:" + ip + ":" + port);
        socket.close();
    }
}

3. Java TCP 通信

TCP 通信也叫 Socket 网络编程,只要代码基于 Socket 开发,底层就是基于了可靠传输的 TCP 通信。

TCP协议相关的类

  1. Socket:一个该类的对象就代表一个客户端程序。
  2. ServerSocket:一个该类的对象就代表一个服务器端程序。

    3.1 Socket

    构造方法:

  3. public Socket(String host, int port):根据 ip 地址字符串和端口号创建客户端 Socket 对象

注意事项:只要执行该方法,就会立即连接指定的服务器程序,如果连接不成功,则会抛出异常。如果连接成功,则表示三次握手通过。

常用方法:

  1. public OutputStream getOutputStream():获得字节输出流对象
  2. public InputStream getInputStream():获得字节输入流对象

3.2 ServerSocket

构造方法:

  1. public ServerSocket(int port):根据 port 端口创建服务端 ServerSocket 对象;

常用方法:

  1. public Socket accept():等待接收一个客户端的 Socket 管道连接请求,连接成功返回一个 Socket 对象;

    3.3 实现客户端服务端收发消息

    客户端发一行消息,服务端接收一行。
    可以拓展多个客户端向服务端发送消息,服务端创建线程池,没收到一个请求分配一个线程接收消息。

  2. 客户端:

    public class Client {
     public static void main(String[] args) throws Exception {
         // 1.客户端要请求于服务端的socket管道连接。
         // Socket(String host, int port)
         Socket socket = new Socket("127.0.0.1", 9999);
         // 2.从socket通信管道中得到一个字节输出流
         OutputStream os = socket.getOutputStream();
         // 3.把低级的字节输出流包装成高级的打印流。
         PrintStream ps = new PrintStream(os);
         // 4.开始发消息出去
         ps.println("我是客户端,喜欢你很久了,第一次给你发消息,只想说:约吗?");
         ps.flush();
         System.out.println("客户端发送完毕~~~~");
     }
    }
    
  3. 服务端:

    public class ServerDemo02 {
     public static void main(String[] args) throws Exception {
         System.out.println("----服务端启动----");
         // 1.注册端口: public ServerSocket(int port)
         ServerSocket serverSocket = new ServerSocket(9999);
         // 2.开始等待接收客户端的Socket管道连接。
         Socket socket = serverSocket.accept();
         // 3.从socket通信管道中得到一个字节输入流。
         InputStream is = socket.getInputStream();
         // 4.把字节输入流转换成字符输入流
         Reader isr = new InputStreamReader(is);
         // 5.把字符输入流包装成缓冲字符输入流。
         BufferedReader br = new BufferedReader(isr);
         // 6.按照行读取消息 。
         String line;
         if ((line = br.readLine()) != null) {
             System.out.println(line);
         }
     }
    }
    

    4. 基本通信模型的概念介绍

  4. BIO通信模式:同步阻塞式通信。(Socket网络编程也就是上面的通信架构)

    1. 同步:当前线程要自己进行数据的读写操作。(自己去银行取钱)
    2. 异步: 当前线程可以去做其他事情,(委托一小弟拿银行卡到银行取钱,然后给你)
    3. 阻塞: 在数据没有的情况下,还是要继续等待着读。(排队等待)
    4. 非阻塞:在数据没有的情况下,会去做其他事情,一旦有了数据再来获取。(柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理

BIO表示同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

同步阻塞式性能极差:大量线程,大量阻塞。

  1. 伪异步通信:引入了线程池。
    1. 不需要一个客户端一个线程,可以实现1个线程复用来处理很多个客户端!
    2. 这种架构,可以避免系统的死机,因为不会出现很多线程,线程可控。
    3. 但是高并发下性能还是很差:a.线程数量少,数据依然是阻塞的。数据没有来线程还是要等待!
  1. NIO表示同步非阻塞IO,服务器实现模式为请求对应一个线程,
    1. 同步:线程还是要不断的接收客户端连接,以及处理数据。
    2. 非阻塞:如果一个管道没有数据,不需要等待,可以轮询下一个管道是否有数据!

即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

  • 1个主线程专门负责接收客户端:
  • 1个线程[c1 ,s2 ,c3,c4, ,s2 ,c3,c4,,c3,c4, ,s2 ,c3,c4]轮询所有的客户端,发来了数据才会开启线程处理
  • 这种架构性能还可以
  1. AIO表示异步非阻塞IO,服务器实现模式为一个有效请求一个线程,
    1. 客户端的I/O请求都是由操作系统先完成IO操作后再通知服务器应用来启动线程进行处理。
    2. 异步:服务端线程接收到了客户端管道以后就交给底层处理它的io通信。自己可以做其他事情。
    3. 非阻塞:底层也是客户端有数据才会处理,有了数据以后处理好通知服务器应用来启动线程进行处理。

小结:
各种模型应用场景:

  • BIO适用于连接数目比较小且固定的架构,该方式对服务器资源要求比较高,JDK 1.4以前的唯一选择。
  • NIO适用于连接数目多且连接比较短(轻操作)的架构,如聊天服务器,编程复杂,JDK 1.4开始支持。
  • AIO适用于连接数目多且连接比较长(重操作)的架构,如相册服务器,充分调用操作系统参与并发操作,编程复杂,JDK 1.7开始支持。