深入研究HTTP协议以及部分应用
- 引言
工作了一段时间,都是在开发网页,自然和http打交道打得最多了,投身工作之后原来理解的东西又变得模糊,所以有必要深入探讨一下http协议的细节,也借此总结一下学习的成果。
- HTTP相关认识
对HTTP的学术认识我可是总结不到位的,不过就实际应用来说,HTTP协议是对TCP协议的一个细化扩展(个人理解)。TCP是一个可靠的面向连接的传输层协议,他指出两个通信端在通信时候保证通信正常进行的步骤,比如三次握手,比如传输的报文格式,这些规定了点到点的传输形式。而HTTP是基于TCP协议在应用层方向上的扩展。
HTTP分为请求,响应。请求的格式有,请求头,请求行,请求正文,这些格式规定了通信时候服务器所作出的响应。而响应的格式也是响应头和响应主体。作为工作实际中,应用到最多的两种方法GET和POST方法。他们的区别就在于,一个是携带参数在url中,一个是把参数放在请求正文中传输。现在来探究他们的格式。
- GET请求格式
它的格式如下。首先第一行发起HTTP请求方式GET,空格,请求目标目录下的资源(可以是/index.jsp这样,在这个url上携带参数,后加?username=123&password=123),空格,请求http版本。
之后就按照格式了,每一行的key值有不同的作用,没有深究。
最后,请求结束了之后记得换行(\r\n),不然服务器会等待。
GET / HTTP/1.1
Accept:*/*
Accept-Language:zh-cn
User-Agent: JavaSocket/1.8.0_91
Host:www.baidu.com:80
Connection:Keep-Alive
Content-Type:text/xml;charset=GBK
- POST请求格式
格式如GET,请求正文之前换行,正文借书后要空一行。Content-Type如下是使用了表单形式,便于后端获得数据。另外还有几种类型不再介绍。
POST / HTTP/1.1
Accept:*/*
Accept-Language:zh-cn
User-Agent: JavaSocket/1.8.0_91
Host:www.baidu.com:80
Connection:Keep-Alive
Content-Type:application/x-www-form-urlencoded;charset=GBK
Content-Length:25 username=134&password=123
- Socket实现HTTP请求
一个完整的http请求由很多小步骤,但是我这里分为两步,发送请求,接受响应。而发送请求之前,必须建立连接。构建请求,就如之前说的一样,在传输上构造请求的格式,就可以实现一个http请求了。
package HttpProtocol;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException; /**
* 实现一个http请求
* @author ctk
*
*/
public class HttpRequestImpl {
private final static String encoding = "GBK";
public static void main(String[] args) throws UnknownHostException, IOException {
Socket request = new Socket("localhost",8080);
OutputStreamWriter out = new OutputStreamWriter(request.getOutputStream());
StringBuffer sb = new StringBuffer();
String content = "username=134&password=123";
//换行前不要加空格
sb.append("POST /webPractice/RecvHttpMine HTTP/1.1\r\n");
sb.append("Accept:*/*\r\n");
sb.append("Accept-Language:zh-cn\r\n");
sb.append("User-Agent: JavaSocket/").append(System.getProperty("java.version")).append("\r\n");
sb.append("Host:locahost:8080\r\n");
sb.append("Connection:close\r\n");
// sb.append("Connection:Keep-Alive\r\n");
// sb.append("Content-Type:text/xml;"+"charset="+encoding+"\r\n");//请求提交text
sb.append("Content-Type:application/x-www-form-urlencoded;"+"charset="+encoding+"\r\n");//请求提交表单模式
sb.append("Content-Length:"+content.length()+"\r\n");
sb.append("\r\n");
sb.append(content+"\r\n");
//一个请求的结束应该有一个换行
sb.append("\r\n");
System.out.println("发送请求.....");
System.out.println(sb.toString());
out.write(sb.toString());
out.flush(); //获取响应
InputStream is = request.getInputStream();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(is, encoding));
String str = "";
int i = 0;
File f = new File("src/rev.txt");
if(!f.exists())
f.createNewFile();
FileOutputStream fout = new FileOutputStream(f);
while ((str = bufferedReader.readLine()) != null) {
fout.write((str+"\r\n").getBytes());
fout.flush();
System.out.println((i++)+":"+str);
}
is.close();
fout.close();
out.close();
request.close();
System.out.println("==============");
}
}
我的servlet放在tomcat上运行,请求到来之后由tomcat进行解析。
package com.util.servlet; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* Servlet implementation class RecvHttpMine
*/
@WebServlet("/RecvHttpMine")
public class RecvHttpMine extends HttpServlet {
private static final long serialVersionUID = 1L; /**
* @see HttpServlet#HttpServlet()
*/
public RecvHttpMine() {
super();
// TODO Auto-generated constructor stub
} /**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("get请求得到的数据: username="+request.getParameter("username"));
System.out.println("get请求得到的数据: password="+request.getParameter("password"));
response.getWriter().println("get success");
} /**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("post 接受到的参数username:"+request.getParameter("username"));
System.out.println("post 接受到的参数password:"+request.getParameter("password")); response.getWriter().println("post success");
} }
socket程序执行完毕之后控制台打印如下。
servlet程序打印如下
我这里试了很久,主要是Content-Type的问题,一定要设置成表单,不然提交之后tomcat解析为null。
- socket实现http响应
有了如上的基础,响应也就是一堆字符串格式而已,只要做个监听的socket,访问的时候返回响应就好,解析参数就在读取响应的过程做就好了。
package HttpProtocol; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date; /**
* http服务器
* @author ctk
*
*/
public class HttpServer {
private final static String encoding = "UTF-8";
private final static int stateCode = 200;
public static void main(String[] args) throws IOException {
ServerSocket server = null;
String responseContent = "this is a test";
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 ").append(stateCode).append(" OK\r\n");
sb.append("Server: Ctk\r\n");
sb.append("Content-Length: ").append(responseContent.length()).append("\r\n");
sb.append("Date: ").append(new Date(System.currentTimeMillis()).toString()).append("\r\n");
sb.append("Connection: close\r\n");
sb.append("\r\n");
sb.append(responseContent).append("\r\n");
sb.append("\r\n");
try {
server = new ServerSocket(8000);
System.out.println("监听开始....");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while(true){
Socket socket = server.accept();
System.out.println("获得连接:"+socket.getInetAddress()+":"+socket.getPort());
int len = -1;
InputStream in = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(in, encoding));
String str = "";
while (bufferedReader.read() != -1) {
if(!((str = bufferedReader.readLine()) != null))
System.out.println(str);
else
break;
}
System.out.println("回送响应==============");
System.out.println(sb.toString());
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
out.write(sb.toString());
out.flush(); out.close();
in.close();
socket.close();
}
}
}
运行之后控制台打印如下。

在服务器readLine的时候,要判断read是否为-1,否则无法结束。
- 另类玩法
我用它来访问百度,返回了404

然后我用它来访问网易,竟然返回了8000行的代码。

如果你用post访问网易的主页,它返回一个405,method not allow。
可见,学通原理,走遍天下都不怕。
- 转发和重定向
这个是开发中最常用的概念了。
转发:客户端只产生一个请求,服务端负责转发这个请求,方老师神比喻:你找我借钱,我帮你找他借钱。
重定向:客户端再发起一个新请求,请求服务器资源,还是方老师:你找我借钱,我让你找他借钱。
转发主要的特征就是同一个请求域,在这个请求域中可以共用参数。重定向没啥好说的。
- 分步注册实例
转发实现:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>第一步</title>
</head>
<body>
<h1>第一步</h1>
<form action = "sendTest" method="post">
<input name="msg" type="text" id="msg" value="${save}"/>
<input name="page" style="display:none;" value="step2.jsp"/>
<br/>
<button id="nextStep" type="submit">下一步</button>
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>第二步</title>
</head>
<body>
<h1>第二步</h1>
<form action = "sendTest" method="post">
<input name="msg" type="text" id="msg" value="${save}"/>
<input name="page" style="display:none;" id="page"/>
<br/>
<button id="preStep" type="submit">上一步</button>
<button id="nextStep" type="submit">下一步</button>
</form>
<script type="text/javascript">
$(document).ready(function(){
$('#preStep').click(function(){
$('#page').val("step1.jsp");
});
});
$(document).ready(function(){
$('#nextStep').click(function(){
$('#page').val("step3.jsp");
});
});
</script>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>第三步</title>
</head>
<body>
<h1>第三步</h1>
<form action = "sendTest" method="post">
<input name="msg" type="text" id="msg" value="${save}"/>
<input name="page" style="display:none;" id="page"/>
<br/>
<button id="preStep" type="submit">上一步</button>
</form>
<script type="text/javascript">
$(document).ready(function(){
$('#preStep').click(function(){
$('#page').val("step2.jsp");
});
});
</script>
</body>
</html>
serlvet
package com.util.servlet; import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* Servlet implementation class sendTest
*/
@WebServlet("/sendTest")
public class sendTest extends HttpServlet {
private static final long serialVersionUID = 1L;
private String msg;
private String page;
/**
* @see HttpServlet#HttpServlet()
*/
public sendTest() {
super();
// TODO Auto-generated constructor stub
} /**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
msg = request.getParameter("msg");
page = request.getParameter("page");
System.out.println("得到的msg = "+msg);
System.out.println("得到的page = "+page);
if(msg == null){
System.out.println("分支1");
request.setAttribute("save", "");
request.getRequestDispatcher("step1.jsp").forward(request, response);
}else{
System.out.println("分支2");
request.setAttribute("save", msg);
request.getRequestDispatcher(page).forward(request, response);
}
} /**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
} public String getPage() {
return page;
} public void setPage(String page) {
this.page = page;
} }
深入研究HTTP协议以及部分应用的更多相关文章
- RTMP协议研究
RTMP协议研究 1协议研究概述 协议设计和分析一直都是在工作遇到,正好在这里总结一下,说到协议,在这个网络的时代,没有人可以离开它了.他存在我们生活中的任何角落,只不过我们平时,并没有注意到它的存在 ...
- 闲来无聊,研究一下Web服务器 的源程序
web服务器是如何工作的 1989年的夏天,蒂姆.博纳斯-李开发了世界上第一个web服务器和web客户机.这个浏览器程序是一个简单的电话号码查询软件.最初的web服务器程序就是一个利用浏览器和web服 ...
- CWMP开源代码研究1——开篇之作
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...
- WAF攻防研究之四个层次Bypass WAF
从架构.资源.协议和规则4个层次研究绕过WAF的技术,助于全方位提升WAF防御能力. 绕过WAF的相关技术研究是WAF攻防研究非常重要的一部分,也是最有趣的部分,所以我在写WAF攻防时先写攻击部分.还 ...
- RTMP协议
Real Time Messaging Protocol(实时消息传送协议协议)概述 实时消息传送协议是Adobe Systems公司为Flash播放器和服务器之间音频.视频和数据传输开发的私有协 ...
- RTMP协议详解(转)
转自<RTMP协议详解(一) (二) (三) > Real Time Messaging Protocol(实时消息传送协议协议)是Adobe Systems公司为Flash播放器和服务器 ...
- http协议与web本质
当你在浏览器地址栏敲入“http://www.csdn.net/”,然后猛按回车,呈现在你面前的,将是csdn的首页了(这真是废话,你会认为这是理所当然的).作为一个开发者,尤其是web开发人员,我想 ...
- 逆向wireshark学习SSL协议算法(转)
小贴士:SSL协议的定义 SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整 ...
- [置顶] Java开源代码研究总结
由于工作中的需要,最近在研究SNMP协议和利用snmp4j和snmp4j.agent( http://www.snmp4j.org/ ),实现snmp的南向和北向功能. 结合以前看过的 ...
随机推荐
- 4.1/4.2 多线程进阶篇<上>(Pthread & NSThread)
本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人 “简书” 本文源码 Demo 详见 Githubhttps://github.com/shorfng ...
- 记录我的点点滴滴从此刻做起——iOS开发工程师
作为一个iOS工程师,想写博客也是有原因的:首先有这个想法(写博客的想法)也是因为想到自己都从事iOS开发快两年了,怎么也只会堆代码,写view,技术真的很一般,感觉都要被淘汰了:基于以上原因,自己也 ...
- Laravel大型项目系列教程(四)显示文章列表和用户修改文章
小编心语:不知不觉已经第四部分了,非常感谢很多人给小编提的意见,改了很多bug,希望以后能继续帮小编找找茬~小编也不希望误导大家~这一节,主要讲的 是如何显示文章列表和让用户修改文章,小编预告一下(一 ...
- AEAI HR_v1.5.2升级说明,开源人力资源管理系统
1.升级说明 本次AEAI HR升级内容主要是针对数通畅联推出AEAI ECP企业云联平台而升级的,其中对AEAI HR的各模块进行扩展,同时增加了移动门户版功能及为AEAI ECP提供数据服务接口. ...
- HTML之DocType的几种类型 -转载
HTML之DocType的几种类型转载 doctype类型详细doctype的几种类型html之doctype 分类: 前端文摘 在默认情况下,FF和IE的解释标准是不一样的,也就是说,如果一个网页 ...
- postgres创建表的过程以及部分源码分析
背景:修改pg内核,在创建表时,表名不能和当前的用户名同名. 首先我们知道DefineRelation此函数是最终创建表结构的函数,最主要的参数是CreateStmt这个结构,该结构如下 typede ...
- Linux paste命令
Linux paste命令用于合并文件的列. paste指令会把每个文件以列对列的方式,一列列地加以合并. 语法 paste [-s][-d <间隔字符>][--help][--versi ...
- 理解Docker(7):Docker 存储 - AUFS
(1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 (4)Docker 容器的隔离性 - 使用 ...
- 阿里技术协会好文推荐:Android绘制流程http://click.aliyun.com/m/8719/
一.前言 1.1.C++界面库 MFC.WTL.DuiLib.QT.Skia.OpenGL.Android里面的画图分为2D和3D两种: 2D是由Skia 来实现的,3D部分是由OpenGL实现的. ...
- 启用SQLite的Data Provider 运行WECOMPANYSITE时遇到ERROR CREATING CONTEXT 'SPRING.ROOT': ERROR THROWN BY A DEPENDENCY OF OBJECT 'SYSTEM.DATA.SQLITE'
从网上下载的源码WeCompanySite,运行时报错 Error creating context 'spring.root': Error thrown by a dependency of ob ...