【Java】20 基于 TCP 协议的网络编程

时间:2022-07-26
本文章向大家介绍【Java】20 基于 TCP 协议的网络编程,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Java 对基于 TCP 协议的网络通信提供了良好的封装,Java 使用 Socket 对象来代表两端的通信端口,并通过 Socket 产生 IO 流来进行网络通信。

1.1 TCP 基础

  IP 协议是 Internet 上使用的一个关键协议,它的全称是 Internet Protocol,即 Internet 协议,通常简称 IP 协议。通过使用 IP 协议,从而使 Internet 成为一个允许连接不同类型的计算机和不同操作系统的网络。要使两台计算机彼此能进行通信,必须使两台计算机使用同一种“语言”,IP 协议只保证计算机能发送和接收分组数据。IP 协议负责将消息从一个主机传送到另一个主机,消息在传送的过程中被分割成一个个的小包。尽管计算机通过安装 IP 软件,保证了计算机之间可以发送和接收数据,但 IP 协议还不能解决数据分组在传输过程中可能出现的问题。   因此,若要解决可能出现的问题,连上 Internet 的计算机还需要安装 TCP 协议来提供可靠并且无差错的通信服务。TCP 协议被称作一种端对端协议。这是因为它对两台计算机之间的连接起了重要作用——当一台计算机需要与另一台远程计算机连接时,TCP 协议会让它们建立一个连接:用于发送和接收数据的虚拟链路。   TCP 协议负责收集这些信息包,并将其按适当的次序放好传送,接收端收到后再将其正确地还原。TCP 协议保证了数据包在传送中准确无误。TCP 协议使用重发机制——当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体的确认信息,如果没有收到另一个通信实体的确认信息,则会再次重发刚才发送的信息。通过这种重发机制,TCP 协议向应用程序提供了可靠的通信连接,使它能够自动适应网上的各种变化。即使在 Internet 暂时出现堵塞的情况下,TCP 也能够保证通信的可靠性。   虽然 IP 和 TCP 这两个协议的功能不尽相同,也可以分开单独使用,但它们是在同一时期作为一个协议来设计的,并且在功能上也是互补的。只有两者结合起来,才能保证 Internet 在复杂的环境下正常运行。凡是要连接到 Internet 的计算机,都必须同时安装和使用这两个协议,因此在实际中常把这两个协议统称为 TCP/IP 协议。


1.2 TCP 通信相关类

  在Java中,提供了两个类用于实现TCP通信程序,客户端:java.net.Socket类,用于创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。服务端:java.net.ServerSocket类,用于创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。

1.2.1 Socket 类

Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。

构造方法

public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的 host 是 null ,则相当于指定地址为回送地址。

补充:回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。

常用方法

方法名

说明

InputStream getInputStream()

返回该 Socket 对象对应的输入流,让程序通过该输入流从 Socket 中取出数据

OutputStream getOutputStream()

返回该 Socket 对象对应的输出流,让程序通过该输出流向 Socket 中输出数据

void close()

关闭 Socket

void shutdownOutput()

写结束标记

InetAddress getLocalAddress()

获取 Socket 绑定的本地地址

InetAddress getInetAddress()

返回 Socket 连接的地址

int getPort()

返回此 Socket 连接到的远程端口

1.2.2 ServerSocket 类

ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。

构造方法

public ServerSocket(int port) :使用该构造方法在创建 ServerSocket 对象时,就可以将其绑定到一个指定的端口号上,参数 port 就是端口号。

常用方法

方法名

说明

Socket accept()

接收到客户端 Socket 的连接请求,该方法将返回一个与客户端 Socket 对应的 Socket;否则该方法将一直处于等待状态,线程也被阻塞


1.3 实现 TCP 通信

1.3.1 TCP 通信分析

①【服务端】启动,创建 ServerSocket 对象,等待连接。 ②【客户端】启动,创建 Socket 对象,请求连接。 ③【服务端】接收连接,调用 accept 方法,并返回一个 Socket 对象。 ④【客户端】Socket 对象,获取 OutputStream,向服务端写出数据。 ⑤【服务端】Scoket 对象,获取 InputStream,读取客户端发送的数据。 ⑥【服务端】Socket 对象,获取 OutputStream,向客户端回写数据。 ⑦【客户端】Scoket 对象,获取 InputStream,解析回写数据。 ⑧【客户端】释放资源,断开连接。

1.3.2 服务端代码

public class DemoServerSocket {
    public static void main(String[] args) throws Exception{
    	// 使用多线程连接多个客户端
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    method();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
 
    public static void method() throws Exception{
        // 创建服务器连接
        ServerSocket serverSocket = new ServerSocket(8888);
        // 获取通信对象
        Socket socket = serverSocket.accept();
        // 获取网络输入流
        InputStream inputStream = socket.getInputStream();
        // 创建缓冲流
        BufferedInputStream bis = new BufferedInputStream(inputStream);
 
        // 创建日期格式化对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHssmmSS");
 
        // 本地缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("src\com\" + sdf.format(new Date()) + ".java"));
        // 创建缓冲区
        byte[] bytes = new byte[1024 * 1024];
        // 读取的长度
        int len;
        // 循环读取字节,并写出到服务器
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes,0,len);
        }
 
        // 获取网络输出流
        OutputStream outputStream = accept.getOutputStream();
        // 写回信息
        outputStream.write("上传成功".getBytes());
 
        // 释放资源
        bis.close();
        bos.close();
        accept.close();
    }
}

1.3.3 客户端代码

public class DemoSocket {
    public static void main(String[] args) throws Exception{
        // 创建连接
        Socket socket = new Socket("127.0.0.1", 8888);
        // 获取网络输出流
        OutputStream socketOutput = socket.getOutputStream();
        // 创建本地缓冲输入流
        BufferedInputStream nativeInput = new BufferedInputStream(new FileInputStream(
                new File("C:\Users\Demo_Null\Desktop\socket.java")));
 
        // 创建缓冲数组
        byte[] bytes = new byte[1024 * 1024];
        // 读取的长度
        int len;
        // 循环读取字节并上传到服务器
        while ((len = nativeInput.read(bytes)) != -1) {
            socketOutput.write(bytes,0,len);
        }
        // 写结束标记
        socket.shutdownOutput();
 
        // 获取网络输入流
        InputStream inputStream = socket.getInputStream();
        // 创建缓冲流
        BufferedInputStream b =  new BufferedInputStream(inputStream);
        // 打印服务器写回信息
        while ((len = b.read(bytes)) != -1) {
            System.out.print(new String(bytes,0,len));
        }
 
        // 释放资源
        socket.close();
    }
}

补充: 服务端程序,需要事先启动,等待客户端的连接;客户端主动连接服务器端,连接成功才能通信,服务端不可以主动连接客户端。