http学习笔记(模拟http请求和响应过程)

本文详细解析了HTTP请求和响应报文的格式,并通过Java实现了简单的HTTP服务器和客户端,包括如何处理POST请求及参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HTTP请求消息

1、http请求报文格式解析:起始行+首部字段+主体

POST /api/feed/ HTTP/1.1 –起始行
Accept-Encoding: gzip –请求头
Content-Length: 225873
Content-Type: multipart/form-data; boundary=OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
Host: www.myhost.com
Connection: Keep-Alive
(以下请求正文)
–OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp –分隔符
Content-Disposition: form-data; name=”lng” –参数
Content-Type: text/plain; charset=UTF-8 –(说明是文本)
Content-Transfer-Encoding: 8bit
–空行
116.361545 参数值
–OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
Content-Disposition: form-data; name=”lat”
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

39.979006
–OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
Content-Disposition: form-data; name=”images”; filename=”/storage/emulated/0/Camera/jdimage/1xh0e3yyfmpr2e35tdowbavrx.jpg”
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

这里是图片的二进制数据
–OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp–

2、http响应报文格式解析

HTTP/1.1 200 OK
Date: Sun, 08 Jan 2017 08:24:23 GMT
Content-Type: text/html;charset=UTF-8
Content-Length: 908

resp{
errmsg=操作成功
errno=0
}


进入正题

1、模拟http服务器


package http;

import java.io.IOException;
import java.net.ServerSocket;

/**
* Created by 蓝师傅 on 2017/1/7.
*/

public class SimpleHttpServer extends Thread {

//端口号
public static final int HTTP_PORT = 8005;
ServerSocket mSocket = null;


public SimpleHttpServer(){
    try {
        mSocket = new ServerSocket(HTTP_PORT);
    } catch (IOException e) {
        throw new RuntimeException("服务器socket初始化失败");
    }


}

@Override
public void run() {

    //等待客户端连接
    try {
        while (true){

            System.out.println("等待连接.....");
            //这里会一直阻塞,知道有socket连接进来
            //一旦客户端连接,拿到socket对象,交给DeliverThread 处理
            new DeliverThread(mSocket.accept()).start();

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}


可以看到,SimpleHttpServer 是一个线程,run方法无限循环,被mSocket.accept() 阻塞,当有socket接入的时候会调用new DeliverThread(mSocket.accept()).start();,把请求交给DeliverThread 去处理,代码如下:

package http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
* Created by 蓝师傅 on 2017/1/7.
* 处理请求的线程
*/

public class DeliverThread extends Thread {

Socket mClientSocket;
//输入流
BufferedReader mInputStream;
//输出流
PrintStream mOutputStream;
//请求方法
String httpMethod;
//子路径
String subPath;
//分隔符
String boundary;
//请求参数
Map<String,String> mParams = new HashMap<String,String>();
//是否已经解析完header
boolean isParseHeader;

public DeliverThread(Socket socket){
    mClientSocket = socket;
}
@Override
public void run() {

    /**
     * 处理请求
     */
    try {
        //获取输入流
        mInputStream = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
        //获取输出流
        mOutputStream = new PrintStream(mClientSocket.getOutputStream());

        //解析请求
        parseRequest();
        //返回response
        handleResponse();

    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            mInputStream.close();
            mOutputStream.close();
            mClientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

/**
 * 解析请求
 */
private void parseRequest() {
    String line; //读一行数据

    try {
        int lineNum = 0;

        while ((line = mInputStream.readLine())!= null){

            //第一行是请求行
            if(lineNum == 0){
                parseRequestLine(line);
            }
            //是否是结束行
            if(isEndLine(line)){
                break;
            }

            //解析header参数
            if(lineNum != 0 && !isParseHeader){
                parseHeaders(line);
            }

            if(isParseHeader){
                parseRequestParams(line);
            }

            lineNum ++;

        }
    } catch (Exception e) {
        e.printStackTrace();
    }

}


/**
 * 解析请求正文参数
 * @param line
 */
private void parseRequestParams(String line) throws IOException {
    /**
     *
     -----------------------------7d77af5871cc4b             分隔符
     Content-Disposition: form-data; name="last_server_md5"  参数名
                                                               空行
     bdf244bde5deb41d0f24d5f2e13efcba                        参数值
     -----------------------------7d77af5871cc4b
     Content-Disposition: form-data; name="t"

     Q=u%3D%25PQ%250df551a583a87f4e9%26src%3D360chrome%26t%3D1

     -----------------------------7d77af5871cc4b--
                                                             空行结束
     */
    if(line.equals("--"+boundary)){             // 分隔符开始
        String key = mInputStream.readLine();   //参数名
        mInputStream.readLine();                 //空行
        String value = mInputStream.readLine();  //参数值
        mParams.put(key,value);
        System.out.println("解析参数,key="+key+",value="+value);

    }

}


/**
 * 解析请求头
 * @param line
 */
private void parseHeaders(String line) {
    //header以空行结束
    if(line.equals("")){
        //解析结束
        isParseHeader = true;
        System.out.println("解析到空行,解析header 结束》》》》");
        return;
    }else if(line.contains("boundary")){
        boundary = parseSecondField(line);
        System.out.println("解析到分隔符》》》》"+boundary);

    }else{
        //解析普通的header
        parseHeaderParam(line);
    }


}

/**
 * 解析header参数
 * @param line
 */
private void parseHeaderParam(String line) {
    String[] keyValue = line.split(":");
    mParams.put(keyValue[0].trim(),keyValue[1].trim());
    System.out.println("解析header参数:key="+keyValue[0].trim()+",value = "+keyValue[1].trim());

}

/**
 * 解析header 第二个参数,返回分隔符
 * @param line
 * @return
 */
private String parseSecondField(String line) {
    //// TODO: 2017/1/8
    //Content-Type: application/octet-stream
    //Content-Type: application/octet-stream; boundary = abcdefg

    String[] keyValue = line.split(";");
    parseHeaderParam(keyValue[0]); // Content-Type: application/octet-stream
    if(keyValue.length >1){
        return  keyValue[1].split("=")[1];
    }

    return "";
}


/**
 * 是否是结束行
 * @param line
 * @return
 */
private boolean isEndLine(String line) {
    return line.equals("--"+boundary+"--");
}

/**
 * 解析请求行
 * @param line
 */
private void parseRequestLine(String line) {
    // 请求方法 空行  url  空行  http版本 回车符,换行符
    String[] tempString = line.split(" ");
    httpMethod = tempString[0]; //
    subPath = tempString[1]; //

    System.out.println("请求方法:"+tempString[0]);
    System.out.println("子路径:"+tempString[1]);
    System.out.println("http版本:"+tempString[2]);

}


/**
 * 响应:返回结果
 */
private void handleResponse() {

    try {
        sleep(1000);
        mOutputStream.println("HTTP/1.1 200 OK");
        mOutputStream.println("Content-Type: application.json");
        mOutputStream.println("");
        mOutputStream.println("{\"stCode\":\"success\"}");


    } catch (InterruptedException e) {
        e.printStackTrace();
    }


}

}


然后呢,在main中调用 new SimpleHttpServer.start(); 即可启动服务器


看看客户端吧:
以post请求为例,客户端需要调用

    HttpPost post = new HttpPost("127.0.0.1");
    post.addParams("name", "lanshifu");
    post.excute();
    //发送post请求

HttpPost 的代码如下:


package http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
* Created by 蓝师傅 on 2017/1/8.
*/

public class HttpPost {

String url = "127.0.0.1";
//请求参数
Map<String,String> mParams = new HashMap<String,String>();
Socket mSocket;

public HttpPost(String url) {
    this.url = url;
}

public void addParams(String key,String value){
    mParams.put(key,value);
}

public void excute(){
    try {
        mSocket = new Socket(this.url,SimpleHttpServer.HTTP_PORT);
        PrintStream outPutStream = new PrintStream(mSocket.getOutputStream());
        BufferedReader inputStream = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));

        String boundray ="分割符";

        writeHeader(boundray,outPutStream);

        writeParams(boundray,outPutStream);

        waitResponse(inputStream);


    } catch (IOException e) {
        e.printStackTrace();
        //创建连接失败
    }

}

/**
 * 构造header
 * @param boundray
 * @param outPutStream
 */
private void writeHeader(String boundray, PrintStream outPutStream) {
    outPutStream.println("POST /pro1/empManage1.0/login.php HTTP/1.1");
    outPutStream.println("Accept-Encoding: gzip, deflate, sdch");
    outPutStream.println("Accept-Language: zh-CN,zh;q=0.8");
    outPutStream.println("Content-Type: application/octet-stream; boundray="+boundray);
    outPutStream.println();
}


/**
 * 构造请求正文参数
 * @param boundray
 * @param outPutStream
 */
private void writeParams(String boundray, PrintStream outPutStream) {
     System.out.println("writeParams调用》》》");

    Iterator<String> iterator = mParams.keySet().iterator();
    while (iterator.hasNext()){
        String paramName = iterator.next();

        outPutStream.println("--"+boundray);
        outPutStream.println("Content-Dusposition:from-data; name="+paramName);
        outPutStream.println();
        outPutStream.println(mParams.get(paramName));

        System.out.println("writeParams,paramName=:"+mParams.get(paramName));

    }

    //结束符
    outPutStream.println("--"+boundray+"--");
    System.out.println("writeParams调用结束》》》");

}

/**
 * 等待响应
 * @param inputStream
 */
private void waitResponse(BufferedReader inputStream) {
     System.out.println("waitResponse调用结束》》》");

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值