- 了解http协议是什么
- 掌握http请求信息、响应信息格式
项目目标:
实现本地客户端与服务器一问一答的请求与响应,了解http协议即可.
项目步骤:
-
服务器端代码编写
-
先在当前src文件下新建一个包: webserver,再该包下创建一个类Server,书写代码如下:
package webserver; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 服务端的使用演示类: */ public class Server { ServerSocket serverSocket; Server() { try { serverSocket = new ServerSocket(8088);//创建服务器 并设置端口 } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Server s = new Server(); } }
-
运行测试以下状态:
-
弹出下列窗口直接点击允许访问即可.
-
控制台中如果报错如下:
-
运行成功:
-
-
再当前WebServer类中添加start方法,主要编写接收客户端请求代码
package webserver; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 服务端的使用演示类: */ public class Server { ServerSocket serverSocket; Server() { try { serverSocket = new ServerSocket(8088);//创建服务器 并设置端口 } catch (IOException e) { e.printStackTrace(); } } public void start() { try { System.out.println("等待客户端链接"); Socket socket = serverSocket.accept(); System.out.println("客户端已链接"); ClientHandle handle = new ClientHandle(socket);//将socket对象 传递给构 造方法中 handle.run();//执行解析请求和响应的方法 } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Server s = new Server(); s.start();//调用start方法 } }
-
运行测试:
-
当控制台中显示如下:
-
打开浏览器,在地址栏中输入localhost:8088 回车即可
-
打开控制台查看是否显示一个客户端已连接,显示则表示客户端已经连接服务器并给服务器发送请求
-
-
-
处理客户端发送的请求并响应
-
在当前项目包下新建一个类:ClientHandler
package webserverdemo; import java.net.Socket; /** * 处理客户端发送的请求 --处理请求并响应 */ public class ClientHandler { private Socket socket; public ClientHandler(Socket socket){ this.socket = socket; } /** 自定义一个run方法,用来处理和响应客户端发送来的请求 * */ public void run() { } }
-
在WebServer类中的start方法,实例化ClientHandler对象并将接收到的socket对象传递构造方法进行赋值.
-
那么目前我们已经清楚了客户端是如何连接服务端,以及也看到了连接后客户端发送的请求信息,现在我们来认识一下HTTP协议
-
HTTP协议(面试重点)
HTTP概念
-
HTTP协议 超文本传输协议 由万维网制定(w3c)
是浏览器与服务器通讯的应用层协议,规定了浏览器与服务器之间的交互规则以及交互数据的格式信息等。
-
TCP协议 工作在传输层,主要关注的是,客户端与服务器建立连接,互相传递数据的协议
HTTP交互规则
HTTP协议对于客户端与服务端之间的交互规则有以下定义:
-
要求浏览器与服务端之间必须遵循一问一答的规则,即:浏览器与服务端建立TCP连接后需要 先发送一个请求(问),然后服务端接收到请求并予以处理后再发送响应(答)。注意,服务端永远 不会主动给浏览器发送信息。
-
HTTP要求浏览器与服务端的传输层协议必须是可靠的传输,因此是使用TCP协议作为传输层 协议的。
HTTP请求和响应(必须记住)
-
HTTP协议对于浏览器与服务端之间交互的数据格式,内容也有一定的要求。
- 浏览器给服务端发送的内容称为请求Request
- 服务端给浏览器发送的内容称为响应Response
-
请求和响应中大部分内容都是文本信息(字符串),并且这些文本数据使用的字符集为: ISO8859-1.这是一个欧洲的字符集,里面是不支持中文的!!!。而实际上请求和响应出现 的字符也就是英文,数字,符号。
请求Request
请求是浏览器发送给服务端的内容,HTTP协议中一个请求由三部分构成: 分别是:请求行,消息头(请求头),消息正文(请求实体)。消息正文部分可以没有。
请求行
请求行是一行字符串,以连续的两个字符(回车符和换行符)作为结束这一行的标志。
- 回车符:在ASC编码中2进制内容对应的整数是13.回车符通常用CR表示。
- 换行符:在ASC编码中2进制内容对应的整数是10.换行符通常用LF表示。
- 回车符和换行符实际上都是不可见字符。
请求行分为三部分:
请求方式(SP)抽象路径(SP)协议版本(CRLF) 注:SP是空格
GET / HTTP/1.1(CRLF)
URL地址格式:
协议://主机地址信息/抽象路径 http://localhost:8088 GET / HTTP/1.1
消息头
消息头是浏览器给服务端发送的一些附加信息,有的用来说明浏览器自身内容,有的用来告知服务端交互细节,有的告知服务端消息正文详情等。
消息头由若干行字符串组成,每行结束也是以CRLF标志。 每个消息头的格式为:消息头的名字(:SP)消息的值(CRLF) 消息头部分结束是以单独的(CRLF)标志。 例如:
GET / HTTP/1.1(CRLF)
Host: localhost:8088(CRLF)
Connection: keep-alive(CRLF)
Upgrade-Insecure-Requests: 1(CRLF)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36(CRLF)
Sec-Fetch-User: ?1(CRLF)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9(CRLF)
Sec-Fetch-Site: none(CRLF)
Sec-Fetch-Mode: navigate(CRLF)
Accept-Encoding: gzip, deflate, br(CRLF)
Accept-Language: zh-CN,zh;q=0.9(CRLF)(CRLF)
消息正文
消息正文是2进制数据 , 通常是用户上传的信息,比如:在页面输入的注册信息,上传的 附件等内容。
GET / HTTP/1.1
Host: localhost:8088
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Sec-Fetch-User: ?1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
101010101011100101.....
当客户端发送请求时,将客户端的请求行和消息正文显示出来
-
在ClientHandler 类中的 run方法中定义读取客户端发送请求的内容,代码如下:
public void run() {//用于处理解析客户端发送请求 以及 响应给客户端的逻辑方法 //处理(解析)请求 try { InputStream input = socket.getInputStream();//获取输入流,准备接收请求 int d = 0;//用来存储每次实际读取到的内容(字节) char cur = 'a';//cur 代表当前读取到的内容 char per = 'a';//per 代表上次读取到的内容 //StringBuilder 用于频繁修改字符串时的一种工具,性能好. StringBuilder builder = new StringBuilder();//创建一个StringBuilder while ((d = input.read()) != -1) {//如果读取的内容不等于-1 就一直读 cur = (char) d;//将内容存给当前读取到的变量 cur //13表示回车 10表示换行 if (per == 13 && cur == 10) {//如果上次读的内容是回车,本次是换行,代表一行结束。 break;//退出 } builder.append(cur);//拼接当前读到的字符 per = cur;//将本次读取的内容 存给 per 进行下次读取,那么per则装的就是上一次读取的内容 } String line = builder.toString().trim();//将builder对象转换为String对象去除空白 System.out.println(line);//打印输出每一行读取到的内容 } catch (IOException e) { e.printStackTrace(); } }
如果想要全部读取,则需要循环读取,可以将读取到一行的逻辑封装成方法,便于使用:
package webserver;
import java.io.*;
import java.net.Socket;
/**
* 处理客户端的请求与响应类
*/
public class ClientHandle {
Socket socket;
ClientHandle(Socket socket) {
this.socket = socket;
}
public void run() {//用于处理解析客户端发送请求 以及 响应给客户端的逻辑方法
//处理(解析)请求
try {
while (true){
String line = readLine();
if(line.isEmpty()){//如果line 是空串 则表示读到最后一次的CRLF
break;//退出循环
}
System.out.println(line);//打印输出每一行读取到的内容
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String readLine() throws IOException {
InputStream input = socket.getInputStream();//获取输入流,准备接收请求
int d = 0;//用来存储每次实际读取到的内容(字节)
char cur = 'a';//cur 代表当前读取到的内容
char per = 'a';//per 代表上次读取到的内容
//StringBuilder 用于频繁修改字符串时的一种工具,性能好.
StringBuilder builder = new StringBuilder();//创建一个StringBuilder
while ((d = input.read()) != -1) {//如果读取的内容不等于-1 就一直读
cur = (char) d;//将内容存给当前读取到的变量 cur
//13表示回车 10表示换行
if (per == 13 && cur == 10) {//如果上次读的内容是回车,本次是换行,代表一行结束
break;//退出
}
builder.append(cur);//拼接当前读到的字符
per = cur;//将本次读取的内容 存给per进行下次读取,那么per则装的就是上一次读取的内容
}
String line = builder.toString().trim();//将builder转换为String对象去除空白
return line;
}
}
- 在run中调用如下图所示,再进行测试一次就能拿到完整的信息
运行测试:
-
当控制台中显示如下:
-
打开浏览器,在地址栏中输入localhost:8088 回车即可
-
打开控制台查看是否是显示客户端发送的请求,如图:
响应Response
响应是服务端发送给客户端的内容。一个响应包含三部分:状态行,响应头,响应正文
状态行
状态行是一行字符串(CRLF结尾),并且状态行由三部分组成,格式为:
protocol(SP)statusCode(SP)statusReason(CRLF)
协议版本(SP)状态代码(SP)状态描述(CRLF)
例如:
HTTP/1.1 200 OK
状态代码是一个3位数字,分为5类:
1xx:保留
2xx:成功,表示处理成功,并正常响应
3xx:重定向,表示处理成功,但是需要浏览器进一步请求
4xx:客户端错误,表示客户端请求错误导致服务端无法处理
5xx:服务端错误,表示服务端处理请求过程出现了错误
具体的数字在HTTP协议手册中有相关的定义,可参阅。
状态描述手册中根据不同的状态代码有参考值,也可以自行定义。通常使用参考值即可。
响应头
响应头与请求中的消息头格式一致,表示的是服务端发送给客户端的附加信息。
Content-Type: text/html(CRLF)
Content-Length: 2546(CRLF)(CRLF)
响应正文
2进制数据部分,包含的通常是客户端实际请求的资源内容。
响应的大致内容:
HTTP/1.1 200 OK(CRLF)
Content-Type: text/html(CRLF)
Content-Length: 2546(CRLF)(CRLF)
1011101010101010101......
这里的两个响应头: Content-Type是用来告知浏览器响应正文中的内容是什么类型的数据(图片,页面等等) 不同的类型对应的值是不同的,比如:
文件类型 Content-Type对应的值
html text/html
css text/css
js application/javascript
png image/png
gif image/gif
jpg image/jpeg
Content-Length是用来告知浏览器响应正文的长度,单位是字节。
浏览器接收正文前会根据上述两个响应头来得知长度和类型从而读取出来做对应的处理以 显示给用户看.
上述代码完成后,我们接收了客户端的请求,现在可以任意响应给客户端一个html界面
-
资源准备:选中项目中项目根路径文件夹右键—new—HTML File 单机, 如图所示
-
选中后文件名字:index 回车即可
-
双击创建好的index.html文件,内容添加如下即可:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>响应成功!</h1>
</body>
</html>
-
找到ClientHandle类,在类中run方法写关于响应客户端的代码如下:
public void run() {//用于处理解析客户端发送请求 以及 响应给客户端的逻辑方法 //处理(解析)请求 try { File file = new File("./index.html"); //创建一个文件对象,该对象表示是根路径下的网页 OutputStream output = socket.getOutputStream(); //响应 String data = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n" + "Content-Length: "+file.length+"\r\n"+"\r\n";//按照规则书写响应的信息内容。 output.write(data.getBytes());//将响应的信息写出 FileInputStream input = new FileInputStream(file); int d = 0; while ( (d =input.read()) != -1){ output.write(d);//循环将读取到的文件内容写出 响应给客户端 } } catch (IOException e) { e.printStackTrace(); } }
-
运行测试:
- 运行webServer类
- 打开浏览器输入localhost:8088 回车
- 界面呈现如下则表示客户端接收响应成
客户端与服务器的请求响应 一问一答实现完成
总结
-
了解http协议是什么?
答:HTTP协议 超文本传输协议 由万维网制定(w3c)
是浏览器与服务器通讯的应用层协议,规定了浏览器与服务器之间的交互规则以及交互数据的格式信息.
-
了解socket是什么,如何使用?
答:在计算机领域中,Socket也被称为套接字编程,它是计算机之间进行通信的一种约定或者说是一种方式。可 以通过socket来通过互联网收发信息内容。
服务器端ServerSocket
在服务器端选择一个端口号,然后在指定的端口号上等待客户端发起的连接
构造方法:
ServerSocket(int port) 创建一个绑定特定端口号的服务器套接字 accept() 侦听并接受到发送到此套接字的连接 close() 关闭此套接字
客户端Socket
构造方法:
Socket(String host,int port) 创建一个套接字,并且连接到host,并且绑定端口号 close() 关闭此套接字 getInputStream() 返回此套接字的输入流 getOutputStram() 返回此套接字的输出流
-
掌握http请求信息、响应信息格式?
请求Request
请求是浏览器发送给服务端的内容,HTTP协议中一个请求由三部分构成: 分别是:请求行,消息头(请求头),消息正文(请求实体)。消息正文部分可以没有。
响应Response
响应是服务端发送给客户端的内容。一个响应包含三部分:状态行,响应头,响应正文
上篇文章:入门Java编程的知识点—>socket的使用(day19)-CSDN博客https://blog.csdn.net/Z0412_J0103/article/details/141590311下篇文章: MySQL的安装—>Mariadb的安装(day21)-CSDN博客https://blog.csdn.net/Z0412_J0103/article/details/141678370