第一章 网络编程入门
1.1 软件结构
- 两种架构各有优势,但是无论哪种架构,都离不开网络的支持。“网络编程”,就是在一定的协议下,实现两台计算机的通信的程序
1.2 网络编程三要素_网络通信协议(规则)
- 协议:计算机网络通信必须遵守的规则。
1.3 网络通信协议分类
- 消耗资源小,通信效率高
- 可能会出现数据的丢失
- 三次握手
- 第一次:客户端向服务器端发出连接请求
- 第二次:服务器端向客户端回送一个响应
- 第三次:客户端再次向服务器端发送确认信息,确认连接。
1.4 网络编程三要素_IP地址
- IP地址:指互联网协议地址(Internet protecol Address),俗称IP。
- IP地址用来给一个网络中的计算机设备做唯一的编号。
2. 注意事项:
1. 常用命令:
* 查看本机IP地址:ipconfig
* 检查网络是否连通:ping 222.198.34.77
2. 特殊的IP地址
* 本机IP地址:127.0.0.1,localhost
3. IP就是电脑的唯一标识
1.5 网络编程三要素_端口号
- 注意事项:
- 1024之前的端口号我们不能使用,已经被系统分配给已知的网络软件了
- 网络软件的端口号不能重复。
- 常用的端口号
- 80端口:网络端口
- 数据库
- MySQL:3306
- oracle:1521
- Tomcat服务器:8080
第二章 TCP通信程序
2.1 TCP通信的概述(上)
- 注意事项:
- TCP通信:面向连接的通信,客户端和服务器端必须经过3次握手,建立逻辑连接,才能通信(安全)
- 客户端和服务器端建立逻辑连接包含一个IO对象
- 通信数据不止是字符,所以是字节流对象【重点】
- 客户端和服务器端进行一个数据交互,需要4个IO流对象
2.2 TCP通信的概述(下)
- 服务器端明确的两件事:
- 多个客户端同时和服务器器端交互,服务器端必须明确和那个客户端进行的交互
- 服务器端有个方法,accept【!】:服务器端获取请求的客户端对象
Socket s1 = server.accept();//指定一台客户端
Socket s2 = server.accept();//指定另一台客户端
- 多个客户端同时和服务器端交互,就需要使用多个IO流对象
- 服务器端使用客户端的流和客户端交互【重点】
2.3 TCP通信的客户端代码实现
- TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据
- 表示客户端的类:
- java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
- 套接字:包含了IP地址和端口号的网络单位(相当于电脑)
- 构造方法:
- Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。
- 参数:
- String host:服务器主机的名称/服务器的IP地址
- int port:服务器的端口号
- 成员方法:
- OutputStream getOutputStream() 返回此套接字的输出流。
- InputStream getInputStream() 返回此套接字的输入流。
- void close() 关闭此套接字。
- 实现步骤【重点】:
- 【新】创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
- 可以随机指定端口号,但是想访问某个服务器需要和该服务器同步
- 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
- 使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
- 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
- 使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
- 【新】释放资源(Socket)
- 注意:
- 客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
- 当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路
- 这时如果服务器没有启动,那么就会抛出异常ConnectException: Connection refused: connect
- 如果服务器已经启动,那么就可以进行交互了
public static void main(String[] args) throws IOException {
//1. 创建一个客户端==对象Socket==,==构造方法绑定服务器的IP地址和端口号==
Socket socket = new Socket("127.0.0.1", 8888);
//2. 使用==Socket对象中的方法getOutputStream==()获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//3. 使用==网络字节输出流OutputStream对象中的方法write==,给服务器发送数据
os.write("你好服务器".getBytes());
//还没有建立服务器——这里只创建了客户端
//4. 使用==Socket对象中的方法getInputStream==()获取网络字节输入流InputStream对象
//5. 使用==网络字节输入流InputStream对象中的方法read==,读取服务器回写的数据
//6. 释放资源(Socket)
socket.close();
}
2.4 TCP通信的服务器端代码实现
- TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
- 表示服务器的类:
- java.net.ServerSocket:此类实现服务器套接字。
- 构造方法:
- ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
- 服务器端必须明确一件事情,必须的知道是哪个客户端请求的服务器
- 所以可以使用accept方法获取到请求的客户端对象Socket
- 成员方法:
- Socket accept() 侦听并接受到此套接字的连接。
- 服务器的实现步骤:
- 【新】创建服务器ServerSocket对象和系统要指定的端口号
- 和系统要指定的端口号
- 【新】使用ServerSocket对象中的++方法accept++,获取到请求的客户端对象Socket
- 返回的就是一个socket
- 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
- 使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
- 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
- 使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
- 回写数据
- 【新】释放资源(Socket,ServerSocket)
- 注意:
- 先启动服务器端
- 服务器端代码启动后一直运行
//定义一个服务器端:TCPServer.java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
//1. 创建服务器ServerSocket对象和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
//2. 使用ServerSocket对象中的++方法accept++,获取到请求的客户端对象Socket
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==,给客户端回写数据
os.write("收到谢谢".getBytes());
//7. 释放资源(==Socket,ServerSocket==)
socket.close();
server.close();
}
}
//定义一个客户端:TCPClient.java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
//1. 创建一个客户端==对象Socket==,==构造方法绑定服务器的IP地址和端口号==
Socket socket = new Socket("127.0.0.1", 8888);
//2. 使用==Socket对象中的方法getOutputStream==()获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//3. 使用==网络字节输出流OutputStream对象中的方法write==,给服务器发送数据
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.1文件上传案例
3.2 综合案例_文件上传案例的客户端
- 文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器回写的数据
- 明确:
- 数据源:F:\picture\1.jpg
- 目的地:服务器
- 实现步骤:
- 创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
- 创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
- IP–>字符
- 端口号–>整型
- 使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
- 使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
- 使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
- 使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
- 使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
- 释放资源(FileInputStream,Socket)
public class TCPClient {
public static void main(String[] args) throws IOException {
//1. 创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("F:\\picture\\1.jpg");
//2. 创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1", 8888);
//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);
}
//6. 使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//7. 使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
while ((len = is.read(bytes)) != -1){
System.out.println(new String(bytes, 0, len));
}
//8. 释放资源(FileInputStream,Socket)
fis.close();
socket.close();
}
}
3.3 综合案例_文件上传案例的服务器端
- 文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"
- 明确:
- 数据源:客户端上传的文件
- 目的地:服务器的硬盘 E:\picture\1.jpg
- 实现步骤:
- 创建一个服务器ServerSocket对象,和系统要指定的端口号
- 端口号–>整数
- 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
- 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
- 判断E:\picture文件夹是否存在,不存在则创建
File file = new File("E:\\picture");
if(!file.exists()){ //是否存在
file.mkdirs(); //创建一个文件
}
- 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\1.jpg");
- 注:开始位置加文件分隔符
- 使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
- 使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
- 使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
- 使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
- 释放资源(FileOutputStream,Socket,ServerSocket)
public class TCPServer {
public static void main(String[] args) throws IOException {
//1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
//2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
Socket socket = server.accept();
//3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4. 判断E:\\picture文件夹是否存在,不存在则创建
File file = new File("E:\\picture");
if(!file.exists()){ //是否存在
file.mkdirs(); //创建一个文件
}
//5. 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\1.jpg");
//6. 使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes)) != -1){
//7. 使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
fos.write(bytes, 0, len);
}
/*//8. 使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//9. 使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
os.write("上传成功".getBytes());*/
//8,9合成一个
socket.getOutputStream().write("上传成功".getBytes());
//10. 释放资源(FileOutputStream,Socket,ServerSocket)
fos.close();
socket.close();
server.close();
}
}
- 注:++问题:程序运行成功,但是执行完不能停止问题++
3.4 综合案例_文件上传案例阻塞问题
- 上述问题不能停止–>阻塞
- 客户端:本地字节输入流中
- fis.read(bytes):读取本地文件,结束标记是读取到-1结束
- while循环不会读取到-1
- 那么也不会把结束标记写给服务器
- 服务器端:网络字节输入流中
- is.read():读取客户端上传的文件,永远也读取不到文件的结束标记
- read方法进入到阻塞状态,一直死循环等待结束标记
- 8,9,10代码就不会执行,也不会给客户端回写上传成功
- 客户端:网络字节输入流中
- is.read():读取不到服务器端回写的数据,进入到阻塞状态
- 解决方法【重点】:–解决因无法上传结束标记而导致无法停止的阻塞状态
- 上传完文件,给服务器写一个结束标记
- void shutdownOutput():禁用此套接字的输出流。(java.net包中,Socket类的方法)
- 对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
- 写在–>客户端:本地字节输入流–>后面
//改3.2客户端代码:在本地字节输入流后面
//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);
}
socket.shutdownOutput();
//3.2完整代码
public static void main(String[] args) throws IOException {
//1. 创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("F:\\picture\\1.jpg");
//2. 创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1", 8888);
//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);
}
/*5. ==解决方法【重点】==:--解决因无法上传结束标记而导致无法停止的阻塞状态
1. 上传完文件,给服务器写一个结束标记
2. void ==shutdownOutput==():禁用此套接字的输出流。(java.net包中,socket类的方法)
* 对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
3. 写在-->客户端:本地字节输入流-->后面
*/
socket.shutdownOutput();
//6. 使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//7. 使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
while ((len = is.read(bytes)) != -1){
System.out.println(new String(bytes, 0, len));
}
//8. 释放资源(FileInputStream,Socket)
fis.close();
socket.close();
}
3.5 综合案例_文件上传案例优化(文件命名&循环接收&多线程提高效率)
- 解决问题一:文件命名
- 每次运行一次,同名文件就被覆盖
- 自定义一个文件的命名规则:防止同名的文件被覆盖
- 规则:域名+毫秒值+随机数
/*
自定义一个文件的命名规则:防止同名的文件被覆盖
规则:域名+毫秒值+随机数
*/
String fileName = "oop" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg";
//5. 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\" + fileName);
- 解决问题二:服务器用一次就关闭了
- 让服务器一直处于监听状态(死循环accept方法)
- 有一个客户端上传文件,就保存一个文件
- 所以服务器就不用关闭了
- 不用写:server.close();
public static void main(String[] args) throws IOException {
//1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
/*
让服务器一直处于监听状态(死循环accept方法)
* 有一个客户端上传文件,就保存一个文件
*/
while (true){
//2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
Socket socket = server.accept();
//3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4. 判断E:\\picture文件夹是否存在,不存在则创建
File file = new File("E:\\picture");
if(!file.exists()){ //是否存在
file.mkdirs(); //创建一个文件
}
/*
自定义一个文件的命名规则:防止同名的文件被覆盖
规则:域名+毫秒值+随机数
*/
String fileName = "oop" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg";
//5. 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\" + fileName);
//6. 使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes)) != -1){
//7. 使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
fos.write(bytes, 0, len);
}
/*//8. 使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//9. 使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
os.write("上传成功".getBytes());*/
//8,9合成一个
socket.getOutputStream().write("上传成功".getBytes());
//10. 释放资源(FileOutputStream,Socket,ServerSocket)
fos.close();
socket.close();
}
//服务器就不用关闭
//server.close();
}
- 解决问题三:使用多线程来提高效率
- 使用多线程技术,提高程序的效率
- 有一个客户端上传文件,就开启一个线程,完成文件的上传while
- 注:实现Runnable接口,重写run方法时候【重点】
- 接口中的run方法没有声明抛出异常
- 所以子类重写方法是也不能声明抛出异常
- 需要用到try…catch
- catch中写:IOException
- 还可以继续优化,将两个关闭写到finally中(自愿)
public static void main(String[] args) throws IOException {
//1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
/*
让服务器一直处于监听状态(死循环accept方法)
* 有一个客户端上传文件,就保存一个文件
*/
while (true){
//2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
Socket socket = server.accept();
/*
使用多线程技术,提高程序的效率
有一个客户端上传文件,就开启一个线程,完成文件的上传
*/
new Thread(new Runnable() {
//完成文件的上传
@Override
public void run() {
try {
//3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4. 判断E:\\picture文件夹是否存在,不存在则创建
File file = new File("E:\\picture");
if(!file.exists()){ //是否存在
file.mkdirs(); //创建一个文件
}
/*
自定义一个文件的命名规则:防止同名的文件被覆盖
规则:域名+毫秒值+随机数
*/
String fileName = "oop" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg";
//5. 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\" + fileName);
//6. 使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes)) != -1){
//7. 使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
fos.write(bytes, 0, len);
}
/*//8. 使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//9. 使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
os.write("上传成功".getBytes());*/
//8,9合成一个
socket.getOutputStream().write("上传成功".getBytes());
//10. 释放资源(FileOutputStream,Socket,ServerSocket)
fos.close();
socket.close();
}catch (IOException e){
System.out.println(e);
}
}
}).start();
}
//服务器就不用关闭
//server.close();
}
3.6 模拟BS服务器分析
//定义了一个服务器(默认状态)
public static void main(String[] args) throws IOException {
//1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象(浏览器)
Socket socket = server.accept();
//3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4. 使用网络字节输入流InputStream对象中的方法read,读取客户端的请求信息
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes)) != -1){
System.out.println(new String(bytes, 0, len));
}
}
//网页访问http://127.0.0.1:8080/day11/web/index.html是回显
GET /day11/web/index.html HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3754.400 QQBrowser/10.5.3991.400
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
3.7 模拟BS服务器代码实现
- 3.6中网络字节输入流注释掉(注释部分用来读取客户端的请求信息,如:路径),改为如下(2,3,4,5,6)步骤:
原来的:
//1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象(浏览器)
Socket socket = server.accept();
//3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
- 获取回写内容的第一行中的路径,步骤:
- 把is网络字节输入流对象(InputStream),转换为字符缓冲输入流(BufferedReader)
- InputStreamReader是字节流通向字符流的桥梁
- 把客户端请求信息的第一行读取出来 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html HTTP/1.1
- 用到BufferedReader特有的方法,readLine–>读取一行,返回类型字符串
- 把读取的信息进行切割,只要中间部分 /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html
- 用String中的split方法,用空格来切割–>返回的是字符串数组
- 把路径前面的/去掉,进行截取 Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html
- 用String中的substring方法,从1开始截取保留后面的
//获取回写内容的第一行中的路径步骤:
//1. 把is网络字节输入流对象(InputStream),转换为字符缓冲输入流(BufferedReader)
//InputStreamReader是字节流通向字符流的桥梁
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//2. 把客户端请求信息的第一行读取出来 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html HTTP/1.1
//用到BufferedReader特有的方法,readLine-->读取一行,返回类型字符串
String line = br.readLine();
//3. 把读取的信息进行切割,只要中间部分 /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html
//用String中的split方法,用空格来切割-->返回的是字符串数组
String[] arr = line.split(" ");
//4. 把路径前面的/去掉,进行截取 Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html
//用String中的substring方法,从1开始截取保留后面的
String htmlpath = arr[1].substring(1); //这里出来的就是html的路径了
- 根据路径读取本地文件,步骤:
- 创建一个本地字节输入流,构造方法中绑定要读取的html路径
- 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
// 根据路径读取本地文件,步骤:
//1. 创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//2. 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
- 写入HTTP协议响应头,固定写法(这里加上就行,后面会讲):
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
- 一读一写复制文件,把服务器端读取的html文件回写到客户端(页面上)
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();
//定义了一个服务器:TCPServer.java
public static void main(String[] args) throws IOException {
//1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象(浏览器)
Socket socket = server.accept();
//3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4. 使用网络字节输入流InputStream对象中的方法read,读取客户端的请求信息
/*byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes)) != -1){
System.out.println(new String(bytes, 0, len));
}*/
//获取回写内容的第一行中的路径步骤:
//1. 把is网络字节输入流对象(InputStream),转换为字符缓冲输入流(BufferedReader)
//InputStreamReader是字节流通向字符流的桥梁
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//2. 把客户端请求信息的第一行读取出来 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html HTTP/1.1
//用到BufferedReader特有的方法,readLine-->读取一行,返回类型字符串
String line = br.readLine();
//3. 把读取的信息进行切割,只要中间部分 /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html
//用String中的split方法,用空格来切割-->返回的是字符串数组
String[] arr = line.split(" ");
//4. 把路径前面的/去掉,进行截取 Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html
//用String中的substring方法,从1开始截取保留后面的
String htmlpath = arr[1].substring(1); //这里出来的就是html的路径了
// 根据路径读取本地文件,步骤:
//1. 创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//2. 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
// 写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
//一读一写复制文件,把服务器端读取的html文件回写到客户端(页面上)
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();
}
- 结果:
3.8 3.7中服务器执行后,页面图片不显示问题
- 原因:
- 浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片
- 我们就的让服务器一直处于监听状态,客户端请求一次,服务器就回写一次
- 同3.5问题三
1. 使用多线程技术,提高程序的效率
* 有一个客户端上传文件,就开启一个线程,完成文件的上传while
2. 注:实现Runnable接口,重写run方法时候【重点】
* 接口中的run方法没有声明抛出异常
* 所以子类重写方法是也不能声明抛出异常
* 需要用到try...catch
* catch中写:IOException
//定义一个服务器:TCPServerThread.java
public static void main(String[] args) throws IOException {
//1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
/*
浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片
我们就的让服务器一直处于监听状态
*/
while (true){
//2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象(浏览器)
Socket socket = server.accept();
/*
使用多线程技术,提高程序的效率
客户端请求一次,服务器就回写一次
*/
new Thread(new Runnable() {
@Override
public void run() {
try {
//3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//获取回写内容的第一行中的路径步骤:
//1. 把is网络字节输入流对象(InputStream),转换为字符缓冲输入流(BufferedReader)
//InputStreamReader是字节流通向字符流的桥梁
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//2. 把客户端请求信息的第一行读取出来
//用到BufferedReader特有的方法,readLine-->读取一行,返回类型字符串
String line = br.readLine();
//System.out.println(line); //有一张图片就请求一次(演示)
//3. 把读取的信息进行切割,只要中间部分
//用String中的split方法,用空格来切割-->返回的是字符串数组
String[] arr = line.split(" ");
//4. 把路径前面的/去掉,进行截取
//用String中的substring方法,从1开始截取保留后面的
String htmlpath = arr[1].substring(1); //这里出来的就是html的路径了
// 根据路径读取本地文件,步骤:
//1. 创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//2. 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
// 写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
//一读一写复制文件,把服务器端读取的html文件回写到客户端(页面上)
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(e);
}
}
}).start();
}
//服务器就不用关闭
//server.close();
}
- 结果:
2. 客户端请求信息演示(有一个图片请求一次)
* 在readLine读取一行的方法后面输出line
结果: GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/logo2.png HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/header.jpg HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/1.jpg HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/title2.jpg HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/small03.jpg HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/middle01.jpg HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/big01.jpg HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/ad.jpg HTTP/1.1 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/img/footer.jpg HTTP/1.1
* 除了第一次是请求客户端信息外,后面每有一个图片就请求一次