Android 常用开源框架源码解析 系列 (二)网络框架之一 OkHttp杂题

1、Android基础网络编程:socket、HttpClient、HttpURLConnection

    1.1 Socket 定义

    是一个对TCP/IP协议进行封装的编程调用接口,本身不是一种协议是接口Api!!

    成堆出现,一对套接字:包括ip地址和端口号

 

    基于应用层和传输层抽象出来的一个层。App可以通过该层发送、接收数据,并通过Socket将App添加到网络当中

    简单来说就是应用与外部通信的端口,提供了两端数据的传输的通道

 

    1.2 Socket通信模型

    基于TCP和UDP协议的两种模型

  •     TCP:采用字节流协议来提供可靠的字节流服务
  •     UDP:采用数据报文的形势提供数据,打包的形势发送服务

 

    1.3 Socket与Http对比

   Android与服务器的通信方式

    (1)Http通信

        基于请求-响应方式;属于应用层解决如何包装数据的问题

    (2)Socket通信

        采用服务器主动发送数据的方式,Socket属于传输层;解决数据如何在网络中传输

 

    1.4 Socket实现

/**

* Tcp 客户端Socket

*/

public void TcpSendMessage(String msg) {

    Socket socket = null;

    OutputStream outputStream = null;

    InputStream inputStream = null;

    try {

        //1、创建客户端Socket对象,传入目标主机名orId地址和端口号

        socket = new Socket("192.168.1.1", 8080);

        //2、通过socket获取输出流

        outputStream = socket.getOutputStream();

        //3、写入输出流操作

        outputStream.write(msg.getBytes());

        //4、关闭socket操作,msg写入结束 ps:不调用会造成服务器端消息返回的无法获取

        socket.shutdownOutput();

        //5、msg的IO流读取操作

        inputStream = socket.getInputStream();

        byte[] b = new byte[1024];

        int len = -1;

        final StringBuffer sb = new StringBuffer();

        while ((len = inputStream.read(b)) != -1) {

            sb.append(new String(b, 0, len, Charset.forName("gbk")));

        }

        //todo 在主线程中更新Ui

    } catch (UnknownHostException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    } finally {

        //注意,输出流不需要关闭,因为它不是创建的而是通过Socket中得到输出流对象获取的

        if ((socket != null)) {

            try {

                socket.close();

            } catch (IOException e) {

                e.printStackTrace();

            }

        }

    }

}

/**

* Tcp 服务器Socket

*/

public void ServerMessage() {

    ServerSocket server = null;

    Socket socket = null;

    try {

        //1、创建服务器Socket对象并监听需要的端口号

        server = new ServerSocket(8888);

        while (true) {

            //2、接收客户端发送的请求;ps:若客户端没有发送数据,该线程会停滞,accept中会阻塞

            socket = server.accept();

            //3、获取输入流

            InputStream inputStream = socket.getInputStream();

            //4、创建缓存输入流进行数据的读入

            BufferedInputStream bis = new BufferedInputStream(inputStream);

            byte[] b = new byte[1024];

            int len = -1;

            while ((len = bis.read()) != -1) {

                System.out.println(new String(b, 0, len, "UTF-8"));

            }

            socket.shutdownInput();

            OutputStream outputStream = socket.getOutputStream();

            outputStream.write("收到".getBytes());

 

            bis.close();

            socket.close();//serverSocket不能被关闭!

            socket = null;

        }

    } catch (IOException e) {

        e.printStackTrace();

    }

}

  1.4 HttpClient 和 HttpURLConnection

基本的HttpURLConnection链接  一个简单Get实例:

/**

* HttpURLConnection

*/

public static void readContentFromGet() throws IOException {

    //1、拼接get请求字符串

    String getURL = "GET_URL" + "?username= " + URLEncoder.encode("fat-man", "UTF-8");

    //2、创建URL对象

    URL getUrl = new URL(getURL);

    //3、表明这个connection只能发送一个请求

    HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();

    //4、建立链接,这时并没有将真正请求发送给服务器端

    connection.connect();

    //取得输入流,并使用Reader读取,getInputStream()方法将真正的请求发送给服务器端

    BufferedReader reader = new BufferedReader(new InputStreamReader

            (connection.getInputStream()));

    String lines;

    while ((lines = reader.readLine()) != null) {

        System.out.println(lines);

    }

    reader.close();

    //断开链接,关闭底层Socket链接

    connection.disconnect();

}

 

2、了解WebSocket?知道和Socket的区别?OkHttp是如何处理WebSocket的相关问题

 

    2.1 WebSocket

   推送-轮询 是特定的时间间隔,由浏览器对服务器发送Http请求,然后由服务器返回最新的数据给客户端的浏览器

 

短轮询

提交表单的形势进行数据传递;

   缺陷:在某个时间段Server没有更新数据,但Client端仍然每隔一段时间发送请求来询问,所以这段时间内的询问都是无效的,冗余数据。

 

长轮询

    服务器端接收request请求后不会立即返回数据response给客户端,会检查数据是否有更新。如果有更新了就会返回给客户端数据,如果没有更新则不返回。

 

缺陷:

  •             浪费带宽 
  •             Http Head 过大实际body缺不大
  •             消耗服务器CPU占用

 

WebSocket

  WebSocket一旦建立了两端的连接,可以不断的进行通信,是一种全双通的通信模式。

 

    2.2 WebSocket 与Http

Http是 懒惰的协议,有接收才有响应

WebSocket是全双向通信网络协议,server主动向client发送数据

 

   2.3 WebSocket 与Socket

Socket 首先要明白是一种接口 并不是一向协议

WebSocket是同等级的网络协议

两者没有任何关系

    

  •     本质上是一个基于TCP的协议
  •     向服务器发起一个HTTP请求 /“Upgrade WebSocket”
  •     服务器端解析头信息

   

 2.4 OkHttp是如何处理WebSocket的

        

  private Handler handler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            super.handleMessage(msg);

        }

    };

 

    /**

     * WebSocketListener 运行在工作线程的

     */

    private final class EchoWebSocketListener extends WebSocketListener {

 

        //WebSocket 和远程 服务器端建立链接

        @Override

        public void onOpen(WebSocket webSocket, Response response) {

//            super.onOpen(webSocket, response);

            //OkHttp使用自己的后台发送数据,不用担心sendMessage1会阻塞当前线程的问题

            webSocket.send("xxx");

            //发送消息已经完成

            webSocket.close(1000, "ss");

        }

 

        /**

         * onMessage()中与主线程的交互要非常非常小心!!与主线程用handler交互可以

         */

        @Override

        public void onMessage(WebSocket webSocket, String text) {

//            super.onMessage(webSocket, text);

            setText("onMessage :" + text);

            handler.sendEmptyMessage(0);

        }

 

        //远端已经没有数据的情况,准备关闭WebSocket链接但是还没有关闭

        @Override

        public void onClosing(WebSocket webSocket, int code, String reason) {

//            super.onClosing(webSocket, code, reason);

            setText("onClosed:" + code + "/" + reason);

        }

 

        //这个链接已经完全被关闭了

        @Override

        public void onClosed(WebSocket webSocket, int code, String reason) {

//            super.onClosed(webSocket, code, reason);

            setText("onClosed:" + code + "/" + reason);

        }

 

        @Override

        public void onFailure(WebSocket webSocket, Throwable t, Response response) {

//            super.onFailure(webSocket, t, response);

            setText("onFailure:" + t + "/" + response);

        }

    }

 

3、Http如何处理缓存?OkHttp如何处理缓存相关问题?

 

  • 强制缓存

(1)Expires 过期时间 —Http1.0 

    值表示服务器端返回的到期时间;

    下一次请求时候,请求的时间 <  服务端返回的到期时间 —》会直接使用缓存数据

缺陷:

    到期时间是由服务器端生成的,会与客户端的时间造成误差

 

(2)Cache-Control

    Cache-control 是由服务器返回的Response中添加的头信息;

   作用是告诉用户是从本地读取缓存还是从服务端获取消息

 

Cache-Control的取值:   

  •         private :表示客户端可以取缓存
  •         public  :表示客户端和代理服务器都可以缓存
  •         max-age:表示缓存内容在多少秒后失效
  •         no-cache:表示强制缓存的标识无法处理缓存
  •         no-store:表示不进行缓存

 

  • 对比缓存——网络协议

 

    1、首先需要进行比较判断是否可以使用缓存

    2、服务器会将缓存标识与数据一起返回给客户端

 

流程:

    再次请求数据——>

            if(有缓存 != null) {

                    if(是否过期 ?){

                            没过期直接从缓存读取数据

                      }else if(无法判断是否已经过期){

                       // 进行对比缓存检查

                                if(判断ETag 标准){

                                  向web服务器请求带If-None-Match—— 两者进行匹配!     

                                       资源 有改动返回200,请求响应; 无改动返回304 直接从缓存读取    

                                  }else if(ETag != null){

                                    if(Last-Modified == null ){

                                        向服务器请求带If-Modified-Since

                                            有改动返回200 ,请求响应;无改动返回304 直接从缓存读取

                                    }

                                 }

                      }

             }

    

 

当前资源是否被改动过,改动过返回200,再去请求响应,没有改动过返回304

 

ETag / If-None-Match 成对出现

 

  • ETag   :            服务器端响应请求时候,告诉浏览器当前资源在服务器的唯一标识

   ps:生成规则由服务器端决定唯一标识 与下面的进行匹配

 

  • If-None-Match:再次请求服务器时候,通过此字段通知服务器客户端缓存数据的唯一标识

 

Last-Modified / If-Modefied-Since 成对出现

 

  • Last-Modified    :   服务器在响应请求时,告诉浏览器资源的最后修改时间
  • If-Modefied-Since :再次请求服务器时,浏览器通知服务器端上次请求时,服务器返回的资源最后修改时间

 

 

4、断点续传的原理?如何实现?OkHttp中如何实现相关原理?

    4.1 断点续传

   断点续传:从文件已经下载完的地方开始继续下载

    实现:客户端发送给浏览器端的请求头报文当中,添加这次下载从什么位置开始的新条件

RANGE:bytes = 200080 - 

        表明这次从 资源的 200080位置开始下载

 

在Java中用HttpURLConnection 实现:

public void doBreakDownLoadJava() {

    URL url = null;

    try {

       //1、创建URL对象

        url = new URL("http://www.sjtu.edu.cn/down.zip");

        //2、通过URL创建 HttpURLConnection,由它完成网络请求

        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

       //3、通过setRequestProperty ,创建请求头部信息,设置断点续传的开始位置

        httpURLConnection.setRequestProperty("RANGE", "bytes=2000080”);

        InputStream inputStream = httpURLConnection.getInputStream();

        //4、获取到流信息保存到文件中,用字节进行指定的读取

        RandomAccessFile oSaveFile = new RandomAccessFile("down.zip", "rw");

        long nPos = 2000070;

        //5、表明文件读取的位置

        oSaveFile.seek(nPos);

        //常规IO流读写操作

        byte[] b = new byte[1024];

        int nRead;

        while ((nRead = inputStream.read(b, 0, 1024)) > 0) {

         //6、对文件写入操作

            oSaveFile.write(b, 0, nRead);

        }

    } catch (MalformedURLException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    }

}

 

OkHttp中相关实现实例:

/**

* OkHttp断点续传

*/

public void doDownloadWithOkHttp() {

    InputStream is = null;

    RandomAccessFile savedFie = null;

    File file;

    //1、首先记录已经下载的文件长度

    long downloadLength = 0;

    String downloadUrl = "www.baidu.com/wenku/o2232.txt";

    String filename = downloadUrl.substring(downloadUrl.lastIndexOf("/"));

    File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

    file = new File(directory + filename);

   //2、判断下载的文件是否存在 ,存在的话下载长度范围赋值

    if (file.exists()) {

        downloadLength = file.length();

    }

    long contentLength = getContentLength(downloadUrl);

    //3、创建OkHttpClient对象

    OkHttpClient client = new OkHttpClient();

   //4、创建Request对象,通过addHeader加头部信息添加到请求里,表明下载的范围

    Request request = new Request.Builder()

            .addHeader("RANGE", "bytes=" + downloadLength + "-")

            .url(downloadUrl)

            .build();

    //5、开启一个同步请求

    try {

        Response response = client.newCall(request).execute();

        //6、根据Response进行判断

        if (request != null) {

            is = request.body().byteStream();

            savedFie = new RandomAccessFile(file, "rw");

            //7、跳过已经下载的字节

            savedFie.seek(downloadLength);

            byte[] b = new byte[1024];

            int total = 0;

            int len;

            while ((len = is.read()) != -1) {

                total += len;

                savedFie.write(b, 0, len);

               //8、计算已经下载的百分比

                int progress = (int) ((total + downloadLength) * 100 / contentLength);

            }

        }

    } catch (IOException e) {

        e.printStackTrace();

    }

}

 

5、多线程下载原理,OkHttp如何实现?

 

多线程下载:每个线程只负责下载文件的一部分,也就是分段加载。

5.1 Java中多线程

在Java中多线程的下载 实例:

    

    /**

     * 多线程下载

     */

   public void download() throws Exception {

        URL url = new URL(path);

        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        connection.setRequestMethod("GET");

        connection.setConnectTimeout(10000);

       //根据connection的ResponseCode来进行相应的操作

        int code = connection.getResponseCode();

        if (code == 200) {

            //获取资源文件大小

            int connectionLength = connection.getContentLength();

            //在本地创建一个与资源同样大小的文件来占位

            RandomAccessFile randomAccessFile = new RandomAccessFile(

                    new File(targetfilePath, getFilePath));

            //在本地创建一个占位文件

            randomAccessFile.setLength(connectionLength);

 

            //计算每一个线程加载的数量

            int blockSize = connectionLength / threadCount;

            //为每一个线程分配任务

            for (int threadId = 0; threadId < threadCount; threadId++) {

                //线程开始/结束下载的位置

                int startIndex = threadId * blockSize;

                int endIndex = (threadId + 1) * blockSize - 1;

                if (threadId == (threadCount - 1)) {

                   //将所有任务交给endIndex完成

                    endIndex = connectionLength - 1;

                }

                //开始正式多线程的实现

                new DownloadThread(threadId, startIndex, endIndex).start();

            }

            randomAccessFile.close();

        }

    }

 

    /**

     * 开始正式多线程的实现

     */

   private class DownloadThread extends Thread {

        private int threadID, startIndex, endIndex;

 

        public DownloadThread(int threadID, int startIndex, int endIndex) {

            this.threadID = threadID;

            this.startIndex = startIndex;

            this.endIndex = endIndex;

        }

 

        @Override

        public void run() {

            System.out.println("线程" + threadID + "开始下载");

            try {

                //1、分段下载也需要分段的获取URL 将文件保存到本地

                URL url = new URL(path);

                //2、加载下载位置的文件,获取文件大小

                File downThreadFile = new File(targetFilePath, "downThread_" + threadID + ".dt");

                //3、创建一个新的RandomAccessFile

                RandomAccessFile downThreadStream = null;

                if (downThreadFile.exists()) {

                    //4、如果文件不存在

                    downThreadStream = new RandomAccessFile(downThreadFile, "rwd");

                    String startIndex_str = downThreadStream.readLine();

                    if (startIndex_str != unll || !"".equals(startIndex_str)) {

                        this.startIndex = Integer.parseInt(startIndex_str) - 1;//下载起点

                    }

                } else {

                    downThreadStream = new RandomAccessFile(downThreadFile, "rwd");

                }

                HttpURLConnection connection = (HttpURLConnection) url.openConnection();

                connection.setRequestMethod("GET");

                connection.setConnectTimeout(10000);

               //5、设置分段下载头信息

                connection.setRequestProperty("RANGE", "bytes=" + startIndex + "-" + endIndex);

                if (connection.getResponseCode() == 206) {//6、部分资源请求成功

                    InputStream inputStream = connection.getInputStream();

                    //7、获取创建的文件

                    RandomAccessFile randomAccessFile = new RandomAccessFile(

                            new File(targetFilePath, getFileName(url), "rw")

                    );

                    //8、文件写入的计算位置

                    randomAccessFile.seek(startIndex);

                   //IO流读写操作

                    byte[] buffer = new byte[1024];

                    int length = -1;

                    int total = 0;//记录本次下载文件的大小

                    while ((length = inputStream.read(buffer)) > 0) {

                        randomAccessFile.write(buffer, 0, length);

                        total += length;

                        downThreadStream.seek(0);

                        downThreadStream.write((startIndex + total + "").getBytes("UTF-8"));

                    }

                    //9、关闭IO流操作

                    downThreadStream.close();

                    inputStream.close();

                    randomAccessFile.close();

                    cleanTemp(downThreadFile);//删除创建的占位临时文件

                    System.out.println("线程:" + threadID + "下载完毕");

                } else {

                    System.out.println("响应码:" + connection.getResponseCode() + "服务器不支持");

                }

            } catch (FileNotFoundException e) {

                e.printStackTrace();

            } catch (IOException e) {

                e.printStackTrace();

            } catch (FileNotFoundException e) {

                e.printStackTrace();

            }

        }

    }

 

5.2 OkHttp 多线程下载的实现:

    

/**

* OkHttp断点续传

*/

public void doDownloadWithOkHttp() {

    InputStream is = null;

    RandomAccessFile savedFie = null;

    File file;

   //1、记录已经下载的文件长度

    long downloadLength = 0;

    String downloadUrl = "www.baidu.com/wenku/o2232.txt";

    String filename = downloadUrl.substring(downloadUrl.lastIndexOf("/"));

    File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

    file = new File(directory + filename);

    if (file.exists()) {

        downloadLength = file.length();

    }

    long contentLength = getContentLength(downloadUrl);

    //2、创建OkHttpClient对象

    OkHttpClient client = new OkHttpClient();

    //3、创建Request对象,通过addHeader加头部信息添加到请求里,表明下载的范围

    Request request = new Request.Builder()

            .addHeader("RANGE", "bytes=" + downloadLength + "-")

            .url(downloadUrl)

            .build();

    //4、开启一个同步请求

    try {

        Response response = client.newCall(request).execute();

        //5、根据Response进行判断

        if (request != null) {

            is = request.body().byteStream();

            savedFie = new RandomAccessFile(file, "rw");

            //6、跳过已经下载的字节

            savedFie.seek(downloadLength);

            byte[] b = new byte[1024];

            int total = 0;

            int len;

            while ((len = is.read()) != -1) {

                total += len;

                savedFie.write(b, 0, len);

                //7、计算已经下载的百分比

                int progress = (int) ((total + downloadLength) * 100 / contentLength);

            }

        }

    } catch (IOException e) {

        e.printStackTrace();

    }

}

 

6、文件上传如何做?原理?OkHttp如何完成文件上传

     6.1 文件上传

 Java中的文件上传 :在UrlConnection中使用post方法 然后在请求头时候添加Http之 Content-Type

指定请求和响应的HTTP内容类型 ,比如:

    Content-Type : multipart / form-data;

     boundary = ———(分割数据)WebkKitFormBoundaryOGKWPJsJCPWjZP

 

     6.2 OkHttp 文件上传的简单操作

public void upLoadFile( String actionUrl ,HashMap paramsMap){

String requestUrl = String.format (“%s/%s”,”upload”,cationUrl);

//0、通过MultipartBody创建文件上传的body体

MultipartBody.Builder builder = new MultipartBody.Builder()

.addPart(Headers.of(

         "Content-Disposition", 

         "form-data; name=\"mFile\"; 

         filename=\"1.txt\""), fileBody)

     .build();

;

    builder.setType(MultipartBody.FORM);

    for (String key : paramsMap.keySet() ){

        Object object =paramsMap.get(key);

            if( ! ( object instanceof File) ){

                builder.addFormDataPart (key, object.toString() );

            } else {

                File file = (File)object ;

                builder .addFormDataPart(key,file.getName(),

                                RequestBody.create(MediaType.parse(application/octet-stream)) );

            }    

        }

    //1、构建RequestBody

    RequestBody body = builder.build();

    //2、构建Request

   Request request = new Request.Builder().url(“…”).post(body).build();

    //3、构建Call

    Call call = client . newBuilder().writeTimeout(60,TimeUnit.xxx);

    //4、构建异步回调

        call.enqueue(new Callback(){

            ...

        });

 

 

7、json数据如何解析?OkHttp如何解析json类型数据

   7.1 json数据的JAVA 解析

json:文本形式的数据交换格式

 

1、传统的JSON解析——— JSObject和JSArray

2、GSON————

3、FastJSON——

 

GSON 的两种解析方式:

/**

** 将json数据解析成list

*/

public void doGson(){

    //构建Gson对象

   Gson gson = new Gson();

    //通过fromJson 实现反序列化

      List list = gson.fromJson(jsonData ,new TypeToken>(){}.getType());

}

 

public void doGson(){

    //1、构建JsonParser 解析对象

 JsonParser parser = new JsonParser();

    //2、通过解析对象 将String类型json数组转化成JsonArray

JsonArray jsonArray  = parser .parse (stringjson). getAsJsonArray():

    //3、构建Gson对象 list对象

    Gson gson = new Gson();

    ArrayList list = new ArrayList<>();

    //4、开始一个for循环 循环遍历jsonArray,获取jsonArray的每一个元素

for ( JsonElement je : jsonArray ){

     T  t1 = gson .fromJson(je , T.class);

        list .add ( t1) ;

    }

}

 

7.2 OkHttp中的json解析

    a、封装一个工具类HttpUtil

       public class HttpUtil {

            public static void sendOkHttpRequest (final String address ,final okhttp3.Callback callback){

                OkHttpClient client = new OkHttpClient();

                Request request = new Request.Builder().url(address).build();

                client.newCall(request).enqueue(callback);

    b、在响应中的调用

        private void sendRequestWithOkHttp(){

            new Thread(new Runnable(){

                @Override

                public void run(){

                    //子线程中执行http请求,并将最终的请求结果回调到Callback中

                    HttpUtil.sendOkHttpRequest( url, new okhttp3.Callback(){

                        @Override

                       public void onResponse(Call call, Response response) throws IOException{

                               String responseData = response.body().string();

                               //解析json数据

                                parseJsonWithGson(responseData);

                                //显示UI界面,通知主线程更新ui

                                showResponse( responseData.toString() );

                           }

                        @Override

                       public void onFailure(Call call ,IOException e){

                           }

                   )};

                  }

            }).start();

    }

private void parseJsonWithGson (String jsonData){

     //使用轻量级的Gson解析得到json

    Gson gson = new Gson();

    List list = gson.fromJson(jsonData ,new TypeToken>(){}.getType());

}

private void showResponse (final String response) {

    //在自线程中通知ui更新

    runOnUiThread (new Runnable() {

        @Override

        public void run(){

           //在此进行UI处理操作

            text .setText ( response);

        }

    )};

}

Lambda表达式的操作样式

    private void showResponse(final String response) {

        runOnUiThread( () -> {

            text.setText (response);

        });

}

 

8、Https协议处理?

 Https协议

    Https是一种基于SSL/TLS的Http协议,是属于应用层协议;

  •         添加SSL/TLS 握手过程
  •         两者数据加密传输

 

所有传输的内容都经过加密(对称加密+不对称加密)

 

对称加密

 是指加密和解密使用的密钥匙同一个密钥,两者可以互相推算。真正传输的数据进行加密

 

不对称加密

    不对称加密和解密使用的密钥不是同一密钥,对外公开其中一个密钥叫公钥。该加密是用于握手阶段的!

 

传送模式:

对称加密所使用的密钥我们可以通过非对称加密的方式发送出去

 

实例:

    一笔交易流程:

1、客户端生成一个随机对称密钥

2、客户端向服务器端请求一个公共密钥 -不对称加密所需要的公钥,给外界用的

3、服务端返回公钥给客户端

4、客户端接收到公钥后,通过该公钥对自己生成的随机对称密钥进行加密

5、将加密过的对称密钥发送给服务端

6、服务端接收该对称密钥后会用自己的私钥对其进行解密

7、进行传输 使用对称加密进行

 

Https 握手过程:

    1、客户端发起Https链接请求获取不对称加密的公钥(客户端支持的加密规则发送给服务端--不对称加密)

    2、服务端接收到请求后,从规则中选出一个不对称加密算法和一个hash算法(验证数据完整性的)

    3、服务端将自己的身份信息以证书的形式返回给客户端 -含有不对称加密的公钥

    4、客户端生成随机数-对称密钥 需要客户端和服务端双方保存

    5、客户端使用不对称加密的公钥对 “随机生成的对称密钥 进行加密”

    6、客户端将加密过的密钥发送给服务端

    7、服务端通过私钥对获取的加密过的密钥进行解密

    8、之后均是: 通过对称密钥加密的密纹通信 

你可能感兴趣的:(总结)