Java TCP 网络编程


TCP 网络编程

  • TCP 通信同 UDP 通信一样,都能实现两台计算机之间的通信,但 TCP 通信的两端需要创建 Socket 对象。UDP 通信与 TCP 通信的区别在于:UDP 中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据;而 TCP 通信是严格区分客户端与服务器端的,在通信时,必须先由客户端主动连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接;
  • 在 JDK 中提供了两个用于实现 TCP 程序的类:一个是 ServerSocket 类,用于表示服务器端;另一个是 Socket 类,用于表示客户端。通信时,首先要创建代表服务器端的 ServerSocket 对象,创建该对象相当于开启一个服务,此服务会等待客户端的连接;然后创建代表客户端的 Socket 对象,使用该对象向服务器端发出连接请求,服务器端响应请求后,两者才建立连接,开始通信;

1. ServerSocket 类

  • 在开发 TCP 程序时,首先需要创建服务器端程序。JDK 的 java.net 包中提供了 ServerSocket 类,该类的实例对象可以实现一个服务器端的程序。ServerSocket 类提供了多种构造方法:
    (1)ServerSocket()
    (2)ServerSocket(int port)
    (3)ServerSocket(int port, int backlog)
    (4)ServerSocket(int port, int backlog, InetAddress bindAddr)
  • java.net.ServerSocket,详见:Class ServerSocket

a. 服务器端程序

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Test {
    public static void main(String[] args) {
        ServerSocket server_socket = null;
        Socket socket = null;
        DataInputStream in = null;
        DataOutputStream out = null;
        int port = 5050;
        try {
            server_socket = new ServerSocket(port); // 创建绑定端口的服务器端Socket
        } catch (IOException e) {
            System.out.println(e);
        }
        try {
            System.out.println("服务器启动!");
            socket = server_socket.accept();
            // 监听并接受到此Socket 的连接。此方法在连接传入之前处于阻塞状态
            in = new DataInputStream(socket.getInputStream()); // 创建输入流
            out = new DataOutputStream(socket.getOutputStream()); // 创建输出流
            String str = in.readUTF(); // 从输入流读取字符串,读取结束之前处于阻塞状态
            System.out.println("客户机发送过来的信息是: " + str);
            out.writeUTF("你好,我是服务器B"); // 向输出流写入字符串
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            try// 关闭网络连接
            {
                out.close();
                in.close();
                socket.close();
                server_socket.close();
            } catch (Exception e) {
            }
        }
    }
}
  • 该程序首先创建了绑定 5050 端口的服务器端 Socket,并进行连接监听。如果有连接要求,则创建 Socket 连接,并且建立输入流对象和输出流对象。通过输入流对象读取客户端发来的内容。通过输出流对象向客户端发送相应内容;

2. Socket 类

  • 使用 ServerSocket 对象可以实现服务器端程序,但只实现服务器端程序还不能完成通信,此时还需要一个客户端程序与之交互,为此 JDK 提供了一个 Socket 类,用于实现 TCP 客户端程序,Socket 类的常用构造方法:
    (1)Socket()
    (2)Socket(String host, int port)
    (3)Socket(InetAddress address, int port)
  • java.net.Socket,详见:Class Socket
    在这里插入图片描述

a. 客户端程序

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class Test {
    public static void main(String[] args) {
        Socket client_socket = null;
        DataInputStream in = null;
        DataOutputStream out = null;
        String ip = "127.0.0.1"; // 服务器IP 地址
        int port = 5050; // 服务器端口号
        try {
            client_socket = new Socket(ip, port); // 与服务器建立连接
            in = new DataInputStream(client_socket.getInputStream()); // 创建输入流
            out = new DataOutputStream(client_socket.getOutputStream()); // 创建输出流
            out.writeUTF("你好,我是客户机A"); // 向服务器端发送信息
            System.out.println("客户机启动,向服务器发送信息:你好,我是客户机A");
            String str = in.readUTF();// 等待读取服务器端响应的信息,进入阻塞状态
            System.out.println("服务器端的响应信息:" + str);
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            try// 关闭网络连接
            {
                in.close();
                out.close();
                client_socket.close();
            } catch (Exception e) {
            }
        }
    }
}
  • 本例中的客户端程序必须在服务器端程序运行后再运行,当服务器端程序处于运行状态时,客户端向其发出链接请求。链接建立成功,客户端通过输出流向服务器端发送请求内容,并从输入流读取服务器响应的内容,最后关闭链接;

3. 多线程的 TCP 网络编程

  • 如果需要进行多次数据交互,就可以在程序中设置一个循环,不断地向对方发送请求,即可完成多次数据交互。同样,如果需要让服务器端同时响应多个客户端的请求,可以使用多线程的方法,也就是服务器端每接收到一个新的连接请求,就启动一个专门的线程与该客户端进行交互;

a. 用多线程实现四则运算

  • 本例分为 3 个类:客户端类、服务器端类和逻辑线程类;
    (1)客户端类:主要功能是与服务器建立连接,连接成功后生成 Socket 对象,并且进一步建立输入流对象和输出流对象;
    (2)服务器端类:接收客户端的连接请求,启动专门的逻辑处理线程进行处理;
    (3)逻辑线程类:主要实现对客户端的逻辑处理,对接收到的表达式进行计算,并将结果返回给客户端;

i. 客户端类

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientTest {
    public static void main(String args[]) {
        Scanner scanner = new Scanner(System.in);
        String input = null;
        Socket socket = null;
        DataInputStream in = null;
        DataOutputStream out = null;
        String serverIP = "127.0.0.1"; //服务器地址
        int port = 5050;//服务器端口

        try {
            socket = new Socket(serverIP, port);//连接服务器
            in = new DataInputStream(socket.getInputStream());//创建输入流
            out = new DataOutputStream(socket.getOutputStream());//创建输出流
            System.out.println("请输入一个正整数的四则运算表达式:");

            while (scanner.hasNext()) {
                input = scanner.nextLine();//从键盘输入一个待计算的四则运算表达式
                if (!input.equals("0")) {
                    out.writeUTF(input);//向服务器端发送运算请求
                    String result = in.readUTF();//等待读取运算结果
                    System.out.println("服务器返回的计算结果:" + result);
                    System.out.println("请输入一个正整数的四则运算表达式(输入0退出):");
                } else
                    break;//请求结束
            }
        } catch (Exception e) {
            System.out.println("与服务器连接中断");
        } finally {
            try//关闭网络连接
            {
                in.close();
                out.close();
                socket.close();
                System.out.println("连接结束");
            } catch (Exception e) {
            }
        }
    }
}

ii. 服务器类

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerTest {
    public static void main(String args[]) {
        ServerSocket server_socket = null;
        Socket socket = null;
        int port = 5050;

        while (true) {
            try {
                server_socket = new ServerSocket(port);
                System.out.println("服务器启动!");
            } catch (IOException e1) {
                System.out.println("正在监听"); //ServerSocket对象不能重复创建
            }

            try {
                System.out.println("等待客户请求");
                socket = server_socket.accept();
                System.out.println("客户的地址:" + socket.getInetAddress() + ":" + socket.getPort());
            } catch (IOException e) {
                System.out.println("正在等待客户");
            }

            if (socket != null) {
                new ThreadTest(socket); //为每个客户启动一个专门的线程  
            }
        }
    }
}

iii. 逻辑线程类

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class ThreadTest extends Thread {
    Socket socket = null;
    DataInputStream in = null;
    DataOutputStream out = null;
    String str;
    String response;
    String ip;
    int port;

    public ThreadTest(Socket socket) {
        this.socket = socket;
        start();
    }

    public void run() {
        try {
            in = new DataInputStream(socket.getInputStream());//创建输入流
            out = new DataOutputStream(socket.getOutputStream());//创建输出流
            ip = socket.getInetAddress().getHostAddress();//客户端IP地址
            port = socket.getPort();//客户端的端口号

            while (true) {
                str = in.readUTF();//获取客户端的表达式
                System.out.println("客户端" + ip + ":" + port + "发送的请求内容:");
                System.out.println(str + "=?");

                if (str.equals("0")) {
                    System.out.println("连接结束");
                    break;
                } else {
                    response = doComputer(str);//对表达式进行计算
                    out.writeUTF(response);//响应计算结果
                }
            }
        } catch (Exception e) {
            System.out.println("连接结束");
        } finally {
            try {
                in.close();
                out.close();
                socket.close();
            } catch (Exception e) {
            }
        }
    }

    public String doComputer(String str) {
        String input;
        String[] sym;
        String[] data;
        int a = 0, b = 0, result = 0;
        input = str;
        data = input.split("\\D+");//分解表达式中的正整数
        sym = input.split("\\d+");//分解表达式中的运算符
        a = Integer.parseInt(data[0]);//第一个正整数
        b = Integer.parseInt(data[1]);//第二个正整数

        try {
            switch (sym[1])//判断运算符,完成相应的运算
            {
                case "+":
                    result = a + b;
                    break;
                case "-":
                    result = a - b;
                    break;
                case "*":
                    result = a * b;
                    break;
                case "/":
                    result = a / b;
            }
            System.out.println("计算结果:" + input + "=" + result);
            return String.valueOf(result);
        } catch (java.lang.ArithmeticException e) {
            System.out.println("数据错误!");
            return "数据错误!";
        }
    }
}

iv. 运行演示

  • 首先运行服务器端 ServerTest:
    在这里插入图片描述
  • 然后启动客户端 ClientTest:
    在这里插入图片描述
    在这里插入图片描述
  • 输入一个正整数的四则运算表达式 9*9:
    在这里插入图片描述
  • 输入 0,断开链接:
    在这里插入图片描述
发布了276 篇原创文章 · 获赞 280 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Regino/article/details/105035902