Java 网络编程

前言

1、IP 地址 端口

  • IP 地址

    • 在网络中每台计算机都必须有一个的 IP 地址
    • 32 位,4 个字节,常用点分十进制的格式表示,例如:192.168.1.100。
    • 127.0.0.1 是固定 ip 地址,代表当前计算机,相当于面向对象里的 this
  • 端口

    • 两台计算机进行连接,总有一台服务器,一台客户端。
    • 服务器和客户端之间的通信通过端口进行。
  • 获取本机 IP 地址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import java.net.InetAddress;
    import java.net.UnknownHostException;

    public static void main(String[] args) throws UnknownHostException {

    try {
    InetAddress host = InetAddress.getLocalHost();
    String ip = host.getHostAddress();
    System.out.println("本机 ip 地址:" + ip);
    } catch (UnknownHostException e) {
    throw new UnknownHostException(9006, "IP not found!"); // 抛出异常
    }
    }

2、ping 命令

  • 使用 ping 判断一个地址是否能够到达。

  • ping 不是 java 的 api,是 windows 中的一个小工具,用于判断一个地址的响应时间。

  • 使用 java 执行 ping 命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;

    public static void main(String[] args) throws IOException {

    Process p = Runtime.getRuntime().exec("ping " + "192.168.2.106");
    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));

    String line = null;
    StringBuilder sb = new StringBuilder();

    while ((line = br.readLine()) != null) {
    if (line.length() != 0) {
    sb.append(line + "\r\n");
    }
    }
    System.out.println("本次指令返回的消息是:");
    System.out.println(sb.toString());
    }

3、Socket 套接字

3.1 建立连接

  • 服务端开启 8888 端口,并监听着,时刻等待着客户端的连接请求。
  • 客户端知道服务端的 ip 地址和监听端口号,发出请求到服务端。
  • 客户端的端口地址是系统分配的,通常都会大于 1024。
  • 一旦建立了连接,服务端会得到一个新的 Socket 对象,该对象负责与客户端进行通信。

  • 服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;

    try {
    ServerSocket ss = new ServerSocket(8888); // 服务端打开端口 8888
    Socket s = ss.accept(); // 在 8888 端口上监听,看是否有连接请求过来

    s.close();
    ss.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import java.io.IOException;
    import java.net.Socket;
    import java.net.UnknownHostException;

    try {
    Socket s = new Socket("127.0.0.1", 8888); // 连接到本机的 8888 端口

    s.close();
    } catch (UnknownHostException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }

3.2 使用 字节流 收发数字

  • 一旦建立了连接,服务端和客户端就可以通过 Socket 进行通信了。
  • 客户端打开输出流,并发送数字 110。
  • 服务端打开输入流,接受数字 110,并打印。

  • 服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;

    try {
    ServerSocket ss = new ServerSocket(8888);
    Socket s = ss.accept();

    InputStream is = s.getInputStream(); // 打开输入流
    int msg = is.read(); // 读取客户端发送的数据

    is.close();
    s.close();
    ss.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.net.UnknownHostException;

    try {
    Socket s = new Socket("127.0.0.1", 8888);

    OutputStream os = s.getOutputStream(); // 打开输出流
    os.write(110); // 发送数字 110 到服务端

    os.close();
    s.close();
    } catch (UnknownHostException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }

3.3 使用 数据流 收发字符串

  • 直接使用字节流收发字符串比较麻烦,使用数据流对字节流进行封装,这样收发字符串就容易了。
  • 把输出流封装在 DataOutputStream 中。
  • 使用 writeUTF 发送字符串 “Legendary!”。
  • 把输入流封装在 DataInputStream。
  • 使用 readUTF 读取字符串,并打印。

  • 服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;

    try {
    ServerSocket ss = new ServerSocket(8888);
    Socket s = ss.accept();

    InputStream is = s.getInputStream();
    DataInputStream dis = new DataInputStream(is); // 把输入流封装在 DataInputStream
    String msg = dis.readUTF(); // 使用 readUTF 读取字符串

    dis.close();
    s.close();
    ss.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.Scanner;

    try {
    Socket s = new Socket("127.0.0.1", 8888);

    OutputStream os = s.getOutputStream();
    DataOutputStream dos = new DataOutputStream(os); // 把输出流封装在 DataOutputStream 中
    dos.writeUTF("Legendary!"); // 使用 writeUTF 发送字符串

    dos.close();
    s.close();
    } catch (UnknownHostException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }

3.4 使用 Scanner 读取输入

  • 使用 Scanner 读取控制台的输入,并发送到服务端。

  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.Scanner;

    try {
    Socket s = new Socket("127.0.0.1", 8888);
    OutputStream os = s.getOutputStream();
    DataOutputStream dos = new DataOutputStream(os);

    Scanner sc = new Scanner(System.in);
    String str = sc.next(); // 使用 Scanner 读取控制台的输入,并发送到服务端

    dos.writeUTF(str);
    dos.close();
    s.close();
    } catch (UnknownHostException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }

3.5 使用多线程

  • 多线程聊天:如果使用单线程开发 Socket 应用,那么同一时间,要么收消息,要么发消息,不能同时进行。为了实现同时收发消息,就需要用到多线程。
文章目录
  1. 1. 前言
  2. 2. 1、IP 地址 端口
  3. 3. 2、ping 命令
  4. 4. 3、Socket 套接字
    1. 4.1. 3.1 建立连接
    2. 4.2. 3.2 使用 字节流 收发数字
    3. 4.3. 3.3 使用 数据流 收发字符串
    4. 4.4. 3.4 使用 Scanner 读取输入
    5. 4.5. 3.5 使用多线程
隐藏目录