您的位置:首页 > 理论基础 > 计算机网络

Java 进阶,学习笔记-16 网络编程 文件上传,B/S 服务端编写

2020-01-14 02:23 585 查看

Java 进阶,网络编程

文章目录

  • 2 TCP 通信程序
  • 3 文件上传
  • 4 B/S 服务器
  • 1 网络编程

    网络编程即是实现多个计算机之间的通信,用于进行数据传输。

    1.1 网络通信协议及分类

    软件架构

    一般我们我们将软件分为两种结构,C/S 结构和 B/S 结构:
    C/S 结构:全称为 Client/Server 结构,是指客户端和服务器结构。
    B/S 结构:全称为 Browser/Server 结构,是指浏览器和服务器结构。
    无论哪种结构,都离不开网络的支持

    通信协议

    通信协议是对计算机必须遵守规则,只有遵守这些规则,计算机之间才能正常通信。协议中对数据的传输格式、传输速率、传输步骤等做了统一的规定,通信双方必须同时遵守,最终完成数据交换。

    TCP/IP

    传输控制协议/因特网互联协议(Transmission Control Protocol/Internet Protocol)是 Internet 中最基本,最广泛的协议。它定义了计算机之间如何连入因特网,以及数据如何在它们之间传输的标准。它的内容包含一系列的用于处理数据通信的协议,并采用了 4 层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求

    协议分类

    java.net
    中包含的类和接口,提供了低层次的网络通信细节。两种常见的通信协议,TCP(传输控制协议)和 UDP(用户数据报协议)

    TCP(Transimission Control Protocol)
    :面向连接的协议,就是传输数据之前,在发送断喝接收端建立逻辑连接,然后再传输数据。它提供了两台计算机之间可靠无差错的数据传输。发送数据前,都要进行三次握手来保证连接的可靠。一般的应用举例为下载文件、浏览网页等
    UDP(User Datagram Protocol)
    :面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在 64k 以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。一般的应用举例为视频会议,QQ 等

    三次握手

    第一次握手
    :客户端向服务器端发出连接请求,等待服务器确认
    第二次握手
    :服务器端向客户端回送一个响应,通知客户端收到了连接请求
    第三次握手
    :客户端再次向服务端发送确认信息,确认连接

    1.2 通信的三要素(简要概述)

    在网络编程中,要能正常进行两台计算机之间的正常通信,必须有协议,IP 地址和端口号

    协议

    协议即上面写的通信协议

    IP 地址

    指互联网协议地址,IP 地址用来给一个网络中的计算机设备做唯一编号。就相当于一个电话号码一样

    端口号

    网络中计算机之间的通信,本质上是两个进程的通信。每台计算机都有很多的进程。我们就用端口号来区分这些进程(程序)。网络端口的范围是 0~65535
    常见端口:

    端口号 服务 用途
    20、21 FTP FTP服务器以及客户端所开放的端口,用于上传、下载。进行FTP文件传输中,客户端首先连接到FTP服务器的21端口,进行用户的认证,认证成功后,要传输文件时,服务器会开一个端口为20来进行传输数据文件。
    22 ssh ssh 远程连接
    23 telnet 远程连接
    25 SMTP 简单邮件协议,用于发送邮件
    53 DNS 域名服务器
    80 HTTP 网页浏览
    443 HTTPS 网页浏览端口,能提供加密和通过安全端口传输的另一种HTTP
    3306 mysql mysql 进程服务

    利用 协议 + IP 地址 + 端口号 就可以表示网络中的进程了,那么进程间的通信就可以利用这个表示与其它进程进行交互

    2 TCP 通信程序

    TCP 通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端与服务端

    通信步骤:
    1、服务端程序,需要先启动,等待客户端的连接
    2、客户端主动连接服务端,连接成功才能通信,服务端不可以主动连接客户端

    在 Java 中,用两个类用于实现 TCP 通信程序
    客户端:

    java.net.Socket
    类表示,创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信
    服务端:
    java.net.ServerSocket
    类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端的连接

    Socket 类

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

    构造方法:

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

    注:回环地址主要用于网络软件测试以及本地机进程通信,无论什么程序,一旦使用回环地址发送数据,立即返回,不进行任何网络传输

    成员方法:

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

    ServerSocket 类

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

    构造方法:

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

    成员方法:

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

    下面来实现一个简单的程序之间的通信,简单步骤如下:
    1、服务端 启动,创建 ServerSocket 对象,等待连接
    2、客户端 启动,创建 Socket 对象,请求连接
    3、服务端 接受连接,调用 accept 方法,并返回一个 Socket 对象
    4、客户单 Socket 对象,获取 OutputStream,向服务端写出数据
    5、服务端 Socket 对象,获取 InputStream,读取客户端发送的数据 - 至此,客户端向服务端发送数据成功
    6、服务端 Socket 对象,获取 OutputStream,向客户端回写数据
    7、客户端 Socket 对象,获取 InputStream,解析回写数据
    8、客户端 释放资源,断开连接

    示例1:(客户单向服务器发送数据)

    // 服务端的编写
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    public class ServerTCP {
    public static void main(String[] args) throws IOException {
    System.out.println("服务端已开启 ...");
    // 创建 ServerSocket 对象,绑定端口,开始等待连接
    ServerSocket ss = new ServerSocket(8888);
    // 接收连接,用 accept 方法,返回 socket 对象
    Socket server = ss.accept();
    // 通过 socket 获取输入流
    InputStream is = server.getInputStream();
    // 读取数据并放入字节数组中
    byte[] bytes = new byte[1024];
    int len = is.read(bytes);
    String str = new String(bytes, 0, len);
    System.out.println(str);
    is.close();
    server.close();
    }
    }
    
    // 编写客户端
    import java.io.IOException;
    import java.net.Socket;
    import java.io.OutputStream;
    
    public class ClientTCP {
    public static void main(String[] args) throws IOException {
    // 创建 Server 对象
    System.out.println("客户端已开启 ... ");
    // 创建 Socket 对象,确定要连接哪里
    Socket client = new Socket("localhost", 8888);
    // 获取输出流,并向服务端发送数据
    OutputStream os = client.getOutputStream();
    os.write("你好,我是客户端发送过来的数据!".getBytes());
    // 关闭资源
    os.close();
    client.close();
    }
    }

    输出:

    # 客户端
    客户端已开启 ...
    客户端已发送数据!
    
    # 服务端
    服务端已开启 ...
    你好,我是客户端发送过来的数据!

    示例2:(服务端收到客户端发送的消息后回应消息)

    // 服务端的编写
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    public class ServerTCP {
    public static void main(String[] args) throws IOException {
    System.out.println("服务端已开启 ...");
    // 创建 ServerSocket 对象,绑定端口,开始等待连接
    ServerSocket ss = new ServerSocket(8888);
    // 接收连接,用 accept 方法,返回 socket 对象
    Socket server = ss.accept();
    // 通过 socket 获取输入流
    InputStream is = server.getInputStream();
    // 读取数据并放入字节数组中
    byte[] bytes = new byte[1024];
    int len = is.read(bytes);
    String str = new String(bytes, 0, len);
    System.out.println("客户端:" + str);
    
    // 服务端成功接收消息后向客户端回执消息
    System.out.println("向客户端发送确认消息!");
    OutputStream os = server.getOutputStream();
    os.write("收到消息了!".getBytes());
    os.close();
    is.close();
    server.close();
    }
    }
    
    // 编写客户端
    import java.io.IOException;
    import java.net.Socket;
    import java.io.OutputStream;
    import java.io.InputStream;
    
    public class ClientTCP {
    public static void main(String[] args) throws IOException {
    // 创建 Server 对象
    System.out.println("客户端已开启 ... ");
    // 创建 Socket 对象,确定要连接哪里
    Socket client = new Socket("localhost", 8888);
    // 获取输出流,并向服务端发送数据
    OutputStream os = client.getOutputStream();
    os.write("你好,我是客户端发送过来的数据!".getBytes());
    System.out.println("客户端已发送数据!");
    
    // 成功发送消息给服务端后,创建输入流接收服务端发送过来的确认消息
    InputStream is = client.getInputStream();
    byte[] bytes = new byte[1024];
    int len = is.read(bytes);
    String reply = new String(bytes, 0, len);
    System.out.println("服务端:" + reply);
    // 关闭资源
    os.close();
    client.close();
    }
    }

    输出:

    # 服务端
    服务端已开启 ...
    客户端:你好,我是客户端发送过来的数据!
    向客户端发送确认消息!
    
    # 客户端
    客户端已开启 ...
    客户端已发送数据!
    服务端:收到消息了!

    3 文件上传

    在实际生活中,我们都会经常用到文件上传功能,下面我们来实现一个简单的文件上传功能来实现文件上传

    步骤:
    1、客户端 创建输入流来读取图片数据
    2、客户端 通过 Socket 来创建一个输出流向服务端写入刚才读取的数据
    3、服务端 通过 Socket 来创建一个输入流向客户端读取数据
    4、服务端 创建输出流来写出数据到硬盘

    //  编写文件上传的服务端
    import java.io.*;
    import java.net.Socket;
    import java.net.ServerSocket;
    
    public class FileUploadServer {
    public static void main(String[] args) throws IOException{
    
    // 绑定端口
    ServerSocket ss = new ServerSocket(8888);
    // 通过 ServerSocket 创建一个 Socket 对象
    Socket server = ss.accept();
    // 创建一个输入流来读取客户端上传过来的数据
    BufferedInputStream bis = new BufferedInputStream(server.getInputStream());
    // 创建一个输出流来把数据保存在本地
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\2.zip"));
    
    // 正在接收文件
    System.out.println("正在接收文件 ... ");
    byte[] bytes = new byte[1024 * 8];
    int len;
    while((len = bis.read(bytes)) != -1) {
    bos.write(bytes, 0, len);
    }
    bos.close();
    bis.close();
    server.close();
    ss.close();
    System.out.println("文件已保存!");
    }
    }
    
    // 编写文件上传客户端
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.Socket;
    import java.io.InputStream;
    
    public class FileUploadClient {
    
    public static void main(String[] args) throws IOException {
    // 创建一个 Buffered 输入流来读取文件数据
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\1.zip"));
    // 创建 Socket 来向服务器方数据
    Socket client = new Socket("localhost", 8888);
    // 通过 socket 创建一个输出流来向服务端写出数据
    BufferedOutputStream bos = new BufferedOutputStream(client.getOutputStream());
    
    // 开始上传文件
    System.out.println("正在上传文件 ...");
    byte[] bytes = new byte[1024 * 8];
    int len;
    while((len = bis.read(bytes)) != -1) {
    bos.write(bytes, 0, len);
    bos.flush();
    }
    System.out.println("上传完毕!");
    bos.close();
    client.close();
    bis.close();
    }
    }

    文件上传的优化:

    // 编写文件上传服务端
    import java.io.*;
    import java.net.Socket;
    import java.net.ServerSocket;
    
    public class UploadServer {
    public static void main(String[] args) throws IOException{
    System.out.println(" ... 服务端已开启 ...");
    // 创建一个 ServerSocket 来绑定连接
    ServerSocket serverSocket = new ServerSocket(8888);
    
    // 循环接收上传
    while (true) {
    // 开始监听
    Socket socket = serverSocket.accept();
    
    // 利用多线程来接收多用户上传
    new Thread(() -> {
    try(
    BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\2.rar"));
    ){
    // 接收并在本地保存文件
    byte[] bytes = new byte[1024 * 8];
    int len;
    while ((len = bis.read(bytes)) != -1) {
    bos.write(bytes, 0, len);
    }
    System.out.println(socket.getInetAddress());
    // 向客户端确认信息
    OutputStream out = socket.getOutputStream();
    out.write("上传成功!".getBytes());
    
    // 关闭资源
    out.close();
    bos.close();
    bis.close();
    socket.close();
    System.out.println("文件已保存!");
    } catch (IOException i) {
    i.printStackTrace();
    }
    }).start();
    } // end while
    }
    }
    
    // 编写文件上传客户端
    import java.io.*;
    import java.net.Socket;
    
    public class UploadClient {
    public static void main(String[] args) throws IOException{
    // 创建读取本地文件的流
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\1.rar"));
    // 连接服务器
    Socket socket = new Socket("localhost", 8888);
    // 创建用于传送数据的流
    BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
    
    // 上传数据
    System.out.println("正在上传 ...");
    byte[] bytes = new byte[1024 * 8];
    int len ;
    while ((len = bis.read(bytes)) != -1) {
    bos.write(bytes, 0, len);
    }
    socket.shutdownOutput();
    
    // 解析服务器发送过来的数据
    InputStream in = socket.getInputStream();
    byte[] back = new byte[1024];
    len = in.read(back);
    System.out.println(new String(back, 0, len));
    
    // 释放资源
    in.close();
    socket.close();
    bis.close();
    }
    }

    4 B/S 服务器

    简单的模拟一个 B/S 服务器,用浏览器来访问资源,首先我们先准备好一个网页文件如下:

    Web
    index.html
    style.css
    bg1.png

    并将这个目录放置在与 src 同级目录下

    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class BServer {
    public static void main(String[] args) throws IOException{
    // 绑定端口
    ServerSocket serverSocket = new ServerSocket(8888);
    
    // 循环建立连接
    while(true) {
    // 开始监听
    Socket socket = serverSocket.accept();
    
    // 多线程处理请求
    new Thread(new WebRunnable(socket)).start();
    }
    }
    }
    
    class WebRunnable implements Runnable {
    private Socket socket;
    
    public WebRunnable(Socket socket) {
    this.socket = socket;
    }
    
    @Override
    public void run() {
    try {
    // 读取要访问资源的路径
    BufferedReader readWb = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    String request = readWb.readLine();
    String[] strArr = request.split(" ");
    String path = strArr[1].substring(1);
    System.out.println("GET " + path);
    
    // 读取资源
    FileInputStream fis = new FileInputStream(path);
    byte[] bytes = new byte[1024];
    int len = 0;
    
    // 向浏览器写入数据
    OutputStream out = socket.getOutputStream();
    out.write("HTTP/1.1 200 OK\r\n".getBytes());    // 响应头 - > 协议版本和成功与否
    out.write("Content -Type:text/html\r\n".getBytes());    // 相应头 - > 内容类型(记住,如果你的内容中包含图片,这句可以不写或者注意 Content 和 -Type 有个空格,否则无法正常显示网页)
    out.write("\r\n".getBytes());
    // 网页内容
    while((len = fis.read(bytes)) != -1) {
    out.write(bytes, 0, len);
    }
    
    // 关闭资源
    out.close();
    fis.close();
    readWb.close();
    socket.close();
    } catch (Exception ex) {
    // 不处理任何异常
    }
    }
    }

    本教程为 java 学习过程中所记录的笔记,有错误的地方请留言指正

    • 点赞
    • 收藏
    • 分享
    • 文章举报
    ilblogs 发布了23 篇原创文章 · 获赞 0 · 访问量 328 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: