欢迎光临Software MyZone,有问题可留言或到站点论坛发帖,争取第一时间帮忙解决 || 站点论坛:火龙论坛 || 淘宝小店:应小心的易淘屋 【欢迎大家提建设性意见】

HTTP请求方式和报文解析

欢迎热爱编程的朋友们参与到cocos2d-x编程中,为了给大家提供良好的交流环境,网站以开启QQ群
Software MyZone:66202765(群号,欢迎加入,若满,请加1群)
Software MyZone 1群(2dx):286504621
Software MyZone 2群(山东):204954191
Software MyZone【Java群】:162865493
【加群请写:Software MyZone或者是firedragonpzy】
淘宝店:【58买单】初次开店,大家多多支持……
群论坛:【火龙论坛】正试运营阶段,欢迎大家多提些建设性意见……
短视频:【短视频去水印】支持抖音、快手等短视频去水印……
群问答:【火龙问答】正试运营阶段,欢迎大家多提些建设性意见……
摘自:https://blog.csdn.net/weixin_44104367/article/details/104481510

我的digitalocean推广链接:https://www.digitalocean.com/?refcode=65dfdb158f1a,Everyone you refer gets $10 in credit,从此链接注册,你将获得10美元。

一、概述

最近想尝试写一个关于网络请求的系列文章,将网络请求的基础、使用及网络框架的学习分析总结以下,大致准备从以下几个方面分析:

网络请求的基础
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()
}

运行后输出信息

————————————————
版权声明:本文为CSDN博主「Alex@W」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/alexwll/article/details/82287737

Tags:

发表评论