深入研究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;
    }

}
时间: 2024-10-15 00:16:03

深入研究HTTP协议以及部分应用的相关文章

RTMP协议研究

RTMP协议研究 1协议研究概述 协议设计和分析一直都是在工作遇到,正好在这里总结一下,说到协议,在这个网络的时代,没有人可以离开它了.他存在我们生活中的任何角落,只不过我们平时,并没有注意到它的存在,可以这么说如果没有协议,我们生活和日常的工作生产都不能进行.如果仔细想想你生活中用到的所有东西,协议已经包含其中.那到底什么是协议呢?说的简单一点就是双方达成的共识,以便更好的交流,理论上协议是什么呢?如果学过<信号与系统>的人都知道有个简单的道理,就是信息在经过一个管道的符号集,到另一个符号集

AF攻防研究之四个层次Bypass WAF

从架构.资源.协议和规则4个层次研究绕过WAF的技术,助于全方位提升WAF防御能力. 绕过WAF的相关技术研究是WAF攻防研究非常重要的一部分,也是最有趣的部分,所以我在写WAF攻防时先写攻击部分.还是那句老话“不知攻焉知防”,如果连绕过WAF方法都不知道,怎么保证WAF能保护后端服务的安全.在我看来,WAF的绕过技术的研究将不断驱动防御水平提高. 以前一些WAF bypass的文章更像CASE的整理,都把焦点放在了规则对抗层面.绕过WAF规则,更像是正面对抗,属于下策.一直关注规则层面的绕过,

RTMP协议详解(转)

转自<RTMP协议详解(一) (二) (三) > Real Time Messaging Protocol(实时消息传送协议协议)是Adobe Systems公司为Flash播放器和服务器之间音频.视频和数据传输开发的私有协议. 具体使用RTMP的AS代码大概如下: var videoInstance:Video = your_video_instance; var nc:NetConnection = new NetConnection(); var connected:Boolean =

Web开发入门之HTTP协议

HTTP协议 在前一篇的基本概念介绍中,已经提到过HTTP协议.在这篇文章中,我们将继续深入研究HTTP协议的相关内容. 一.HTTP请求格式 HTTP请求由三部分构成,分别是:   ● 请求行     包括请求方式.URI和HTTP协议版本 ● 请求头 ● 请求正文(也叫请求实体) Ⅰ HTTP请求可以使用多种方式,主要包括以下几种. ●GET  (最常用) ●POST (常用) ●HEAD ●PUT ●DELETE 区别:其中,最常用的是GET请求.而只有在表单中指定为POST请求时,才使用

SSL协议会话建立过程解析

SSL协议 缩写 Secure SocketLayer,是一种制定的保证服务器和客户端安全通信的一种协议.最初是由 Netscape 在1996年发布,由于一些安全的原因SSL v1.0和SSL v2.0都没有公开,直到1996年的SSL v3.0.TLS是SSL v3.0的升级版,目前市面上所有的HTTPS都是用的是TLS,而不是SSL.本文主要分析和讲解TLS. HTTPS(Hyper TextTransfer Protocol over Secure Socket Layer),是一种基于

简谈【自动化协议逆向工程技术的当前趋势】

声明: 1)本文由我bitpeach原创撰写,禁止一切形式的转载.如有转载,侵权必究. 2)本简谈主要分为三个方面,第一是自动化协议逆向技术的基本理论,第二是当前发展趋势,第三是入门协议逆向技术的必备过程. 3)既是简谈,则文章篇幅不长,同时本文观点不一定正确,希望抛砖引玉,能得高人指点,幸为殊荣. 4)最近一个月比较忙,昨天正好写完稿子,今天就简写一些内容,避免重复,故为简谈. (一)协议逆向工程理论基础 1.1 什么是协议逆向技术 协议逆向工程是指在不依赖于协议描述的情况下,通过对协议实体的

Kademlia、DHT、KRPC、BitTorrent 协议、DHT Sniffer

catalogue 0. 引言 1. Kademlia协议 2. KRPC 协议 KRPC Protocol 3. DHT 公网嗅探器实现(DHT 爬虫) 4. BitTorrent协议 5. uTP协议 6. Peer Wire协议 7. BitTorrent协议扩展与ut_metadata和ut_pex(Extension for Peers to Send Metadata Files) 8. 用P2P对等网络思想改造C/S.B/S架构的思考 0. 引言 平常我们高端用户都会用到BT工具来

协议漏洞挖掘或协议逆向工程的一些思考

[0]协议漏洞挖掘,有时也叫协议逆向技术.学界两种叫法都有,但是国外一般都称协议逆向技术,笔者倾向于协议逆向技术的叫法. 国外的相关术语用了Protocol Reverse Engineering(协议逆向工程,PRE)这样的词,有的会用Network Protocol Reverse Engineering(NPRE). 国内也会用PRE,部分也会用Protocol Vulnerability mining(协议漏洞挖掘)这样的词. 协议逆向技术有两支,一支是基于指令代码的逆向技术,另一支是基

前端之HTTP协议

HTTP协议简介 作为学习前端开发的开始,我们必须搞明白以下几件事 1.什么是互联网      互联网=物理连接介质+互联网协议     2.互联网建立的目的? 数据传输打破地域限制,否则的话,我想获得对方主机上的数据,只能拿着硬盘去对方主机拷贝     3.什么是上网? 用户上网的过程即浏览器向服务端发送请求,然后将服务端主机的文本文件下载到本地显示的过程.而浏览器与服务器之间走的HTTP协议. 我们学习前端开发就是为了编排好一个文本文件存放到服务端主机,然后提供给浏览器下载显示的,所以在学习