首页 > 编程语言 >02_11_Java语音进阶||day11_网络编程【TCP|UDP】

02_11_Java语音进阶||day11_网络编程【TCP|UDP】

时间:2023-02-26 19:02:34浏览次数:42  
标签:02 11 UDP Socket 对象 new 服务器 字节 客户端


第一章 网络编程入门

1.1 软件结构

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_java


02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络协议_02

  • 两种架构各有优势,但是无论哪种架构,都离不开网络的支持“网络编程”就是在一定的协议下,实现两台计算机的通信的程序

1.2 网络编程三要素_网络通信协议(规则)

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络_03


02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络_04

  • 协议:计算机网络通信必须遵守的规则。

1.3 网络通信协议分类

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_java_05

  • 消耗资源小,通信效率高
  • 可能会出现数据的丢失

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络协议_06


02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络协议_07

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

1.4 网络编程三要素_IP地址

  1. IP地址:指互联网协议地址(Internet protecol Address),俗称IP。
  • IP地址用来给一个网络中的计算机设备做唯一的编号。

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_java_08


2. 注意事项:

1. 常用命令:

* 查看本机IP地址:ipconfig

* 检查网络是否连通:ping 222.198.34.77

2. 特殊的IP地址

* 本机IP地址:127.0.0.1,localhost

3. IP就是电脑的唯一标识

1.5 网络编程三要素_端口号

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_java_09


02_11_Java语音进阶||day11_网络编程【TCP|UDP】_服务器_10

  1. 注意事项:
  1. 1024之前的端口号我们不能使用,已经被系统分配给已知的网络软件了
  2. 网络软件的端口号不能重复。
  1. 常用的端口号
  1. 80端口:网络端口
  2. 数据库
  • MySQL:3306
  • oracle:1521
  1. Tomcat服务器:8080

第二章 TCP通信程序

2.1 TCP通信的概述(上)

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_java_11


02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络_12

  1. 注意事项:
  1. TCP通信:面向连接的通信,客户端和服务器端必须经过3次握手,建立逻辑连接,才能通信(安全)
  2. 客户端和服务器端建立逻辑连接包含一个IO对象
  3. 通信数据不止是字符,所以是字节流对象【重点】
  4. 客户端和服务器端进行一个数据交互,需要4个IO流对象

2.2 TCP通信的概述(下)

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络协议_13

  1. 服务器端明确的两件事:
  1. 多个客户端同时和服务器器端交互,服务器端必须明确和那个客户端进行的交互
  • 服务器端有个方法,accept【!】:服务器端获取请求的客户端对象
Socket s1 = server.accept();//指定一台客户端
Socket s2 = server.accept();//指定另一台客户端
  1. 多个客户端同时和服务器端交互,就需要使用多个IO流对象
  • 服务器端使用客户端的流和客户端交互【重点】

2.3 TCP通信的客户端代码实现

  1. TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据
  • 表示客户端的类:
  • java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
  • 套接字:包含了IP地址和端口号的网络单位(相当于电脑)
  1. 构造方法:
  • Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。
  1. 参数:
  • String host:服务器主机的名称/服务器的IP地址
  • int port:服务器的端口号
  1. 成员方法:
  • OutputStream getOutputStream() 返回此套接字的输出流。
  • InputStream getInputStream() 返回此套接字的输入流。
  • void close() 关闭此套接字。
  1. 实现步骤【重点】:
  1. 【新】创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
  • 可以随机指定端口号,但是想访问某个服务器需要和该服务器同步
  1. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
  2. 使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
  3. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
  4. 使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
  5. 【新】释放资源(Socket)
  1. 注意:
  1. 客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
  2. 当我们创建客户端对象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通信的服务器端代码实现

  1. TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
  • 表示服务器的类:
  • java.net.ServerSocket:此类实现服务器套接字。
  1. 构造方法:
  • ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
  1. 服务器端必须明确一件事情,必须的知道是哪个客户端请求的服务器
  • 所以可以使用accept方法获取到请求的客户端对象Socket
  • 成员方法:
  • Socket accept() 侦听并接受到此套接字的连接。
  1. 服务器的实现步骤:
  1. 【新】创建服务器ServerSocket对象和系统要指定的端口号
  • 和系统要指定的端口号
  1. 【新】使用ServerSocket对象中的++方法accept++,获取到请求的客户端对象Socket
  • 返回的就是一个socket
  1. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
  2. 使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
  3. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
  4. 使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
  • 回写数据
  1. 【新】释放资源(Socket,ServerSocket)
  1. 注意:
  1. 先启动服务器端
  2. 服务器端代码启动后一直运行
//定义一个服务器端: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文件上传案例

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_客户端_14


02_11_Java语音进阶||day11_网络编程【TCP|UDP】_服务器_15

3.2 综合案例_文件上传案例的客户端

  1. 文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器回写的数据
  • 明确:
  • 数据源:F:\picture\1.jpg
  • 目的地:服务器
  1. 实现步骤:
  1. 创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
  2. 创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
  • IP–>字符
  • 端口号–>整型
  1. 使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
  2. 使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
  3. 使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
  4. 使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
  5. 使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
  6. 释放资源(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 综合案例_文件上传案例的服务器端

  1. 文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"
  • 明确:
  • 数据源:客户端上传的文件
  • 目的地:服务器的硬盘 E:\picture\1.jpg
  1. 实现步骤:
  1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
  • 端口号–>整数
  1. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
  2. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
  3. 判断E:\picture文件夹是否存在,不存在则创建
File file = new File("E:\\picture");
if(!file.exists()){ //是否存在
file.mkdirs(); //创建一个文件
}
  1. 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\1.jpg");
  • 注:开始位置加文件分隔符
  1. 使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
  2. 使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
  3. 使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
  4. 使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
  5. 释放资源(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 综合案例_文件上传案例阻塞问题

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络协议_16

  1. 上述问题不能停止–>阻塞
  2. 客户端:本地字节输入流中
  1. fis.read(bytes):读取本地文件,结束标记是读取到-1结束
  2. while循环不会读取到-1
  3. 那么也不会把结束标记写给服务器
  1. 服务器端:网络字节输入流中
  1. is.read():读取客户端上传的文件,永远也读取不到文件的结束标记
  2. read方法进入到阻塞状态,一直死循环等待结束标记
  3. 8,9,10代码就不会执行,也不会给客户端回写上传成功
  1. 客户端:网络字节输入流中
  1. is.read():读取不到服务器端回写的数据,进入到阻塞状态
  1. 解决方法【重点】:–解决因无法上传结束标记而导致无法停止的阻塞状态
  1. 上传完文件,给服务器写一个结束标记
  2. void shutdownOutput():禁用此套接字的输出流。(java.net包中,Socket类的方法)
  • 对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
  1. 写在–>客户端:本地字节输入流–>后面
//改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 综合案例_文件上传案例优化(文件命名&循环接收&多线程提高效率)

  1. 解决问题一:文件命名
  • 每次运行一次,同名文件就被覆盖
  1. 自定义一个文件的命名规则:防止同名的文件被覆盖
  • 规则:域名+毫秒值+随机数
/*
自定义一个文件的命名规则:防止同名的文件被覆盖
规则:域名+毫秒值+随机数
*/
String fileName = "oop" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg";
//5. 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\" + fileName);
  1. 解决问题二:服务器用一次就关闭了
  1. 让服务器一直处于监听状态(死循环accept方法)
  • 有一个客户端上传文件,就保存一个文件
  1. 所以服务器就不用关闭了
  • 不用写: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();
}
  1. 解决问题三:使用多线程来提高效率
  1. 使用多线程技术,提高程序的效率
  • 有一个客户端上传文件,就开启一个线程,完成文件的上传while
  1. 注:实现Runnable接口,重写run方法时候【重点】
  • 接口中的run方法没有声明抛出异常
  • 所以子类重写方法是也不能声明抛出异常
  • 需要用到try…catch
  • catch中写:IOException
  1. 还可以继续优化,将两个关闭写到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服务器分析

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_网络_17

//定义了一个服务器(默认状态)
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服务器代码实现

  1. 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();
  1. 获取回写内容的第一行中的路径,步骤:
  1. 把is网络字节输入流对象(InputStream),转换为字符缓冲输入流(BufferedReader)
  • InputStreamReader是字节流通向字符流的桥梁
  1. 把客户端请求信息的第一行读取出来 GET /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html HTTP/1.1
  • 用到BufferedReader特有的方法,readLine–>读取一行,返回类型字符串
  1. 把读取的信息进行切割,只要中间部分 /Dark-horse-teaching/src/cn/javaadvance/day11/web/index.html
  • 用String中的split方法,用空格来切割–>返回的是字符串数组
  1. 把路径前面的/去掉,进行截取 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的路径了
  1. 根据路径读取本地文件,步骤:
  1. 创建一个本地字节输入流,构造方法中绑定要读取的html路径
  2. 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
// 根据路径读取本地文件,步骤:
//1. 创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//2. 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
  1. 写入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());
  1. 一读一写复制文件,把服务器端读取的html文件回写到客户端(页面上)
byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes)) != -1){
os.write(bytes, 0, len);
}
  1. 释放资源
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();
}
  • 结果:

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_服务器_18

3.8 3.7中服务器执行后,页面图片不显示问题

  1. 原因:
  1. 浏览器解析服务器回写的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();
}
  • 结果:

02_11_Java语音进阶||day11_网络编程【TCP|UDP】_服务器_19


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 ​​ * 除了第一次是请求客户端信息外,后面每有一个图片就请求一次


标签:02,11,UDP,Socket,对象,new,服务器,字节,客户端
From: https://blog.51cto.com/u_15980166/6086813

相关文章