文件发送(Java实现)

文件发送

上网冲浪时,我们经常会遇到文件的下载,传输,本案例通过Java代码实现文件简单的网络传输。

需要了解的知识点:

1.IO流

2.网络编程/多线程

1.IO流(在此仅介绍与本案例有关类功能)

1.1什么是IO

我们的电脑中,硬盘,内存等储存了我们平时使用电脑时所使用的数据,我们可以通过U盘,网络等获得他人的数据或将数据发送给他人。我们把这种数据的传输,称为一种数据的流动(即IO),通过流动的反向,分为输入(Input)和输出(Output),即流向内存时输入,流出内存是输出。

1.2IO的分类

通过数据的流向,分为输入流和输出流。
通过数据的类型,分为字节流和字符流。

1.3IO流顶级父类

含义
Java.io.OutputStream字节输出流顶层父类,抽象类,定义了写出数据方法write()。
Java.io.InputStream字节输入流顶层父类,抽象类,定义了读取数据方法read()。
Java.io.Writer字符输出流顶层父类,抽象类,定义了写出数据方法write()。
Java.io.Reader字符输入流顶层父类,抽象类,定义了读取数据方法read()。

1.4缓冲流

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率

    //创建字节缓冲输入流
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("123.txt"));
    //创建字节缓冲输出流
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("123.txt"));

2.网络编程/多线程

2.1进程和线程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序

2.2线程的创建

2.2.1继承Thread方式

Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。
Java中通过继承Thread类来创建并启动多线程的步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把
    run()方法称为线程执行体。
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法来启动该线程
    自定义线程类:
public class MyThread extends Thread { 
/*
** 重写run方法,完成该线程执行的逻辑
*/
 @Override 
 public void run() 
 { 
      for (int i = 0; i < 200; i++) 
      { 
          System.out.println("自定义线程正在执行!"+i);
      }
  }
}

实现类:

public class Demo1 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
    }
}
2.2.2Runnable接口实现

采用java.lang.Runnable也是常见的一种,我们只需要重写run方法
步骤如下:

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正
    的线程对象。
  3. 调用线程对象的start()方法来启动线程。
public class MyRunnable implements Runnable{ 
      public void run() 
      { 
          for (int i = 0; i < 20; i++) { 
              System.out.println("i= "+i);
          }
      }
}
public class Demo2{
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);
        t.start();        
    }
}
2.2.3匿名内部类创建线程

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:

public class Demo3{
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println("i = " + i);
                }
            }
        });
        t.start();
    }
}

2.3网络编程

概论在此不做赘述,仅给出TCP通信的内容。
TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
两端通信时步骤:

  1. 服务端程序,需要事先启动,等待客户端的连接。
  2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
    在Java中,提供了两个类用于实现TCP通信程序:
    客户端: java.net.Socket 类表示。创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
    服务端: java.net.ServerSocket 类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端的连接。
2.3.1 Socket类

Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
构造方法
public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。

Socket client = new Socket("127.0.0.1", 6666);

成员方法:
public InputStream getInputStream() : 返回此套接字的输入流。
如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
关闭生成的InputStream也将关闭相关的Socket。
public OutputStream getOutputStream() : 返回此套接字的输出流。
如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
关闭生成的OutputStream也将关闭相关的Socket。
public void close() :关闭此套接字。
一旦一个socket被关闭,它不可再使用。
关闭此socket也将关闭相关的InputStream和OutputStream 。 public void shutdownOutput() : 禁用此套接字的输出流。
任何先前写出的数据将被发送,随后终止输出流。

2.3.2 ServerSocket类

ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求。
构造方法
public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号

ServerSocket server = new ServerSocket(6666);

成员方法:
public socket accept(): 侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。

代码实现:

客户端实现:

public class SocketDemo {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost",6666);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\杂项\\text.png"));

        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] buffer = new byte[1024*8];
        int len;
        while ((len = bis.read(buffer))!=-1)
        {
            bos.write(buffer);
        }
        bis.close();
        bos.close();
        System.out.println("文件上传成功!");
        socket.close();
    }
}

服务器端实现:

public class SeverDemo{
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("服务器已经打开");
        Socket socket = serverSocket.accept();
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("1/"+System.currentTimeMillis()+".png"));
        byte[] buffer = new byte[1024*8];
        int len;
        while ((len = bis.read(buffer))!=-1)
        {
            bos.write(buffer);
        }
        bis.close();
        bos.close();
        System.out.println("文件下载完成");
        socket.close();
    }
}

代码改进:

文件上传的案例中,服务器只能为客户端服务器一次,之后服务器端程序就会结束。而我们必须做到让服务器程序不能结束,时时刻刻都要为客户端服务。而且同时可以为多个客户端提供服务器,做到一个客户端就要开启一个信新的线程。

public class SeverDemo{
    public static void main(String[] args) throws IOException {
        //创建
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("服务器已经打开");
        while (true)
        {
            Socket socket = serverSocket.accept();
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

                        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("1/"+System.currentTimeMillis()+".png"));
                        byte[] buffer = new byte[1024*8];
                        int len;
                        while ((len = bis.read(buffer))!=-1)
                        {
                            bos.write(buffer);
                        }
                        bis.close();
                        bos.close();
                        System.out.println("文件下载完成");
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }
}

实际成果:
在这里插入图片描述

学自:开课吧:JavaEE企业级开发工程师


版权声明:本文为mingzibeizhancao原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。