Java 进阶,学习笔记-16 网络编程 文件上传,B/S 服务端编写
Java 进阶,网络编程
文章目录
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 学习过程中所记录的笔记,有错误的地方请留言指正
- 点赞
- 收藏
- 分享
- 文章举报
- Java网络编程学习笔记(1)用java编写客户端/服务器程序(简易)
- Java基础知识强化之网络编程笔记12:TCP之TCP协议上传文本文件并给出反馈
- php学习笔记(十一)文件上传类的编写
- Java 学习笔记(网络编程 之 URL)
- 黑马程序员--Java基础学习笔记【单例设计模式、网络编程、反射】
- java网络编程学习笔记之一(TCP/IP协议与UDP协议的区别和认识)
- 黑马程序员 java学习笔记 Day7:网络编程
- Java学习笔记10 网络编程
- [学习笔记]Java网络编程之UDP通讯
- 黑马程序员------java学习笔记之网络编程
- Java网络编程 入门学习笔记(一)
- java学习笔记16--I/O流和文件
- Java学习笔记―第十二章 Java网络编程入门
- Java学习笔记(十五)网络编程及常用类库
- Java学习笔记之网络编程基础-通过URLConnection获取HTML页面
- Java 网络编程 学习笔记一 基础知识
- 赵雅智_java 网络编程(3)之上传文件和图片
- java 网络编程 黑马程序员学习笔记(3)
- java学习笔记-----java网络编程四
- linux网络编程学习笔记之六 -----I/O多路复用服务端