day18
复习
1.缓冲流【重点】
a。作用:对普通类的增强(性能的增强)
b。BufferedWriter 特有方法: public void newLine();
c。BufferedReader 特有方法: public String readLine();
2.转换流【重点】
转换流可以在读写文件时指定编码
OutputStreamWriter 转换输出流 写数据时指定编码
InputStreamReader 转换输入流 读数据时指定编码
3.序列化流
操作对象的流
ObjectOutputStream 对象的输出流(序列化流),写对象
ObjectInputStream 对象的输入流(反序列化流),读对象
4.commons-io工具包
IOUtis 复制大的或者小的文件
FileUtils 复制文件也能复制文件夹
5.装饰者设计模式
作用: 在不该变原来类的代码,也不使用继承的基础上对某个类的功能进行增强
步骤:
a.同一个接口(被装饰类和装饰类实现同一个接口)
b.含有引用(装饰类内部必须具有被装饰类的引用)
c.对需要增强的方法进行增强
d.对不需要增强的方法调用被装饰类的同名方法
6.打印流
能方便的打印各种数据类型
PrintStream
print(各种类型)
println(各种类型)
今日内容
- 网络编程概念(网络通信协议, IP地址, 端口号)
- TCP 网络编程
- 综合案例(文件上传)
网络编程入门
软件架构介绍
- C/S 架构: 客户端/服务器
- B/S 架构: 浏览器/服务器
网络通信协议
-
网络通信协议概念
网络: 互联网/局域网
通信: 数据传输
协议: 规则规范
-
TCP/IP 协议
TCP/IP协议:传输控制协议/因特网互联协议, Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。

Java中支持的常见协议
java.net
包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net
包中提供了两种常见的网络协议的支持:
-
TCP 协议: 传输控制协议
传输控制协议, TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
优点: 保证数据是完整的, 安全的
缺点: 性能较低
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可
靠。- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。服务器你死了吗?
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。我活着啊!!
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。我知道
了!!
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可
-
UDP 协议: 用户数据报协议
UDP协议是一个面向无连接的协议。传输数据时,不需
要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应
用中,例如视频会议、QQ聊天等。优点: 性能较高
缺点: 不能保证数据完整和不能保证安全性.
网络编程的三要素[重点]
协议
协议:计算机网络通信必须遵守的规则,已经介绍过了,不再赘述。
IP地址
-
IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。
-
127.0.0.1 本机地址(回环地址), Localhost 只是它另外一个名称(访问localhost,内部又把 localhost 翻译为了 127.0.0.1)
-
IP地址分类:
- IPv4:是一个32位的二进制数,通常被分为4个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100 。
其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。
有资料显示,全球IPv4地址在2011年2月分配完毕。 - IPv6:为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
- IPv4:是一个32位的二进制数,通常被分为4个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100 。
-
特殊的IP地址
本机IP地址: 127.0.0.1 、 localhost 。 -
常用的IP地址相关命令
ping 空格 IP地址 ping 220.181.57.216 ping www.baidu.com
端口号
如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。
端口号:用两个字节表示的整数,它的取值范围是0-65535。其中,0-1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。
InetAddress类的使用
在Java中,可以使用java.net.InetAddress类来表示一个IP地址:
/**
InetAddress类概述
* 一个该类的对象就代表一个IP地址对象。
InetAddress类成员方法
* static InetAddress getLocalHost()
* 获得本地主机IP地址对象
* static InetAddress getByName(String host)
* 根据IP地址字符串或主机名获得对应的IP地址对象
* String getHostName();获得主机名
* String getHostAddress();获得IP地址字符串
*/
public class InetAddressDemo01 {
public static void main(String[] args) throws Exception {
// 获得本地主机IP地址对象
InetAddress inet01 = InetAddress.getLocalHost();
// pkxingdeMacBook-Pro.local/10.211.55.2
// 主机名/ip地址字符串
System.out.println(inet01);
// 根据IP地址字符串或主机名获得对应的IP地址对象
// InetAddress inet02 = InetAddress.getByName("192.168.73.97");
InetAddress inet02 = InetAddress.getByName("baidu.com");
System.out.println(inet02);
// 获得主机名
String hostName = inet01.getHostName();
System.out.println(hostName);
// 获得IP地址字符串
String hostAddress = inet01.getHostAddress();
System.out.println(hostName);
System.out.println(hostAddress);
}
}
TCP通信[重点]
TCP通信分为客户端和服务器
- 客户端
- 服务器端
TCP协议是面向连接的通信协议,即在传输数据前先在客户端和服务器端建立逻辑连接,然后再传输数据。它
提供了两台计算机之间可靠无差错的数据传输。TCP通信过程如下图所示:

TCP中的两个重要的类[重点]
- Socket类, 代表客户端类(套接字)
- ServerSocket类, 代表服务器端类(服务器套接字)
- 客户端和服务器端对象都具有
getOutputStream()
,getInputStream()
方法,具体的输入输出流是根据具体在客户端还是服务器端写的获取输入输出流. 对于服务器来说, 在服务器端写的 通过获取的 客户端对象, 去调用的输入输出流也是相对于服务器而言的. - 服务器客户端之间只有两条流, 输入 输出是相对而言的 客户端写给服务器端的东西实在客户端的输出流中, 这条流需要服务器端是通过客户端对象调用获取输入流来获取.
Socket类的介绍和使用
-
构造方法
public Socket(String ip, int port)
: 参数:ip为服务器IP地址, port 为服务器端口号此构造, 会根据传入的参数, 自动去连接服务器,
如果连接成功, 对象正常创建
如果连接失败, 直接抛出异常
-
常用方法
public OutputStream getOutputStream()
;获得连接通道字节输出流对象public InputStream
:获取连接通道中字节输入流对象public void shutDownoutput()
: 关闭连接通道中的输出流,相当于是告知服务器关闭了输出流public void shutDownInput()
: 关闭连接通道中的输入流public void close()
关闭客户端对象
ServerSocket类的介绍和使用
-
构造方法
public ServerSocket(int port)
: 指定服务器端使用的端口号 -
常用方法
public Socket accept()
: 接收连接到服务器的那个Socket对象,如果暂时没有客户端, 该方法会阻塞.publci void close()
; 关闭服务器端 -
注意
在服务器中, 使用accept()方法返回的客户端对象, 再通过客户端对象调用 getOutputStream()方法, 此时获取的字节输出流 是相对于服务器端来说的
TCP单向和双向通信

简单的TCP 通信实现(单向通信)
客户端给服务器发送信息,服务器不回!
public class SocketDemo {
public static void main(String[] args) throws IOException {
//1.创建Socket对象
Socket socket = new Socket("127.0.0.1",8888);
System.out.println("连接服务器成功...");
//2.获取输出流
OutputStream out = socket.getOutputStream();
//3.调用输出流的write方法
out.write("你好我是客户端!".getBytes());
System.out.println("数据发送成功...");
//4.释放资源
out.close();
socket.close();
System.out.println("客户端关闭了...");
}
}
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//1.创建ServerSocket对象
ServerSocket server = new ServerSocket(8888);
System.out.println("服务器启动了...");
//2.接收连接到的客户端对象
Socket socket = server.accept(); //阻塞
System.out.println("有客户端来了...");
//3.获取输入流
InputStream in = socket.getInputStream();
//4.调用输入流的read方法
byte[] bs = new byte[1024];
int len = in.read(bs);
System.out.println("客户端说:"+new String(bs,0,len));
//5.释放资源
in.close();
socket.close();
server.close();
}
}
简单的TCP 通信实现(双向通信)
客户端给服务器发信息,服务器接收到之后,给客户端回信息
public class SocketDemo {
public static void main(String[] args) throws IOException {
//客户端步骤:
//1.创建Socket对象
Socket socket = new Socket("127.0.0.1",9999);
System.out.println("连接成功...");
//2.获取输出流
OutputStream out = socket.getOutputStream();
//3.调用输出流的write方法
out.write("你好,我又来了...".getBytes());
System.out.println("发送成功...");
//==读取服务器回的信息==
//4.获取输入流
InputStream in = socket.getInputStream();
//5.调用输入流的read方法
byte[] bs = new byte[1024];
int len = in.read(bs);
System.out.println("服务器回复:" + new String(bs, 0, len));
//================
//6.释放资源
in.close();
out.close();
socket.close();
System.out.println("客户端关闭...");
}
}
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//服务器的步骤
//1.创建ServerSocket对象
ServerSocket server = new ServerSocket(9999);
System.out.println("服务器启动...");
//2.接收连接到的客户端对象
Socket socket = server.accept();//阻塞
System.out.println("客户端来了...");
//3.获取输入流
InputStream in = socket.getInputStream();
//4.调用输入流的read方法
byte[] bs = new byte[1024];
int len = in.read(bs);
System.out.println("客户端说:" + new String(bs, 0, len));
//=======回信息========
//5.获取输出流
OutputStream out = socket.getOutputStream();
//6.调用输出流的write方法
out.write("您的信息我收到了,安息吧!".getBytes());
System.out.println("成功回复...");
//====================
//7.释放资源
out.close();
in.close();
socket.close();
server.close();
System.out.println("服务器关闭...");
}
}
综合案例: 文件上传
文件上传案例分析
- 【客户端】输入流,从硬盘读取文件数据到程序中。
- 【客户端】输出流,写出文件数据到服务端。
- 【服务端】输入流,读取文件数据到服务端程序。
- 【服务端】输出流,写出文件数据到服务器硬盘中。
- 【服务端】获取输出流,回写数据。
- 【客户端】获取输入流,解析回写数据。


文件上传案例实现
//客户端代码实现
public class SocketDemo {
public static void main(String[] args) throws IOException {
//文件上传客户端步骤:
//1.创建Socket
Socket socket = new Socket("127.0.0.1",9999);
System.out.println("连接成功...");
//2.获取输出流
OutputStream out = socket.getOutputStream();
//3.创建文件的输入流
FileInputStream fis = new FileInputStream("555.png");
//循环:一边读文件,一边发送
byte[] bs = new byte[1024];
int len = 0;
while ((len = fis.read(bs)) != -1) {
out.write(bs,0,len);
}
//添加一句代码,告知服务器文件发送完毕
socket.shutdownOutput();
System.out.println("文件上传完毕...");
//4.获取输入流
InputStream in = socket.getInputStream();
//5.读取服务器回复的信息
len = in.read(bs);
System.out.println("服务器回复:" + new String(bs, 0, len));
//6.释放资源
in.close();
fis.close();
out.close();
socket.close();
System.out.println("客户端关闭...");
}
}
//服务器代码实现
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//文件上传服务器步骤:
//1.创建ServerSocket
ServerSocket server = new ServerSocket(9999);
System.out.println("服务器启动...");
//2.获取客户端
Socket socket = server.accept();
System.out.println("有客户端来了...");
//3.获取输入流
InputStream in = socket.getInputStream();
//4.创建文件的输出流
FileOutputStream fos = new FileOutputStream(System.currentTimeMillis()+".png");
//循环:一边读数据,一边写文件
byte[] bs = new byte[1024];
int len = 0;
while ((len = in.read(bs)) != -1) {
fos.write(bs, 0, len);
}
System.out.println("文件保存成功...");
//5.获取输出流
OutputStream out = socket.getOutputStream();
//6.给客户端回信息
out.write("您的图片我收到了,安息吧~~".getBytes());
System.out.println("消息回复成功...");
//7.释放资源
out.close();
fos.close();
in.close();
socket.close();
server.close();
System.out.println("服务器关闭...");
}
}
服务器多线程文件上传
public class ServerSocketMultiDemo {
public static void main(String[] args) throws IOException {
//文件上传服务器步骤:
//1.创建ServerSocket
ServerSocket server = new ServerSocket(9999);
System.out.println("服务器启动...");
//2.获取客户端
while (true) {
Socket socket = server.accept();
System.out.println("有客户端来了...");
//创建线程
new Thread(()->{
try {
//3.获取输入流
InputStream in = socket.getInputStream();
//4.创建文件的输出流
FileOutputStream fos = new FileOutputStream(System.currentTimeMillis() + ".png");
//循环:一边读数据,一边写文件
byte[] bs = new byte[1024];
int len = 0;
while ((len = in.read(bs)) != -1) {
fos.write(bs, 0, len);
}
System.out.println("文件保存成功...");
//5.获取输出流
OutputStream out = socket.getOutputStream();
//6.给客户端回信息
out.write("您的图片我收到了,安息吧~~".getBytes());
System.out.println("消息回复成功...");
//7.释放资源
out.close();
fos.close();
in.close();
socket.close();
} catch (IOException ie) {
ie.printStackTrace();
}
}).start();
}
//server.close();
//System.out.println("服务器关闭...");
}
}
模拟B/S架构服务器
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//1.创建服务器
ServerSocket server = new ServerSocket(12345);
System.out.println("服务器启动...");
//2.接收客户端
while (true) {
Socket socket = server.accept();
System.out.println("客户端来了..");
//3.获取输入流
InputStream in = socket.getInputStream();
//4.使用输入流的read方法,把浏览器发送过的数据读取出来
//我们现在只想读取,客户端要的那个文件
//a.只读第一行
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line = br.readLine();
//b.从中获取浏览器想要的那个文件名字
String[] splits = line.split(" ");
String fileName = splits[1].substring(1);
System.out.println("浏览器想要:" + fileName);
//5.把浏览器想要的文件,读取出来,发送给浏览器
FileInputStream fis = new FileInputStream(fileName);
OutputStream out = socket.getOutputStream();
byte[] bs = new byte[1024];
int len = 0;
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
out.write("\r\n".getBytes());
while ((len = fis.read(bs)) != -1) {
out.write(bs, 0, len);
}
//5.释放资源
in.close();
socket.close();
}
// server.close();
// System.out.println("服务器关闭..");
}
}
模拟HTTP服务器
// 基于 http 协议的服务器
public class HttpServer {
// 获取 1.jpg 协议版本
// 请求规则 GET 资源名称 HTTP/1.1
// 状态码 200 表示成功 HTTP/1.1
// 响应规则 200 OK 协议版本
// Content-Type: text/html(网页) image/jpeg(图片) ....
// 空行
// 内容
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
// 不断处理连接信息
while(true) {
// 建立一个新的连接
Socket socket = serverSocket.accept();
// 使用一个新的线程处理连接
new Thread(() -> {
// 处理连接, GET /1.jpg HTTP/1.1
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
// 读取第一行
String line = reader.readLine();
// 找到资源部分
String resource = line.split(" ")[1];
resource = resource.substring(1);
System.out.println(resource);
// 返回响应, 向输出流写入资源
// 响应规则 协议版本 200 OK
// Content-Type: text/html(网页) image/jpeg(图片) ....
// 空行
// 内容
try (OutputStream out = socket.getOutputStream()) {
out.write("HTTP/1.1 200 OK\n".getBytes());
if(resource.endsWith(".html")) {
out.write("Content-Type: text/html\n".getBytes());
} else if(resource.endsWith(".jpg")) {
out.write("Content-Type: image/jpeg\n".getBytes());
}
out.write("\n".getBytes());
Files.copy(Paths.get(resource), socket.getOutputStream());
}
} catch (IOException e) {
} // finally close
}).start();
}
}
}
今日小结
能够说出TCP协议特点
TCP的特点: 面向有链接(先建立连接后才能数据传输)
UDP的特点: 面向无连接(只需要发送数据,不需要关心对象是否开机,是否存在)
能够说出TCP协议下两个常用类名称
Socket:客户端类(构造方法,成员方法)
ServerSocket:服务器类(构造方法和成员方法)
"能够编写TCP协议下字符串数据传输程序"【TCP的单、双向通信!!3遍!!】
能够理解TCP协议下文件上传案例
什么叫上传: 客户端把其硬盘中文件,通过网络发送到服务器端,并保存到服务器的硬盘中
上传过程: a.客户端读取本地文件 b.通过输出流发送给服务器
c.服务器读取输入流数据 d.保存到服务器的本地
能够理解TCP协议下BS案例
我们不需要自己编写客户端,使用浏览器当过客户端,去访问服务器
访问时: 127.0.0.1:8888
网友评论