文件发送
上网冲浪时,我们经常会遇到文件的下载,传输,本案例通过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类来创建并启动多线程的步骤如下:
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把
run()方法称为线程执行体。 - 创建Thread子类的实例,即创建了线程对象
- 调用线程对象的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方法
步骤如下:
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正
的线程对象。 - 调用线程对象的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)。
两端通信时步骤:
- 服务端程序,需要事先启动,等待客户端的连接。
- 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
在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企业级开发工程师