[关闭]
@jimbo 2016-10-17T00:40:07.000000Z 字数 11996 阅读 794

layout: post
title: Http协议
category: 网络
NetWork

description:

学习目标:
1、 了解http协议
2、 了解cookie的作用
3、 使用socket实现http请求
4、 说明OKhttp是如何保存cookie的

一、 http协议

1. 什么是http协议

http协议,即超文本传输协议,是一种基于TCP协议,规定了服务器与浏览器之间相互通信规则的运行于应用层的数据传输协议。之所以被称为超文本传输协议,是因为http协议不仅能够传输文本,还能传输图片,视频等多媒体数据。在某些情况下,http协议还会和TSL/SSL协议一起使用,进行更加安全可靠的数据传输工作,即https

http默认的端口号是80,https默认端口号是443

2. http的工作流程

一个完整的http操作可分成下面四步:

  1. 首先客户端与服务器之间建立链连接。
  2. 建立连接后,客户端向服务器发送一个请求。
  3. 服务器收到请求后给予相应的回应。
  4. 适时关闭客户端与服务端的连接。
3. http请求格式

http请求由三个部分组成:请求行,消息报头,请求正文
具体格式如下:
请求行
消息报头
请求正文

请求行:

以请求方法开头+一个空格+请求的URI+一个空格+协议版本,例如:
GET /login.json HTTP/1.1

http请求方式:

请求报头:

请求报头允许客户端向服务端传递请求的附加信息以及客户端自身信息。

请求正文

客户端可以在请求正文中添加要向服务器提交的数据。当然,这个请求要结合实体报头一起使用,请求与相应都可以添加这个实体头。

4. http响应

响应报文主要由状态行,响应报头和响应报文三个部分组成。

状态行

状态行由三个部分组成:协议版本+一个空格+状态码+一个空格+状态码描述
例如: HTTP/1.1 200 OK

状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:

响应报头
响应报文

服务器将要返回的资源内容存放于此

5. 关于https

简单说,https协议就是一种安全版的http协议。通过TSL/SSL协议,提供客户端与服务器之间安全完整的数据传输能力。

TSL/SSL可以保证一下几点:
1. 认证用户和服务器,确保数据发送到正确的客户机和服务器
2. 加密数据以防止数据中途被窃取
3. 维护数据的完整性,确保数据在传输过程中不被改变

工作过程:

  1. 客户端给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。
  2. 服务器确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。
  3. 客户端确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给鲍勃。
  4. 服务器使用自己的私钥,获取爱丽丝发来的随机数(即Premaster secret)。
  5. 客户端和服务器根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。

通过上面5个过程,可以保证数据发送实在正确的客户端和服务器之间进行的,使用这五个过程得到的加密密钥来加密传输数据,这样能保证传输数据的安全和完整性。

详解可见
数字证书验证正确性

二、cookie的作用

一般有两个作用:


  1. 识别用户身份

    由于http请求是无状态的,服务器无法识别请求是来自于哪一个用户,或者说哪一个客户端,所以就需要一个数据来标识身份。
  2. 记录历史数据

三、使用socket来实现http协议

  1. package test.io;
  2. import java.net.*;
  3. import java.io.*;
  4. /**
  5. * 一个简单的Socket实现的HTTP响应服务器。
  6. * 只用于熟悉HTTP协议的目的,可以看到浏览器发过来的数据格式。
  7. *
  8. * @author 赵学庆 www.java2000.net
  9. */
  10. public class MyWebServer {
  11. public static void main(String[] args) {
  12. Socket socket = null;
  13. try {
  14. // 创建一个监听8000端口的服务器Socket
  15. ServerSocket s = new ServerSocket(8000, 3);
  16. while (true) {
  17. socket = s.accept();
  18. new MyWebServerThread(socket).start();
  19. }
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. class MyWebServerThread extends Thread {
  26. private Socket socket;
  27. MyWebServerThread(Socket socket) {
  28. this.socket = socket;
  29. }
  30. @Override
  31. public void run() {
  32. try {
  33. InputStreamReader is = new InputStreamReader(socket.getInputStream());
  34. char[] bs = new char[2048];
  35. PrintStream out;
  36. out = new PrintStream(socket.getOutputStream());
  37. StringBuilder msg = new StringBuilder();
  38. // 如果10毫秒还没有数据,则视同没有新的数据了。
  39. // 因为有Keep-Alive的缘故,浏览器可能不主动断开连接的。
  40. // 实际应用,会根据协议第一行是GET还是 POST确定。
  41. socket.setSoTimeout(10);
  42. // 此处读入请求数据并做相应的处理
  43. //
  44. int len = -1;
  45. try {
  46. while ((len = is.read(bs)) != -1) {
  47. msg.append(bs, 0, len);
  48. msg.append("\n");
  49. }
  50. } catch (Exception ex) {
  51. // ex.printStackTrace();
  52. }
  53. // 下面是由服务器直接生成的主页内容
  54. // 1、首先向浏览器输出响应头信息
  55. out.println("HTTP/1.1 200 OK");
  56. out.println("Content-Type:text/html;charset:GBK");
  57. out.println();
  58. // 2、输出主页信息
  59. out
  60. .println(""
  61. + "
  62. "
  63. + "
  64. HTTP协议测试服务器,当前时间:"
  65. + new java.util.Date()
  66. + ""
  67. + "username:password:
  68. "
  69. + "username:password:
  70. "
  71. + "phototitle:photo:"
  72. + "您提交的数据如下:
  73. " + msg.toString() + "
  74. ");
  75. out.flush();
  76. out.close();
  77. is.close();
  78. System.out.println("close");
  79. // 关闭连接
  80. socket.close();
  81. } catch (IOException e) {
  82. e.printStackTrace();
  83. }
  84. }
  85. }
  1. class Http {
  2. HttpLine httpLine;
  3. HttpHead httpHead;
  4. HttpContent httpContent;
  5. }

四、OkHTTP是如何保存cookie的

在okhttp3.0中,构建request的时候可以看到如下代码private Request networkRequest(Request request) throws IOException {
Request.Builder result = request.newBuilder();

//例行省略....

List<Cookie> cookies = client.cookieJar().loadForRequest(request.url());
if (!cookies.isEmpty()) {
  result.header("Cookie", cookieHeader(cookies));
}

//例行省略....

return result.build();

}

  1. private Request networkRequest(Request request) throws IOException {
  2. Request.Builder result = request.newBuilder();
  3. //例行省略....
  4. List<Cookie> cookies = client.cookieJar().loadForRequest(request.url());
  5. if (!cookies.isEmpty()) {
  6. result.header("Cookie", cookieHeader(cookies));
  7. }
  8. //例行省略....
  9. return result.build();
  10. }
  11. private String cookieHeader(List<Cookie> cookies) {
  12. StringBuilder cookieHeader = new StringBuilder();
  13. for (int i = 0, size = cookies.size(); i < size; i++) {
  14. if (i > 0) {
  15. cookieHeader.append("; ");
  16. }
  17. Cookie cookie = cookies.get(i);
  18. cookieHeader.append(cookie.name()).append('=').append(cookie.value());
  19. }
  20. return cookieHeader.toString();
  21. }

在收到response之后还会执行如下代码:

  1. public void receiveHeaders(Headers headers) throws IOException {
  2. if (client.cookieJar() == CookieJar.NO_COOKIES) return;
  3. List<Cookie> cookies = Cookie.parseAll(userRequest.url(), headers);
  4. if (cookies.isEmpty()) return;
  5. client.cookieJar().saveFromResponse(userRequest.url(), cookies);
  6. }

而这cookjar是一个接口:

  1. public interface CookieJar {
  2. /** A cookie jar that never accepts any cookies. */
  3. CookieJar NO_COOKIES = new CookieJar() {
  4. @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
  5. }
  6. @Override public List<Cookie> loadForRequest(HttpUrl url) {
  7. return Collections.emptyList();
  8. }
  9. };
  10. /**
  11. * Saves {@code cookies} from an HTTP response to this store according to this jar's policy.
  12. *
  13. * <p>Note that this method may be called a second time for a single HTTP response if the response
  14. * includes a trailer. For this obscure HTTP feature, {@code cookies} contains only the trailer's
  15. * cookies.
  16. */
  17. void saveFromResponse(HttpUrl url, List<Cookie> cookies);
  18. /**
  19. * Load cookies from the jar for an HTTP request to {@code url}. This method returns a possibly
  20. * empty list of cookies for the network request.
  21. *
  22. * <p>Simple implementations will return the accepted cookies that have not yet expired and that
  23. * {@linkplain Cookie#matches match} {@code url}.
  24. */
  25. List<Cookie> loadForRequest(HttpUrl url);
  26. }

所以,我们在生成okhttpclient的时候添加一个实现该接口的类后就可以很轻松的管理cookie了。

  1. OkHttpClient client = new OkHttpClient.Builder()
  2. .cookieJar(new CookieJar() {
  3. private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
  4. @Override
  5. public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
  6. cookieStore.put(url, cookies);
  7. }
  8. @Override
  9. public List<Cookie> loadForRequest(HttpUrl url) {
  10. List<Cookie> cookies = cookieStore.get(url);
  11. return cookies != null ? cookies : new ArrayList<Cookie>();
  12. }
  13. })
  14. .build();

序列化cookie设计SerializableOkHttpCookies,PersistentCookieStore这两个类.

SerializableOkHttpCookies

主要做两件事:

  1. public class SerializableOkHttpCookies implements Serializable {
  2. private transient final Cookie cookies;
  3. private transient Cookie clientCookies;
  4. public SerializableOkHttpCookies(Cookie cookies) {
  5. this.cookies = cookies;
  6. }
  7. public Cookie getCookies() {
  8. Cookie bestCookies = cookies;
  9. if (clientCookies != null) {
  10. bestCookies = clientCookies;
  11. }
  12. return bestCookies;
  13. }
  14. private void writeObject(ObjectOutputStream out) throws IOException {
  15. out.writeObject(cookies.name());
  16. out.writeObject(cookies.value());
  17. out.writeLong(cookies.expiresAt());
  18. out.writeObject(cookies.domain());
  19. out.writeObject(cookies.path());
  20. out.writeBoolean(cookies.secure());
  21. out.writeBoolean(cookies.httpOnly());
  22. out.writeBoolean(cookies.hostOnly());
  23. out.writeBoolean(cookies.persistent());
  24. }
  25. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  26. String name = (String) in.readObject();
  27. String value = (String) in.readObject();
  28. long expiresAt = in.readLong();
  29. String domain = (String) in.readObject();
  30. String path = (String) in.readObject();
  31. boolean secure = in.readBoolean();
  32. boolean httpOnly = in.readBoolean();
  33. boolean hostOnly = in.readBoolean();
  34. boolean persistent = in.readBoolean();
  35. Cookie.Builder builder = new Cookie.Builder();
  36. builder = builder.name(name);
  37. builder = builder.value(value);
  38. builder = builder.expiresAt(expiresAt);
  39. builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
  40. builder = builder.path(path);
  41. builder = secure ? builder.secure() : builder;
  42. builder = httpOnly ? builder.httpOnly() : builder;
  43. clientCookies =builder.build();
  44. }
  45. }

PersistentCookieStore

封装管理cookie的方法

  1. public class PersistentCookieStore {
  2. private static final String LOG_TAG = "PersistentCookieStore";
  3. private static final String COOKIE_PREFS = "Cookies_Prefs";
  4. private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
  5. private final SharedPreferences cookiePrefs;
  6. public PersistentCookieStore(Context context) {
  7. cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
  8. cookies = new HashMap<>();
  9. //将持久化的cookies缓存到内存中 即map cookies
  10. Map<String, ?> prefsMap = cookiePrefs.getAll();
  11. for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
  12. String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
  13. for (String name : cookieNames) {
  14. String encodedCookie = cookiePrefs.getString(name, null);
  15. if (encodedCookie != null) {
  16. Cookie decodedCookie = decodeCookie(encodedCookie);
  17. if (decodedCookie != null) {
  18. if (!cookies.containsKey(entry.getKey())) {
  19. cookies.put(entry.getKey(), new ConcurrentHashMap<String, Cookie>());
  20. }
  21. cookies.get(entry.getKey()).put(name, decodedCookie);
  22. }
  23. }
  24. }
  25. }
  26. }
  27. protected String getCookieToken(Cookie cookie) {
  28. return cookie.name() + "@" + cookie.domain();
  29. }
  30. public void add(HttpUrl url, Cookie cookie) {
  31. String name = getCookieToken(cookie);
  32. //将cookies缓存到内存中 如果缓存过期 就重置此cookie
  33. if (!cookie.persistent()) {
  34. if (!cookies.containsKey(url.host())) {
  35. cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());
  36. }
  37. cookies.get(url.host()).put(name, cookie);
  38. } else {
  39. if (cookies.containsKey(url.host())) {
  40. cookies.get(url.host()).remove(name);
  41. }
  42. }
  43. //讲cookies持久化到本地
  44. SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
  45. prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
  46. prefsWriter.putString(name, encodeCookie(new SerializableOkHttpCookies(cookie)));
  47. prefsWriter.apply();
  48. }
  49. public List<Cookie> get(HttpUrl url) {
  50. ArrayList<Cookie> ret = new ArrayList<>();
  51. if (cookies.containsKey(url.host()))
  52. ret.addAll(cookies.get(url.host()).values());
  53. return ret;
  54. }
  55. public boolean removeAll() {
  56. SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
  57. prefsWriter.clear();
  58. prefsWriter.apply();
  59. cookies.clear();
  60. return true;
  61. }
  62. public boolean remove(HttpUrl url, Cookie cookie) {
  63. String name = getCookieToken(cookie);
  64. if (cookies.containsKey(url.host()) && cookies.get(url.host()).containsKey(name)) {
  65. cookies.get(url.host()).remove(name);
  66. SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
  67. if (cookiePrefs.contains(name)) {
  68. prefsWriter.remove(name);
  69. }
  70. prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
  71. prefsWriter.apply();
  72. return true;
  73. } else {
  74. return false;
  75. }
  76. }
  77. public List<Cookie> getCookies() {
  78. ArrayList<Cookie> ret = new ArrayList<>();
  79. for (String key : cookies.keySet())
  80. ret.addAll(cookies.get(key).values());
  81. return ret;
  82. }
  83. /**
  84. * cookies 序列化成 string
  85. *
  86. * @param cookie 要序列化的cookie
  87. * @return 序列化之后的string
  88. */
  89. protected String encodeCookie(SerializableOkHttpCookies cookie) {
  90. if (cookie == null)
  91. return null;
  92. ByteArrayOutputStream os = new ByteArrayOutputStream();
  93. try {
  94. ObjectOutputStream outputStream = new ObjectOutputStream(os);
  95. outputStream.writeObject(cookie);
  96. } catch (IOException e) {
  97. Log.d(LOG_TAG, "IOException in encodeCookie", e);
  98. return null;
  99. }
  100. return byteArrayToHexString(os.toByteArray());
  101. }
  102. /**
  103. * 将字符串反序列化成cookies
  104. *
  105. * @param cookieString cookies string
  106. * @return cookie object
  107. */
  108. protected Cookie decodeCookie(String cookieString) {
  109. byte[] bytes = hexStringToByteArray(cookieString);
  110. ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
  111. Cookie cookie = null;
  112. try {
  113. ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
  114. cookie = ((SerializableOkHttpCookies) objectInputStream.readObject()).getCookies();
  115. } catch (IOException e) {
  116. Log.d(LOG_TAG, "IOException in decodeCookie", e);
  117. } catch (ClassNotFoundException e) {
  118. Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
  119. }
  120. return cookie;
  121. }
  122. /**
  123. * 二进制数组转十六进制字符串
  124. *
  125. * @param bytes byte array to be converted
  126. * @return string containing hex values
  127. */
  128. protected String byteArrayToHexString(byte[] bytes) {
  129. StringBuilder sb = new StringBuilder(bytes.length * 2);
  130. for (byte element : bytes) {
  131. int v = element & 0xff;
  132. if (v < 16) {
  133. sb.append('0');
  134. }
  135. sb.append(Integer.toHexString(v));
  136. }
  137. return sb.toString().toUpperCase(Locale.US);
  138. }
  139. /**
  140. * 十六进制字符串转二进制数组
  141. *
  142. * @param hexString string of hex-encoded values
  143. * @return decoded byte array
  144. */
  145. protected byte[] hexStringToByteArray(String hexString) {
  146. int len = hexString.length();
  147. byte[] data = new byte[len / 2];
  148. for (int i = 0; i < len; i += 2) {
  149. data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
  150. }
  151. return data;
  152. }
  153. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注