1

序言

从我们的最开始使用的HttpClient到HttpURLConnection,当然现在如果还在说你在项目中自己封装使用它们两个,有点多余了,无论是Volley还是OkHttp都是要比其好很多的,写起来方便,效率高,你就是要造轮子,这个就无法阻挡你了,拥有一个造轮子的心,还要记得,性非议也善假于物也。今天回顾下,基本使用,然后从一个post到网络端接收的一个处理过程,进行一个分析,让我们对于其底层的实现有所了解。而现在,HttpClient已经被弃用,所以这里也不再讲,来讲下HttpURLConnection。

HttpURLConnection

HttpURLConnection相比于HttpClient,其API简单,体积小,而且其压缩和缓存机制可以有效的减少网络访问的流量,在提升速度和省电方面都很有优势,

public void sendRequest(String url) throws IOException{
        InputStream is = null;
        try{
            URL newUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)newUrl.openConnection();
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(10000);
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoOutput(true);
            conn.setRequestProperty("Connection", "Keep-Alive");
            String data = "username=jensea";
            OutputStream out = conn.getOutputStream();// 获得一个输出流,向服务器写数据
            out.write(data.getBytes());
            out.flush();
            out.close();
            conn.connect();
            is = conn.getInputStream();
            String result = convertStreamToString(is);
        }catch (IOException e){
            
        }finally {
            is.close();
        }
    }

    private String convertStreamToString(InputStream is) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line = null;
        try{
            while((line=reader.readLine())!=null){
                sb.append(line+"/n");
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        return sb.toString();
    }

从客户端到服务端看网络请求实现

对于客户端和服务端,其实现无非是通过对Socket进行读写,然后对其进行解析,根据解析内容作出响应,然后客户端从自己的socket缓冲区中读取信息,而我们所使用的HttpClient,HttpURLConnection则是对其一个封装,将其中的流读写的细节进行了一个隐藏,而我们的Volley,OkHttp则是在其基础上,结合线程池进行了一个更高效的封装。

服务端

public class SimpleHttpServer extends Thread {
    public static final int HTTP_PORT = 8000;
    ServerSocket mSocket = null;

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

    @Override
    public void run() {
        try {
            while(true){
                System.out.print("等待连接中");
                new DeliverThread(mSocket.accept()).start();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
  • 绑定制定端口套接字

  • 等待连接请求,创建处理线程

对于具体的请求内容处理交给了Deliver线程

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>();

    Map<String, String> mHeaders = new HashMap<String, String>();

    boolean isParseHeader = false;

    public DeliverThread(Socket socket) {
        mClientSocket = socket;
    }

    @Override
    public void run() {
        try {
            mInputStream = new BufferedReader(new
                    InputStreamReader(mClientSocket.getInputStream()));
            mOutputStream = new PrintStream(mClientSocket.getOutputStream());
            parseRequest();
            handleResponse();
        } catch (IOException e) {

        }
    }
//处理请求,读出每一行,确定是头部,还是请求体,然后交给不同的函数处理
    private void parseRequest() {
        String line;
        try {
            int lineNum = 0;
            while ((line = mInputStream.readLine()) != null){
                if(lineNum==0){
                    parseRequestLine(line);
                }
                if(isEnd(line)){
                    break;
                }
                if(lineNum!=0&&!isParseHeader){
                    parseHeader(line);
                }
                //头部被处理完,处理请求参数
                if(isParseHeader){
                    parseRequestparams(line);
                }
                lineNum++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    
    //处理请求行,请求方法+空格+HTTP版本号
    private void parseRequestLine(String lineOne) {
        String[] tmpStrings = lineOne.split(" ");
        httpMethod = tmpStrings[0];
        subPath = tmpStrings[1];
    }
    //处理头部
    private void parseHeader(String headLine) {
        if (headLine.equals("")) {
            isParseHeader = true;
            return;
        } else if (headLine.contains("boundary")) {
            boundary = parseSecondField(headLine);
        }else{
            parseHeaderParam(headLine);
        }
    }

    private String parseSecondField(String line) {
        String[] headerArray = line.split(";");
        parseHeaderParam(headerArray[0]);
        if(headerArray.length>1){
            return headerArray[1].split("=")[1];
        }
        return "";

    }
    //处理头部每一个参数,然后将其添加到mHeaders中
    private void parseHeaderParam(String headerLine) {
        String[] keyvalue = headerLine.split(":");
        mHeaders.put(keyvalue[0].trim(), keyvalue[1].trim());
    }

    //处理请求参数
    private void parseRequestparams(String paramLine) throws IOException{
        if(paramLine.equals("--"+boundary)){
            String ContentDisposition = mInputStream.readLine();
            String paraName = parseSecondField(ContentDisposition);
            mInputStream.readLine();
            String paramvalue = mInputStream.readLine();
            mParams.put(paraName,paramvalue);
        }
    }
    
    //根据传递的参数进行相应的处理
    private void handleResponse(){
        mOutputStream.println("HTTP/1.1 200 OK");
        mOutputStream.println("Content_Type:application/json");
        mOutputStream.println();
        mOutputStream.println("{\"stcode\":\"success\"}");

    ]
}

上述代码设计到一些字符串的处理,相对也比较简单,对于其中请求参数的处理,你可能会感觉有点难懂。首先要明白对于其要处理的数据中,请求的数据格式是怎么样的。

--DFDFJD3243dfddfdfdffdf
content-Disposition:form-data; name="username"

Mr.Simple

通过其格式,再去回顾一下上面的处理方式就不难看懂了。

客户端

public class HttpPost {
    public String url;
    private Map<String,String> mParamsMap = new HashMap<String,String>();
    Socket mSocket;

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

    public void addParam(String key,String value){
        mParamsMap.put(key,value);
    }

    public void execute(){
        try{
            mSocket = new Socket(this.url,SimpleHttpServer.HTTP_PORT);
            PrintStream outputStream = new PrintStream(mSocket.getInputStream());
            BufferedReader inputStream = new BufferedReader(new InputStreamReader(mSocket
                    .getInputStream()));
            final String boundary = "my_boundary_123";
            writeHeader(boundary,outputStream);
            writeParams(boundary,outputStream);
            waitResponse(inputStream);
        }catch (UnknownHostException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            mSocket.close();
        }
    }

    private void writeHeader(String boundary,PrintStream outputStream){
        outputStream.println("POST /api/login/ HTTP/1.1");
        outputStream.println("content-length:123");
        outputStream.println("Host:"+this.url);
        outputStream.println("boundary="+boundary);
        outputStream.println("Uset-Agent:android");
        outputStream.println();
    }

    private void writeParams(String boundary,PrintStream outputStream){
        Iterator<String> paramsKeySet = mParamsMap.keySet().iterator();
        while(paramsKeySet.hasNext()){
            String paramName = paramsKeySet.next();
            outputStream.println("--"+boundary);
            outputStream.println("Content-Disposition:form-data; name="+paramName);
            outputStream.println();
            outputStream.println(mParamsMap.get(paramName));
        }
        outputStream.println("--"+boundary+"--");
    }

    private void waitResponse(BufferedReader inputStream) throws IOException{
        String responseLine = inputStream.readLine();
        while(responseLine==null||!responseLine.contains("HTTP")){
            responseLine = inputStream.readLine();
        }
        while((responseLine=inputStream.readLine())!=null){
            System.out.println(responseLine);
        }
    }
}

代码量不大,创建socket,然后向socket中,写入头部,请求参数,等待服务器的回应,但是这个过程中,socket又干了些什么呢?socket又是个什么呢?这个可以参考我前面对于网络和Linux下进程通信的文章。


Jensen95
2.9k 声望534 粉丝

连续创业者。