学习目标
- 能够使用Junit进行单元测试
1.在当前模块下新建lib文件夹,拷贝junit的jar包,把jar包添加到图书馆中
2.在要执行的方法上添加一个@Test注解
3.点击方法左边的绿色三角或者右键选择方法,选择run 方法名称运行方法
- 能够辨别UDP和TCP协议特点
udp:面向无连接的协议,通信双方不用连接连接,可以直接发送数据(视频聊天,视频会议...)
好处:效率高,耗资小
弊端:容易丢失数据
tcp:面向连接的协议,客户端和服务器必须经过3次握手建立逻辑连接,才能通信(文件的上传和下载,发送文件...)
好处:安全
弊端:效率低
- 能够说出TCP协议下两个常用类名称
客户端:Socket
服务器:ServerSocket accpet获取客户端
- 能够编写TCP协议下字符串数据传输程序
笔记中:TCP通信程序(重点)
- 能够理解TCP协议下文件上传案例(重点)
客户端:读取客户端硬盘上的文件,把文件上传到服务器,读取服务器回写的"上传成功!"
服务器:读取客户端上传的文件,把文件保存到服务器的硬盘上,给客户端回写"上传成功!"
- 能够理解TCP协议下BS案例(看图)
1.使用浏览器作为客户端访问服务器的某一个资源(index.html)
http://localhost:8080/day12/web/index.html==>URL 统一资源定位符
2.在服务器中获取客户端请求的资源路径(web/index.html)
3.服务器使用本地字节输入流,根据路径读取index.html文件
4.服务器在使用网络字节输出流,把读取到的index.html文件,写到客户端浏览器中显示
第一章 Junit单元测试
1.Junit介绍
Junit是一个Java语言的单元测试框架,简单理解为可以用于取代java的(部分)main方法。Junit属于第三方工具,需要导入jar包后使用。
2.Junit的基本使用(重点)
a.在当前模块下创建lib文件夹
b.把junit的jar包,拷贝到lib的文件夹中
c.把jar包添加到图书馆中
package com.itheima.demo01junit;
import org.junit.Test;
/*
junit作用:单独的测试某一个方法
使用步骤:
1.导入第三方的junit的jar包
2.在要执行的方法上添加一个@Test注解(需要导包)
3.点击方法左边的绿色三角或者右键选择方法名称,选择"Run 方法名称",运行方法
点击类左边的绿色三角或者右键选择类名,选择"Run 类名",可以运行类中所有被@Test修饰的方法
右键点击模块名称,选择"Run All Tests",可以运行模块中所有类中被@Test修饰的方法
*/
public class Demo01Junit {
@Test
public void show01(){
System.out.println("show01方法");
}
@Test
public void show02(){
System.out.println("show02方法");
}
@Test
public void show03(){
System.out.println("show03方法1");
System.out.println("show03方法2");
System.out.println("show03方法3");
System.out.println("show03方法4");
}
}
3.Junit的注意事项
package com.itheima.demo01junit;
import org.junit.Test;
/*
Junit的注意事项
1.没有添加@Test注解的方法,不能使用Junit运行
2.junit只能运行public修饰的,没有参数,没有返回值的,非静态方法
*/
public class Demo02Junit {
public void show01(){
System.out.println("Demo02Junit show01方法");
}
//java.lang.Exception: Method show02 should have no parameters 方法show02应该没有参数
//@Test
public void show02(int a){
System.out.println("Demo02Junit show02方法"+a);
}
//java.lang.Exception: Method show03() should be void 方法show03应该返回值类型是void
//@Test
public String show03(){
System.out.println("Demo02Junit show03方法");
return "你好";
}
//java.lang.Exception: Method show04() should not be static 方法shwo04应该没有被static修饰
//@Test
public static void show04(){
System.out.println("Demo02Junit show04方法");
}
//java.lang.Exception: Method show05() should be public 方法show05应该是public修饰
//@Test
void show05(){
System.out.println("Demo02Junit show05方法");
}
/*
可以定义一个能使用junit运行的方法
在方法中调用哪些不能运行的方法就可以了
*/
@Test
public void method(){
show01();
show02(10);
String s = show03();
System.out.println(s);
show04();
show05();
}
}
4.Junit相关注解
package com.itheima.demo01junit;
import org.junit.*;
/*
Junit相关注解
@Test:
可以单独执行某一个方法
@Before:
用来修饰方法,该方法会在每一个测试方法执行之前[自动]执行一次。
@After:
用来修饰方法,该方法会在每一个测试方法执行之后[自动]执行一次
@BeforeClass:
用来[静态]修饰方法,该方法会在所有测试方法之前[自动]执行一次,而且只执行一次
@AfterClass:
用来[静态]修饰方法,该方法会在所有测试方法之后[自动]执行一次,而且只执行一次
注意:
@Before,@After,@BeforeClass,@AfterClass:这四个注解修饰的方法,不能单独执行
@Before,@After,@BeforeClass,@AfterClass:这四个注解修饰的方法,会自动在@Test修饰的方法前后执行
*/
public class Demo03Junit {
@Test
public void show01(){
System.out.println("show01方法");
}
@Test
public void show02(){
System.out.println("show02方法");
}
@Test
public void show03(){
System.out.println("show03方法");
}
@Before
public void before(){
System.out.println("before方法!");
}
@After
public void after(){
System.out.println("after方法!");
}
@BeforeClass
public static void beforeClass(){
System.out.println("beforeClass方法!");
}
@AfterClass
public static void afterClass(){
System.out.println("afterClass方法!");
}
}
执行的结果:
beforeClass方法!
before方法!
show01方法
after方法!
before方法!
show02方法
after方法!
before方法!
show03方法
after方法!
afterClass方法!
备注:
Junit常用注解(Junit5.x版本)
* @BeforeEach:用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
* @AfterEach:用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
* @BeforeAll:用来静态修饰方法,该方法会在所有测试方法执行之前执行一次。
* @AfterAll:用来静态修饰方法,该方法会在所有测试方法执行之后执行一次
5.断言
-
作用
预测判断某个条件一定成立, 如果条件不成立,则直接奔溃(抛出断言异常) -
使用方式
两个参数: 第一个代表预判值, 第二个代表实际结果
如果预判正确就会绿色通过
如果预判错误就会红色失败
Assert.assertEquals(期望结果 , 实际结果);
package com.itheima.demo01junit;
import org.junit.Assert;
/*
断言
- 作用
预测判断某个条件一定成立, 如果条件不成立,则直接奔溃(抛出断言异常)
- 使用方式
两个参数: 第一个代表预判值, 第二个代表实际结果
如果预判正确就会绿色通过
如果预判错误就会红色失败
Assert.assertEquals(期望结果 , 实际结果);
*/
public class Demo04Junit {
public static void main(String[] args) {
//断言正常:程序正常执行
Assert.assertEquals(true,"aaa".length()==3);
//断言错误:AssertionError: expected:<true> but was:<false> 预期是true,得到的是false
//Assert.assertEquals(true,"aaaa".length()==3);
//断言正常:程序正常执行
Assert.assertEquals(10,Integer.parseInt("10"));
//断言错误:AssertionError: expected:<10> but was:<11> 预期是10,得到的是11
//Assert.assertEquals(10,Integer.parseInt("11"));
}
}
第二章 网络编程入门
网络编程三要素:协议,IP地址,端口号
1.软件结构
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。
2.网络通信协议
协议就是规则:就是计算机通信需要遵守的
udp:面向无连接的协议,通信的双方不用建立连接,可以直接发送数据
好处:效率高(速度快),耗资小
弊端:不安全,容易丢失数据
tcp:面向连接协议,客户端和服务器端必须经过3次握手建立逻辑连接,才能通信
好处:安全
弊端:效率低
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
第一次握手,客户端向服务器端发出连接请求,等待服务器确认。服务器你死了吗?
第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。我活着 啊!!
第三次握手,客户端再次向服务器端发送确认信息,确认连接。我知道了!!
3.IP地址
IP地址:就相当于计算机的身份号(唯一)
ip地址的作用:具有唯一性,在网络中可以通过ip地址找到另外一台计算机
ip地址分类
1.ipv4:ip地址由4个字节组成,一个字节8位(比特位1,0)
二进制:11001101.11001100.11000001.11001111
为了表示方便使用十进制:192.0.0.106
每个字节的范围:0-255(2^8),ip地址第一位不能为0
ip地址的数量:42亿
2^32=4294967296个
问题:随着计算机的增多,ip地址面临枯竭(全球IPv4地址在2011年2月分配完毕)不够用,就出了ipv6地址
2.ipv6:ip地址由16个字节组成,一个字节8位(比特位1,0)
ip地址的数量:
2^128=3.4028236692093846346337460743177e+38
3400000000000000000000000000000000000000000000000000000000000000
号称可以为地球上每一粒沙子编写一个ip地址
为了表示方便使用十六进制:fe80::a8a6:b83c:8b8b:2685%17
常用一些dos命令:dos窗口 win+r==>cmd==>dos窗口
1.ipconfig Windows IP 配置
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::a8a6:b83c:8b8b:2685%17
IPv4 地址 . . . . . . . . . . . . : 192.168.0.106
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.0.1
2.ping ip地址:测试你的电脑和指定ip地址的电脑是否可以连通
ping空格ip地址
C:\Users\Administrator>ping 192.168.0.222 没有ping通
正在 Ping 192.168.0.222 具有 32 字节的数据:
来自 192.168.0.106 的回复: 无法访问目标主机。
来自 192.168.0.106 的回复: 无法访问目标主机。
来自 192.168.0.106 的回复: 无法访问目标主机。
来自 192.168.0.106 的回复: 无法访问目标主机。
C:\Users\Administrator>ping 192.168.0.107 ping通
正在 Ping 192.168.0.107 具有 32 字节的数据:
来自 192.168.0.107 的回复: 字节=32 时间=3ms TTL=64
来自 192.168.0.107 的回复: 字节=32 时间=3ms TTL=64
来自 192.168.0.107 的回复: 字节=32 时间=5ms TTL=64
来自 192.168.0.107 的回复: 字节=32 时间=3ms TTL=64
C:\Users\Administrator>ping www.baidu.com
正在 Ping www.a.shifen.com [61.135.169.121] 具有 32 字节的数据:
来自 61.135.169.121 的回复: 字节=32 时间=6ms TTL=56
来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
ping 127.0.0.1 ping localhost ping本机的ip地址(你自己电脑的ip地址)
4.端口号
注意:每个网络软件都会分配一个端口号,通过这个端口号就可以找到这个软件
5.InetAddress类
package com.itheima.demo02InetAddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
/*
java.net.InetAddress:此类表示互联网协议 (IP) 地址。
静态方法:
static InetAddress getLocalHost() 返回本地主机。 你自己的电脑
static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
非静态方法:
String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
String getHostName() 获取此 IP 地址的主机名。
*/
public class Demo01InetAddress {
public static void main(String[] args) throws UnknownHostException {
show02();
}
/*
static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
参数:
String host:电脑名,ip地址,域名
static InetAddress[] getAllByName(String host)
*/
private static void show02() throws UnknownHostException {
//InetAddress inet = InetAddress.getByName("LAPTOP-8I2VG167");//LAPTOP-8I2VG167/192.168.136.48
//InetAddress inet = InetAddress.getByName("192.168.136.48");//LAPTOP-8I2VG167/192.168.136.48
//InetAddress inet = InetAddress.getByName("www.baidu.com");//www.baidu.com/182.61.200.6
InetAddress inet = InetAddress.getByName("www.itheima.com");//www.itheima.com/47.95.137.221
System.out.println(inet.getHostAddress());
System.out.println(inet.getHostName());
System.out.println(inet);
}
/*
static InetAddress getLocalHost() 返回本地主机。 你自己的电脑
UnknownHostException:未知主机异常
*/
private static void show01() throws UnknownHostException {
InetAddress inet = InetAddress.getLocalHost();
System.out.println(inet);//daofeng/192.168.136.73 打印对象名不是地址,重写了toString方法
//String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
String hostAddress = inet.getHostAddress();
System.out.println(hostAddress);//192.168.136.73
//String getHostName() 获取此 IP 地址的主机名。
String hostName = inet.getHostName();
System.out.println(hostName);//daofeng
}
}
第三章 TCP通信程序
1.TCP通信的概述
2.TCP通信的客户端(重点)
package com.itheima.demo03TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
创建TCP客户端
作用:和服务器经过3次握手建立逻辑连接,给服务器发送数据,读取服务器回写的数据
表示客户端的类:
java.net.Socket
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
套接字就是封装了IP地址和端口号的网络单位
构造方法:
Socket(InetAddress address, int port)创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
参数:
InetAddress address|String host:服务器的ip地址
int port:服务器的端口号
成员方法:
OutputStream getOutputStream()返回此套接字的输出流。
InputStream getInputStream() 返回此套接字的输入流。
----------------------------------------------------------------------------------------------
注意事项:
1.当我们创建客户端Socket对象的时候,就会通过服务器的ip地址和端口号找服务器进行三次握手
a.服务器已经启动,并且ip地址和端口号书写正确,握手成功
b.服务器没有启动,或者ip地址端口号书写错误,握手失败,抛出连接异常
ConnectException: Connection refused: connect
2.客户端和服务器之间进行读写数据必须使用Socket中提供的网络流对象
Socket中提供的流对象的数据源和目的地就是客户端和服务器
使用自己创建的流对象,数据源和目的地是本地硬盘
new FileInputStream("a.txt") new FileOutputStream("a.txt"); a.txt==>硬盘
----------------------------------------------------------------------------------------------
实现步骤(重点):
1.创建客户端Socket对象,构造方法绑定服务器的ip地址和端口号(三次握手)
2.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
5.使用网络字节输入流InputStream对象中的方法read,读取服务器发送的数据
6.释放资源(Socket)
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建客户端Socket对象,构造方法绑定服务器的ip地址和端口号(三次握手)
Socket socket = new Socket("localhost",8888);
//2.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
System.out.println("①客户端给服务器发送数据:你好服务器");
os.write("你好服务器".getBytes());
//4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//5.使用网络字节输入流InputStream对象中的方法read,读取服务器发送的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println("④客户端读取服务器发送的数据:"+new String(bytes,0,len));
//6.释放资源(Socket)
socket.close();
}
}
3.TCP通信服务器端(重点)
package com.itheima.demo03TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
TCP通信服务器端(重点)
作用:和客户端经过3次握手建立连接通路,读取客户端发送的数据,给客户端回写数据
表示服务器的类:
java.net.ServerSocket:此类实现服务器套接字。
构造方法:
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
成员方法:
Socket accept() 侦听并接受到此套接字的连接。
服务器调用accpet方法,就会出现监听状态,一直等待客户端连接服务器
获取客户端Socket对象
没有客户端连接服务器,此方法会阻塞(一直等待)
---------------------------------------------------------------------------------------------
实现步骤(重点):
1.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
2.使用服务器ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
3.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
5.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
6.使用网络字节输出流OutputStream对象中的方法write,给客户端发送数据
7.释放资源(Socket,ServerSocket)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
//2.使用服务器ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
System.out.println("----------------服务器已经启动,等待客户端连接--------------------");
Socket socket = server.accept();
//3.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println("②服务器读取客户端发送的数据:"+new String(bytes,0,len));
//5.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//6.使用网络字节输出流OutputStream对象中的方法write,给客户端发送数据
System.out.println("③服务器给客户端发送数据:收到谢谢");
os.write("收到谢谢".getBytes());
//7.释放资源(Socket,ServerSocket)
socket.close();
server.close();
}
}
服务器启动之后,服务器的accpet方法一直处于监听状态,等待客户端连接
4.TCP通信的流程
第四章 综合案例
1.文件上传案例需求分析
2.文件上传的客户端(重点)
package com.itheima.demo04TCPUpload;
import java.io.*;
import java.net.Socket;
/*
文件上传的客户端(重点)
作用:读取本地要上传的文件,把文件上传到服务器,读取服务器回写的"上传成功"
文件的上传就是文件的复制:
数据源: c:\\1.jpg
目的地: 服务器
实现步骤:
1.创建本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
2.创建客户端Socket对象,构造方法中封装服务器ip地址和端口号(3次握手)
3.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
4.使用本地字节输入流FileInputStream对象中的方法read,读取要上传的文件
5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传服务器
6.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
7.使用网络字节输入流InputStream对象中的read,读取服务器回写的"上传成功!"
8.释放资源(fis,Socket)
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("c:\\1.jpg");
//2.创建客户端Socket对象,构造方法中封装服务器ip地址和端口号(3次握手)
Socket socket = new Socket("127.0.0.1",9999);
//3.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//4.使用本地字节输入流FileInputStream对象中的方法read,读取要上传的文件
byte[] bytes= new byte[1024];
int len = 0;
while ((len=fis.read(bytes))!=-1){
//5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传服务器
os.write(bytes,0,len);
}
/*
解决:客户端写完文件之后,给服务器写一个结束标记
void shutdownOutput() 禁用此套接字的输出流。
对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列(结束标记)。
*/
socket.shutdownOutput();
//6.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//7.使用网络字节输入流InputStream对象中的read,读取服务器回写的"上传成功!"
System.out.println("333333333333333333333333333333333");
while ((len=is.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
System.out.println("444444444444444444444444444444444");
//8.释放资源(fis,Socket)
fis.close();
socket.close();
}
}
3.文件上传的服务器端(重点)
package com.itheima.demo04TCPUpload;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
文件上传的服务器端(重点)
作用:读取客户端上传的文件,把文件保存到服务器的硬盘上,给客户端回写"上传成功!"
文件的上传就是文件的复制:
数据源: 客户端上传的文件
目的地: 服务器的硬盘上 d:\\upload\\1.jpg
实现步骤:
1.判断d盘是否有upload文件夹,没有则创建
2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
10.释放资源(fos,socket,server)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.判断d盘是否有upload文件夹,没有则创建
File file = new File("d:\\upload");
if(!file.exists()){
file.mkdir();
}
//2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
ServerSocket server = new ServerSocket(9999);
//3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
System.out.println("--------------服务器已经启动,等待客户端上传文件---------------");
Socket socket = server.accept();
//4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
FileOutputStream fos = new FileOutputStream("d:\\upload\\1.jpg");
//6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
byte[] bytes = new byte[1024];
int len = 0;
System.out.println("11111111111111111111111111111111111");
while ((len=is.read(bytes))!=-1){
//7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
fos.write(bytes,0,len);
}
System.out.println("2222222222222222222222222222222222222");
//8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
os.write("上传成功!".getBytes());
//10.释放资源(fos,socket,server)
fos.close();
socket.close();
server.close();
}
}
问题:客户端没有读取到服务器回写的"上传成功",服务器和客户端都没有执行结束
4.文件上传的阻塞问题(重点)
/*
解决:客户端写完文件之后,给服务器写一个结束标记
void shutdownOutput() 禁用此套接字的输出流。
对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列(结束标记)。
*/
socket.shutdownOutput();
5.文件上传自定义文件名称命名规则(了解-扩展)
package com.itheima.demo05TCPUpload;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
/*
文件上传的服务器端(重点)
作用:读取客户端上传的文件,把文件保存到服务器的硬盘上,给客户端回写"上传成功!"
文件的上传就是文件的复制:
数据源: 客户端上传的文件
目的地: 服务器的硬盘上 d:\\upload\\1.jpg
实现步骤:
1.判断d盘是否有upload文件夹,没有则创建
2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
10.释放资源(fos,socket,server)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.判断d盘是否有upload文件夹,没有则创建
File file = new File("d:\\upload");
if(!file.exists()){
file.mkdir();
}
//2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
ServerSocket server = new ServerSocket(9999);
//3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
System.out.println("--------------服务器已经启动,等待客户端上传文件---------------");
Socket socket = server.accept();
//4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
/*
自定义一个文件的名称:规则==>不重复
域名+毫秒值+随机数
*/
String fileName = "itheima"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";
//5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
FileOutputStream fos = new FileOutputStream("d:\\upload\\"+fileName);
//6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
byte[] bytes = new byte[1024];
int len = 0;
System.out.println("11111111111111111111111111111111111");
while ((len=is.read(bytes))!=-1){
//7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
fos.write(bytes,0,len);
}
System.out.println("2222222222222222222222222222222222222");
//8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
os.write("上传成功!".getBytes());
//10.释放资源(fos,socket,server)
fos.close();
socket.close();
server.close();
}
}
6.多线程版本服务器(了解-扩展)
package com.itheima.demo06ThreadUpload;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
/*
多线程版本的服务器:
1.定义一个死循环.轮询accpet方法,一直获取客户端Socket对象
2.有客户端请求服务器,就开启一个新的线程,完成文件上传
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
File file = new File("d:\\upload");
if(!file.exists()){
file.mkdir();
}
ServerSocket server = new ServerSocket(9999);
System.out.println("--------------服务器已经启动,等待客户端上传文件---------------");
//1.定义一个死循环.轮询accpet方法,一直获取客户端Socket对象
while (true){
Socket socket = server.accept();
/*
Socket对象中的方法
InetAddress getInetAddress() 获取上传文件客户端ip地址对象
*/
System.out.println("当前上传文件的客户端是:"+socket.getInetAddress().getHostAddress());
//2.有客户端请求服务器,就开启一个新的线程,完成文件上传
new Thread(()->{
try {
InputStream is = socket.getInputStream();
String fileName = "itheima"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";
FileOutputStream fos = new FileOutputStream("d:\\upload\\"+fileName);
byte[] bytes = new byte[1024];
int len = 0;
while ((len=is.read(bytes))!=-1){
fos.write(bytes,0,len);
}
OutputStream os = socket.getOutputStream();
os.write("上传成功!".getBytes());
fos.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
//server.close();
}
}
7.模拟BS服务器(了解)
模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果。
案例分析
-
准备页面数据,web文件夹。
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>我的主页</title> </head> <body> <h1>我的主页</h1> </body> </html>
- 我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问,查看网页效果
package com.itheima.demo06BSTCP;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
BS版本的TCP案例
B:浏览器(作为客户端)
S:服务器
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//创建服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//使用ServerSocket对象accept,监听并获取客户端Socket对象
Socket socket = server.accept();
//使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//使用字节输入流InputStream对象中的方法read,读取客户端的请求信息
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
//释放资源
socket.close();
server.close();
}
}
http://localhost:8080/day12/web/index.html
代码实现
BS服务器
package com.itheima.demo07BSTCP;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
BS版本的服务器
服务器要完成的事情:根据文件的相对路径,创建本地字节输入流读取index.html文件,把index.html写到客户端浏览器显示出来
1.把网络字节输入流InputStream转换为BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(is));
2.使用BufferedReader中的方法readLine,读取第一行文本
String s = "GET /day12/web/index.html HTTP/1.1";
3.使用String类的方法split,根据空格截取字符串,返回一个字符串数组
4.只要数组的arr[1] "/day12/web/index.html "
5.使用String类的方法subString,从1索引开始截取字符串 "day12/web/index.html"
6.创建本地字节输入流,根据文件的相对路径,读取文件
7.使用网络字节输出流OutputStream,把读取到的文件,发送到客户端显示
*/
public class TCPServer2 {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
Socket socket = server.accept();
//使用Socket对象获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//1.把网络字节输入流InputStream转换为BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//2.使用BufferedReader中的方法readLine,读取第一行文本
String s = br.readLine();//"GET /day12/web/index.html HTTP/1.1";
//3.使用String类的方法split,根据空格截取字符串,返回一个字符串数组
String[] arr = s.split(" ");
//4.只要数组的arr[1] "/day12/web/index.html "
//5.使用String类的方法subString,从1索引开始截取字符串 "day12/web/index.html"
String path = arr[1].substring(1);
//6.创建本地字节输入流,根据文件的相对路径,读取文件
FileInputStream fis = new FileInputStream(path);
//7.使用网络字节输出流OutputStream,把读取到的文件,发送到客户端显示
OutputStream os = socket.getOutputStream();
//增加以下三行代码的目的:告之客户端浏览器,写回的是html类型的文件,让客户端以网页的形式显示文件,而不是文本
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
//一读一写复制文件
byte[] bytes = new byte[1024];
int len = 0;
while ((len=fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
fis.close();
socket.close();
server.close();
}
}
c.index.html页面添加多个图片,多次请求服务器
package com.itheima.demo07BSTCP;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
BS版本的服务器
问题:
服务器给客户端写的页面中包含图片
服务器仅仅是把index.html页面写给客户端,并没有把图片一起写回
客户端:找不到图片
客户端显示index.html页面包含图片的路径<img src="img/logo2.png">
客户端会自动根据图片的路径,再一次请求服务器,让服务器根据路径读取图片,写回到客户端
*/
public class TCPServer3 {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//1.增加一个死循环,轮询accpet方法,一直监听客户端的请求
while (true){
//使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
Socket socket = server.accept();
//2.有客户端请求服务器,获取到客户端,开启一个新的线程,根据客户端发送的路径,读取文件,给服务器写回
new Thread(()->{
try {
//使用Socket对象获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//1.把网络字节输入流InputStream转换为BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//2.使用BufferedReader中的方法readLine,读取第一行文本
String s = br.readLine();//"GET /day12/web/index.html HTTP/1.1";
//3.使用String类的方法split,根据空格截取字符串,返回一个字符串数组
String[] arr = s.split(" ");
//4.只要数组的arr[1] "/day12/web/index.html "
//5.使用String类的方法subString,从1索引开始截取字符串 "day12/web/index.html"
String path = arr[1].substring(1);
System.out.println(path);
//6.创建本地字节输入流,根据文件的相对路径,读取文件
FileInputStream fis = new FileInputStream(path);
//7.使用网络字节输出流OutputStream,把读取到的文件,发送到客户端显示
OutputStream os = socket.getOutputStream();
//增加以下三行代码的目的:告之客户端浏览器,写回的是html类型的文件,让客户端以网页的形式显示文件,而不是文本
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
//一读一写复制文件
byte[] bytes = new byte[1024];
int len = 0;
while ((len=fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
fis.close();
socket.close();
} catch (IOException e) {
System.out.println("您发送的路径找不到!");
}
}).start();
}
}
}