HTTP请求方式和报文解析
程序员文章站
2022-06-16 15:47:32
...
一、概述
最近想尝试写一个关于网络请求的系列文章,将网络请求的基础、使用及网络框架的学习分析总结以下,大致准备从以下几个方面分析:
- 网络请求的基础
- HTTP请求方式和报文解析
- Cookie 和 Session的理解与使用
- HTTP Cache缓存机制
- 封装网络请求
- TCP 和 Socket
二、HTTP请求方式
HTTP提供了七种请求方式:GET、POST、DELETE、PUT、HEAD、TRACE、OPTIONS,其中PUT,DELETE、POST、GET分别对应了资源的增、删、改、查,也是使用最多的请求方式;
- GET
- GET请求报文 和 服务器的响应报文
上述请求的资源是www.devtf.cn/articles/123.html文件,GET请求是将请求参数放在URL之后,第一个参数之前使用“?“,之后的参数格式为:参数名=参数值,参数名之间使用”&“连接,如:www.devtf.cn/articles/123.html?username=myname&userid=123
- POST
POST请求通常是使用来提交HTML的表单,表单中的数据传输到服务器,由服务器对这些数据处理,请求和响应的报文如下:
- PUT
与GET从服务器获取数据相反的是,PUT是想服务器写入资源,比如像CSDN这样允许用户创建Web页面,并用PUT直接传输到服务器上,返回服务器上的资源地址;
- DELETE
使用方法和GET一样,请求删除URL指定的资源文件
三、HTTP请求报文
HTTP的请求报文由请求行(Request line)、请求头部(Header)空行和请求数据;
- 请求行:请求报文的第一行,用来说明以什么方式请求、请求的地址和HTTP版本
- 头部字段:每个头部字段都包含一个名字和值,二者之间采用“:”连接,如:Connection:Keep-Alive
- 请求数据:请求的主体根据不同的请求方式请求主体不同
- GET、DELETE
这两种请求报文比较简单,查看上面即可
- POST、PUT
POST和PUT的请求行和请求头部,在上述已经列出,现在主要介绍请求报文中的参数:
- 一个参数的开始是由“--”加上boundary开始的
- 然后加上参数的Header信息,格式为字段名和字段值,二者之间使用“:”连接,如:Content-Type:text/plain
- 加上一个空行
- 发送的参数值
- 请求的数据以“--”+boundary+“--”结束真个请求的报文结束符
四、HTTP响应报文
HTTP的响应报文为3个部分组成:状态行、消息报文、响应正文
- 状态行:由HTTP版本、响应状态码、响应状态描述;如:HTTP/1.1 200 OK
- 响应报文头部:使用关键字和值表示,二者使用“:”隔开;如:Content-Type:text/html
- 响应内容:请求空行之后就是请求内容
- 常见的状态码和描述
- 200 OK : 客户端请求成功
- 400 Bad Request:客户端请求语法错误,服务器无法解析
- 401 Unauthorized:请求未经授权
- 403 Forbidden:服务器收到请求拒绝服务
- 404 Not Found:请求资源不存在,常见URL错误
- 500 Internal Server Error:服务端不可预期错误
- 503 Server Unavailable:服务器当前不能处理客户端请求
五、简单模拟HTTP服务器
- 创建HttpServer:内部创建ServerSocket监听客户端的输出信息
public const val HTTP_PORT = 8080 // 监听的端口
class HttpServiceSocket : Thread() {
val mServiceSocket by lazy {
ServerSocket(HTTP_PORT) // 创建ServerSocket
}
override fun run() {
while (true){
Log.d("HttpServiceSocket","Service 等待输入...")
DeliverThread(mServiceSocket.accept()).start() //开启线程接收信息
}
}
- 为了不阻塞线程,再开辟新线程从输入流中读取数据
class DeliverThread(val socket: Socket) : Thread() {
val bufferedReader by lazy {
BufferedReader(InputStreamReader(socket.getInputStream()))
}
val printStream by lazy {
PrintStream(socket.getOutputStream())
}
val headerParamsMap = mutableMapOf<String,String>() // 储存Header的参数信息
val paramsMap = mutableMapOf<String,String>() // 储存参数的参数信息
lateinit var httpMethod : String
lateinit var subPath : String
lateinit var boundary : String
var isParseHeader = false
override fun run() {
super.run()
parseRequest() // 解析Request
handlerResult() // 构建返回的Response
bufferedReader.close()
printStream.close()
socket.close()
}
}
- 解析Request:parseRequest()
private fun parseRequest() {
var line = 1
var lineString : String? = bufferedReader.readLine()
while (lineString != null){
if (line == 1){
parseRequestLine(lineString)// 解析请求行
}
if (line != 1 && !isParseHeader){
parserHeader(lineString) // 解析请求头部
}
if (isParseHeader){
parseParams(lineString) // 解析参数
}
lineString = bufferedReader.readLine() //循环读取数据
line++
}
}
- 解析请求行、请求头部、请求参数
// 解析请求行
private fun parseRequestLine(lineString: String) {
val strings = lineString.split(" ")
Log.d("Http Method",strings[0])
Log.d("Http Path",strings[1])
Log.d("Http Version",strings[2])
}
// 解析请求头部
private fun parserHeader(lineString: String) {
if (lineString == ""){
isParseHeader = true
Log.d("Http Request","-------Header 解析完成 -------")
return
}else if (lineString.contains("boundary")){
boundary = parseBoundary(lineString)
Log.d("Http Request","boundary=$boundary")
}else{
val strings = lineString.split(":")
headerParamsMap[strings[0]] = strings[1]
Log.d("Http Content","${strings[0] .trim()} : ${strings[1] .trim()}")
}
}
private fun parseBoundary(lineString: String): String {
val strings = lineString.split(";")
parserHeader(strings[0])
if (strings[1] != null){
return strings[1].split("=")[1]
}
return ""
}
private fun parseParams(lineString: String) {
if (lineString == "--$boundary"){
Log.d("Http Content",lineString)
parseParamsType()
}
}
private fun parseParamsType() {
val stringParams = bufferedReader.readLine()
Log.d("Http Content",stringParams)
val name = parseBoundary(stringParams)
bufferedReader.readLine()
val value = bufferedReader.readLine()
paramsMap[name] = value
Log.d("Http Content"," ")
Log.d("Http Content","$value")
}
- 模拟POST请求
class HttpPost(var url:String) {
private lateinit var mSocket: Socket // 创建Socket
val paramsMap = mutableMapOf<String,String>() // 储存设置的信息
fun add(name : String, value : String){ // 添加参数
paramsMap[name] = value
}
fun execute(){
mSocket = Socket(url, HTTP_PORT)
val printStream = PrintStream(mSocket.getOutputStream())
val bufferedReader = BufferedReader(InputStreamReader(mSocket.getInputStream()))
val boundary = "http_boundary_123"
writeHead(boundary,printStream)
writeParams(boundary,printStream)
writeResponse(bufferedReader)
}
// 输出返回的Response
private fun writeResponse(bufferedReader: BufferedReader) {
Log.d("Http Response","请求结果...")
Thread{
var string = bufferedReader.readLine()
while (string == null || !string.contains("HTTP")){
string = bufferedReader.readLine()
}
while (string != null){
Log.d("Http Response",string)
string = bufferedReader.readLine()
}
}.start()
}
// 写入参数
private fun writeParams(boundary: String, printStream: PrintStream) {
var iterator = paramsMap.keys.iterator()
while (iterator.hasNext()){
val name = iterator.next()
printStream.println("--$boundary")
printStream.println("Content-Disposition: from-data; name=$name")
printStream.println()
printStream.println(paramsMap.get(name))
}
printStream.println("--$boundary--")
}
//写入请求头部
private fun writeHead(boundary: String, printStream: PrintStream) {
printStream.println("POST /api/login/ HTTP/1.1")
printStream.println("content-length:123")
printStream.println("Host:$url:$HTTP_PORT")
printStream.println("Content-Type:multipart/from-data;boundary=$boundary")
printStream.println("User-Agent:android")
printStream.println()
}
}
- 启动ServerSocket并发送请求信息
btnService.setOnClickListener { HttpServiceSocket().start() }
btnPost.setOnClickListener {
Thread {
val httpPost = HttpPost("127.0.0.1")
httpPost.add("userName","Simple")
httpPost.add("pwd","pwd_123")
httpPost.execute()
}.start()
}
- 运行后输出信息
上一篇: 你为什么只喜欢
下一篇: 超经典的DOS命令全集第1/6页