Java进阶知识学习:网络编程

目录,更新ing,学习Java的点滴记录

  目录放在这里太长了,附目录链接大家可以自由选择查看--------Java学习目录

一丶网络编程入门

1. 软件结构

  1. C/S结构:全称为Client/Server结构,是指客户端和服务器结构,常见的程序有QQ,网盘等软件
    在这里插入图片描述
  2. B/S结构:全称为Browser/Server结构,是指浏览器和服务器结构,常见浏览器有谷歌,火狐等
    在这里插入图片描述
  3. 两种架构各有优势,但是无论哪种架构,都离不开网络的支持,网络编程,就是在一定的协议下,实现两台计算机的通信的程序.

2.网络通信协议

  1. **网络通信协议:**通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
  2. TCP/IP协议: 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
  3. TCP/IP四层结构图
    在这里插入图片描述
  4. TCP/IP协议的四层分别是应用层,传输层,网络层,链路层,每层负责不同的通信功能.
     1) 应用层(Application Layer):应用层包含所有的高层协议,包括:虚拟终端协议(TELNET),文件传输协议(FTP,File Transfer Protocol),电子邮件传输协议(SMTP,Simple Mail Transfer Protocol),域名服务(DNS,Domain Name Service),网上新闻传输协议(NNTP,NET News Transfer Protocol)和超文本传送协议(HTTP,HyperText Transfer Protocol)
     2) 传输层(Transport Layer):使源端和目的端机器上的对等实体进行通信.在这一层定义了两个端到端的协议:传输控制协议(TCP,Transmission Control Protocol)和用户数据报协议(UDP,User Datagram Protocol)。TCP是面向连接的协议,它提供可靠的报文传输和对上层应用的连接服务。为此,除了基本的数据传输外,它还有可靠性保证,流量控制,多路复用,优先权和安全性控制等功能。UDP是面向无连接的不可靠传输协议,主要用于不需要TCP的排序和流量控制等功能的应用程序。
     3) 网络层(Internet Layer):网络层是整个体系结构的关键部分,其功能是使主机可以把传输数据进行分组,然后将分组发送往任何网络,并使分组独立地传向目标,这些分组可能经由不同的网络,到达的顺序和发送的顺序也可能不同.高层如果需要顺序手法,那么就必须自行处理对分组的排序.
     4) 链路层(Link Layer):用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤,网线提供的驱动

3.通信协议分类

  1. 通信的协议还是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
  2. 数据包说明
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  3. java.net 包中提供了两种常见的网络协议的支持:
     1) UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。
      在这里插入图片描述
      特点:数据被限制在64kb以内,超出这个范围就不能发送了。
      数据报(Datagram):网络传输的基本单位
     2) TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手"",每次断开连接都要"四次挥手".”
      A. 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
      A.1 第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由syn=1知道,A要求联机
      A.2 第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机的seq+1),syn=1,ack=1,随机产生的seq=7654321的数据包
      A.3 第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1,则连接成功
      完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
      在这里插入图片描述
      B. 四次挥手:由于TCP的半关闭造成的.因为TCP连接是全双工的(即数据可以在两个方向上同时传递),所以进行关闭时每个方向上都要单独进行关闭.这个单方向的关闭就叫做半关闭.当一方完成它的数据发送任务,就发送一个FIN来向另一方通告将要终止这个方向的连接.
      B.1 :第一次挥手:客户机A把连接释放报文段首部的FIN(终止位)置1,其序号seq=u,序号位等于前面已传送过的数据的最后一个字节的序号+1。这个时候,A进入了FIN-WAIT-1(终止等待1)状态,等待B的确认。
      B.2 :第二次挥手:服务器B收到连接释放报文段后,同样会返回一个确认报文段。确认号是ack = u+1,而这个报文段自己的序号是v,等于B前面已传送过的数据的最后一个字节的序号加1。发送完确认报文段后,服务器B就会进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时应通知高层应用进程,因此从A到B这个方向的连接就释放了。但是此时TCP连接尚未完全关闭,仍处于一个半关闭(half-close)状态。简单地说,此时,客户机A已经没有数据要发送给服务器B了,但是B若发送数据,A仍要接收数据。这个状态可能会持续一段时间。客户机A收到来自服务器B的确认报文段后,就会进入FIN-WAIT-2(终止等待2)状态,等待来自服务器B的连接释放报文段.
      B.3 :第三次挥手:服务器B发出连接释放报文段是FIN置1。我们先假设B现在的序号为w,确认号则为上次已发送过的确认号ack = u+1。然后B就LAST-ACK(最后确认)状态,等待A的确认。
      B.4 :第四次挥手:客户机A收到B的连接释放报文段后,需要返回确认报文段。在确认报文段中,把ACK置1,确认号为ack = w + 1。而自己的序号为seq = u+1。(根据TCP标准,前面发送过的FIN报文段需要消耗一个序号)。然后进入TIME-WAIT(时间等待)状态。注意,这个时候TCP连接还未结束,还需要时间等待计数器(TIME-WAIT timer)设置的时间2MSL后,A才会重新进入CLOSED状态。时间MSL叫做最长报文段寿命,1MSL大概为2分钟。当A撤销相应的传输控制块TCB后,就结束了这次的TCP连接。
      在这里插入图片描述

4.网络编程三要素

  1. 三要素:通信协议,IP地址,端口号.利用协议+IP地址+端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。
  2. IP地址
     1) 概念:IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。
     2) 分类:
      a.IPv4:是一个32位的二进制数,通常被分为4个字节,表示成a.b.c.d 的形式,例如192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个.
      b.IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题
     3) 常用命令:
      a. 控制台输入ipconfig,可以查看本机IP地址
      b. 检查网络是否连通:ping 目标IP地址,如果显示能够成功接收数据,说明本机和目标IP地址之间可以通信
     4) 特殊IP地址:
      本机IP地址:127.0.0.1localhost
     5) IP地址=网络ID+主机ID
      网络ID:标识计算机或网络设备所有的网段
      主机ID:标识特定的主机或网络设备
      在这里插入图片描述
  3. 端口号
     1) 网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分这些进程呢?
     2) 如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。
     3) 概念:**端口号:用两个字节表示的整数,它的取值范围是065535**。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
     4) 常见端口号:网络端口(80),MySQL数据库端口(3306),Oracle数据库端口(1521),tomcat(8080)

二丶TCP通信程序

1. 概述

  1. TCP通信能实现两台计算机之间的数据交互,通信的两端要严格区分为客户端(Client)与服务端(Server)。
  2. 两端通信时步骤:
     1) 服务端程序,需要事先启动,等待客户端的连接。
     2) 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
  3. 在Java中,提供了两个类用于实现TCP通信程序:
     1) 客户端:java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
     2) 服务端:java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。
  4. 客户端和服务器端进行通信时,使用的是IO流对象,由于通信的数据不仅仅是字符,所以用到的IO流对象是字节流对象
  5. 服务器端需要明确的事情
     1) 多个客户端和服务器进行交互,服务器必须明确在特定时间是和哪个客户端进行交互—在服务器端有个方法交accept(),可以获取到请求的客户端对象,根据对象可以判断此时是哪个客户端在与服务器端进行交互
     2) 多个客户端同时和服务器进行交互,就需要使用多个IO流对象
      a. 服务器端是没有IO流的,但是服务器可以获取到发出请求的客户端对象Socket
      b. 使用每个客户端Socket中提供了IO和客户端进行交互,服务器使用客户端的字节输入流读取客户端发送的数据,并使用客户端的字节输出流给客户端返回数据

2. Socket类

  1. Socket 类:该类实现客户端套接字,是计算机之间通信的一种约定或一种方式,通过Socket这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据.
  2. 每个客户端都使用一个Socket对象表示,服务器端使用ServerSocket等待客户端的连接
    在这里插入图片描述
  3. 构造方法
    public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。
     回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。
     如:Socket client = new Socket(“127.0.0.1”, 6666);
  4. 成员方法
     1) public InputStream getInputStream() : 返回此套接字的输入流。
      如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
      关闭生成的InputStream也将关闭相关的Socket。
     2) public OutputStream getOutputStream() : 返回此套接字的输出流。
      如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
      关闭生成的OutputStream也将关闭相关的Socket。
     3) public void close() :关闭此套接字。
      一旦一个socket被关闭,它不可再使用。
      关闭此socket也将关闭相关的InputStream和OutputStream 。
     4) public void shutdownOutput() : 禁用此套接字的输出流。
      任何先前写出的数据将被发送,随后终止输出流。

3.ServerSocket类

  1. ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。
  2. 构造方法
    public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。
     比如:ServerSocket server = new ServerSocket(6666);
  3. 成员方法
    public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。

4. TCP通信程序

  1. 通信步骤
     【服务端】启动,创建ServerSocket对象,等待连接。
     【客户端】启动,创建Socket对象,请求连接。
     【服务端】接收连接,调用accept方法,并返回一个Socket对象。
     【客户端】Socket对象,获取OutputStream,向服务端写出数据。
     【服务端】Scoket对象,获取InputStream,读取客户端发送的数据。
     到此,客户端向服务端发送数据成功。往下,服务端向客户端回写数据。
     【服务端】Socket对象,获取OutputStream,向客户端回写数据。
     【客户端】Scoket对象,获取InputStream,解析回写数据。
     【客户端】释放资源,断开连接。
  2. 示例代码
     客户端
/**
 * 客户端
 * 向服务器端发送请求,接收服务器端返回请求,读取返回数据
 */
public class TCPClient {

    public static void main(String[] args) throws IOException {
        //1. 创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1", 6666);
        //2. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //3. 使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        //write方法只能传入int或者字节数组,传入字符串的话要进行转换
        os.write("客户端向服务器发送第一次信息".getBytes());

        //下面是用来接收服务器返回的数据
        //4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //5. 使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
        byte[] mes = new byte[1024];
        int len = is.read(mes);
        System.out.println(new String(mes,0,len));
        //6. 释放资源
        socket.close();
    }
}

 服务器端

/**
 * 服务器端
 * 接收客户端请求,读取客户端发送的数据,给客户端写回数据
 */
public class TCPServer {

    public static void main(String[] args) throws IOException {
        //1. 创建服务器ServerSocket对象并且指定监听端口号
        ServerSocket serverSocket = new ServerSocket(6666);
        //2. 使用ServerSocket对象中的方法accpet(),获取到请求的客户端对象Socket
        Socket socket = serverSocket.accept();
        //3. 使用Socket对象中的方法getInputStream()获取字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //4. 使用字节输入流InputStream对象中的方法read,读取客户端发送的数据
        byte[] mes = new byte[1024];
        int len = is.read(mes);
        System.out.println(new String(mes,0,len));

        //下面是服务器端向客户端返回数据
        //5. 使用Socket对象中的方法getOutputStream获取字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //6. 使用字节输出流OutputStream对象中的方法write,给客户端写数据
        os.write("服务器端收到客户端数据".getBytes());
        //7. 关闭资源
        serverSocket.close();
    }
}

三丶UDP通信程序

1.概述

  1. UDP数据传递不需要利用IO流实现数据的传输
  2. 每个数据发送单元被统一封装成数据包的形式,发送方将数据包发送到网络中,数据包在网络中寻找它的接收方

2. DatagramSocket类

  1. 表示用来发送和接收数据报包的套接字。 数据报套接字是包投递服务的发送或接收点。
  2. 常用方法
    在这里插入图片描述

3. UDP通信程序

  1. 发送方通信步骤
     1) 创建发送端Socket对象——DatagramSocket
     2) 创建数据并将数据打包到DatagramPacket对象
     3) 通过Socket发送
     4) 释放相关资源
  2. 接收方通信步骤
     1) 创建接受端Socket对象——DatagramSocket
     2) 创建包DatagramPacket对象(数据接收容器)
     3) 调用接受方法接受数据
     4) 解析数据包对象,取出接受到的信息
     5) 释放资源
  3. 示例代码
     发送方
public static void main(String[] args) throws IOException {
        String data = "UDP发送方发送数据";
        //1. 实例化套接字,并指定发送方端口
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        //2. 指定数据目的地的地址,以及目标端口
        InetAddress destination = InetAddress.getByName("localhost");
        //3. 创建数据包,指定目标端口
        DatagramPacket datagramPacket =
                new DatagramPacket(data.getBytes(), data.getBytes().length, destination, 9999);
        //4. 发送数据
        datagramSocket.send(datagramPacket);
        //5. 释放资源
        datagramSocket.close();
    }

 接收方

public static void main(String[] args) throws IOException {
        //1. 实例化套接字,并指定接收端口
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        byte[] buf = new byte[1024];
        //2. 定义接收数据的数据包
        DatagramPacket datagramPacket = new DatagramPacket(buf, 0, buf.length);
        //3. 调用接收方法
        datagramSocket.receive(datagramPacket);
        String data = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
        System.out.println(data);
        // 4. 释放资源
        datagramSocket.close();
    }

四丶InetAddress和InetSocketAddress类

1.InetAddress类

  1. 介绍
     位于java.net包中,封装计算机的IP地址,不包含端口号
  2. 常用方法
    在这里插入图片描述
  3. 示例
public static void main(String[] args) throws UnknownHostException {
        //1. 通过getLocalHost()获得InetAddress对象
        InetAddress ia1 = InetAddress.getLocalHost();
        System.out.println("主机IP地址:"+ia1.getHostAddress());
        System.out.println("主机名:"+ia1.getHostName());

        //2. 通过getByName(域名)
        InetAddress ia3 = InetAddress.getByName("www.baidu.com");
        System.out.println("主机IP地址:"+ia3.getHostAddress());
        System.out.println("主机名:"+ia3.getHostName());

        //3. 根据getByName(IP地址)获取InetAddress对象
        InetAddress ia2 = InetAddress.getByName("localhost");
        System.out.println("主机IP地址:"+ia2.getHostAddress());
        System.out.println("主机名:"+ia2.getHostName());

    }

InetSocketAddress类

  1. 介绍
     位于java.net包中,此类用于实现IP套接字地址(IP地址+端口号),用于Socket通信
  2. 构造方法
    在这里插入图片描述
  3. 常用方法
    在这里插入图片描述
  4. 示例
public static void main(String[] args) throws UnknownHostException {
        // 创建对象方式1
        InetSocketAddress isa1 = new InetSocketAddress("localhost", 8888);
        // 创建对象方式2
        InetAddress localHost = InetAddress.getLocalHost();
        InetSocketAddress isa2 = new InetSocketAddress(localHost, 9999);

        System.out.println("主机名称:"+isa1.getHostName());
        System.out.println("端口号:"+isa1.getPort());
    }

2.URL类

  1. URL(Uniform Resource Locator)介绍
     1) 统一资源定位符,由四部分组成:协议,存放资源的主机域名,端口号和资源文件名
      https://www.baidu.com:80/index.html
     2) URL是指向互联网资源的指针,资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询
  2. 构造方法
    在这里插入图片描述
  3. 常用方法
    在这里插入图片描述
  4. 示例
public static void main(String[] args) throws MalformedURLException {
        URL url = new URL("https://www.baidu.com:80/index.html");
        System.out.println("协议名称:"+url.getProtocol());
        System.out.println("域名:"+url.getHost());
        System.out.println("端口号:"+url.getPort());
        System.out.println("路径"+url.getPath());
    }

五丶综合案例

1.文件上传

  1. 流程图解
     1) 【客户端】使用本地字节输入流,从硬盘读取文件数据到程序中。
     2) 【客户端】使用网络字节输出流(Socket提供),写出文件数据到服务端。
     3) 【服务端】使用网络字节输入流(Socket提供),读取文件数据到服务端程序。
     4) 【服务端】使用本地字节输出流,写出文件数据到服务器硬盘中。
     5) 【服务端】使用网络字节输出流(Socket提供), 向客户端返回"上传成功"信息
     6) 【客户端】使用网络字节输入流(Socket提供), 读取服务器返回数据
     7) 释放资源
    在这里插入图片描述
  2. 文件上传客户端
/**
 * 文件上传客户端
 */
public class UploadClient {

    public static void main(String[] args) throws IOException {
        //1. 创建本地文件输入流FileInputStream对象,绑定要上传的文件数据
        FileInputStream fis = new FileInputStream(new File("d:\\test.txt"));
        //2. 创建客户端对象,构造时绑定服务器IP地址和端口号
        Socket socket = new Socket("localhost", 9999);
        //3. 使用Socket中的getOutputStream,获取网络字节输出流OutputStream
        OutputStream os = socket.getOutputStream();
        //4. 使用本地文件输入流FileInputStream对象中read方法,读取文件内容
        byte[] message = new byte[1024];
        int len = 0;
        while((len=fis.read(message))!=-1){
            //5. 使用网络字节输出流OutputStream对象中write方法,将读取到内容上传到服务器
            os.write(message,0,len);
        }
        //6. 使用Socket中的getInputStream,获取网络字节输出流InputStream
        InputStream is = socket.getInputStream();
        while((len=is.read(message))!=-1){
            //7. 使用网络字节输入流InputStream对象中方法,获取服务器端返回的数据
            System.out.println(new String(message,0,len));
        }
        //8. 释放资源(FileInputStream,Socket)
        fis.close();
        socket.close();
    }
}
  1. 文件上传服务器端
/**
 * 文件上传服务器端
 */
public class UploadServer {

    public static void main(String[] args) throws IOException {
        //1. 创建服务器端ServerSocket对象,指定监听端口号
        ServerSocket serverSocket = new ServerSocket(9999);
        //2. 调用accept方法获得Socket对象,监听客户端信息
        Socket socket = serverSocket.accept();
        //3. 使用socket对象中的getInputStream,获得网络字节输入流对象
        InputStream is = socket.getInputStream();
        //4. 将获取到的数据保存到指定文件夹,因此要先判断文件夹是否存在
        File file = new File("d:\\upload");
        if (!file.exists()){
            file.mkdir();
        }
        //5. 创建本地文件输出流对象FileOutputStream,构造时指定输出目的地
        FileOutputStream fos = new FileOutputStream(new File("d:\\upload\\test.txt"));
        //6. 使用网络字节输入流对象方法read,读取客户端传递数据
        int len=0;
        byte[] message = new byte[1024];
        while((len=is.read(message))!=-1){
            //7. 使用本次字节输出流对象FileOutputStream的write方法,保存文件
            fos.write(message,0,len);
        }
        //8. 使用socket的方法得到OutputStream对象
        OutputStream os = socket.getOutputStream();
        //9. 使用网络字节输出流OutputStream对象向客户端返回数据
        os.write(new String("上传成功").getBytes());
        //10. 释放资源
        fos.close();
        socket.close();
    }
}
  1. 以上代码,先运行服务器端,然后运行客户端,可以发现文件是成功复制的,但是此时客户端和服务器端都处于一个阻塞状态,没有自行关闭
  2. 处理阻塞问题
     1) 原因:在于两个代码中都含有一个read()方法,在Java.io包中的read方法介绍是:从此输入流中读取数据,如果没有输入可用,则此方法将阻塞.在Client中第一个read方法是读取本地文件然后通过网络字节输出流发送到服务器中,但是发送完之后,传递给服务器的文件是没有结束标记的,于是在Server中的read方法中,无法满足while循环的跳出条件因此就在此处阻塞了,Server后面的方法也就无法继续执行了,这造成了server的阻塞,与此同时,Client中第二个read方法需要读取服务端传递的网络字节输入流,但是由于Server的阻塞,导致没有向客户端反馈数据,因此Client端也阻塞了
     2) 解决方案:在客户端向服务器端发送完文件之后,调用socket.shutdownOutput()方法,告知服务器已经结束了传送,将文件的读取结束标记传递给了服务器
    在这里插入图片描述

六丶案例优化

1. 文件名称写死问题

  1. 服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优化,保证文件名称唯一
  2. 示例代码
		/**
         * 优化1:文件名写死问题解决
         * 文件命名规则:时间毫秒数+文件名后缀
         * 防止同名文件被覆盖
         */
        String filename=System.currentTimeMillis()+".txt";
        //5. 创建本地文件输出流对象FileOutputStream,构造时指定输出目的地
        FileOutputStream fos = new FileOutputStream(new File(file+"\\"+filename));

2. 循环接收问题

  1. 服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件
  2. 示例代码
    在这里插入图片描述

3.效率问题

  1. 服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化,对于线程知识不清楚的小伙伴,这篇文章可以帮助你–>线程
  2. 示例代码
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41649001/article/details/106768549