内容简介:目录一、前言二、什么是Socket
目录
一、前言
二、什么是Socket
三、如何使用Socket进行http请求
1、建立socket连接
2、http协议请求和响应格式解析
3、进行http请求
四、写在最后
一、前言
本篇文章是为讲述okhttp源码做一个铺垫,主要是简单讲述一下socket的使用,因为在okhttp中网络通讯使用的便是socket。但这篇文章不会涉及okhttp,会简单阐述下socket,然后用代码进行连接后http通讯,话不多说,开始干!
二、什么是Socket
回答这个问题前我们要先看下TCP/IP四层模型,想必这个图大家都有见过,下面就解释下这四层分别的表现形式是什么(理论解释比较让人摸不着头脑,所以这里以其表现形式来阐述)
- 网络接口层:主要表现为识别mac间比特流的传输
- 网络层:表现为IP协议
- 传输层:表现为TCP、UDP
- 应用层:表现为Http、Https、RTSP等(这里的协议比较多,我们经常使用的http协议就属于应用层)
Tip:顺便说下TCP和UDP的区别。TCP提供可靠的通信传输,类似于打电话,需要等待另一方的接听,才能进行真正的通讯;而UDP则不是可靠的,类似发短信,只将信息发出,至于对方有没收到,这个就不关心了。
而我们关心的socket是什么呢? socket其实是TCP连接的抽象,利用socket进行TCP的连接 (这个解释可能比较片面,但个人觉得是最为直观的解释,毕竟全面的解释比较晦涩难懂)
二、如何使用Socket进行http请求
1、建立socket连接
在 java 中使用socket,其实非常的简单。如果只是需要一个普通的socket,只需通过如下代码,便可以建立一个socket连接
Socket socket = new Socket(“ip或域名”, 端口); 复制代码
如果想建立一个sslSocket,用于https的通讯(例如:www.baidu.com)只需要通过sslSocketFactory进行创建sslSocket即可。代码如下:
Socket socket = SSLSocketFactory.getDefault().createSocket("www.baidu.com", 443); 复制代码
2、http协议请求和响应格式解析
在使用socket进行发起请求前,我们要先来了解下http协议。简单一点的理解,http协议其实就是发起一个按照格式约定的字符串,服务器响应一串按格式组装的数据。 这里不使用教科书式的数据格式,我们使用从"Restlet Client"发起一次请求,观察其请求报文和响应报文来进行讲解。
Tip:Restlet Client是一个api请求工具,日常开发中也可以用来向服务器发起请求,获取数据结构方便调试。可以在chrome的应用商店下载。
这里使用的api是高德的天气预报接口,点击了“send”后,获取到请求报文和响应报文,如下图所示
我们先单独说下这次请求的请求报文(第二个红框中内容,如下所示)
GET /v3/weather/weatherInfo?city=%E9%95%BF%E6%B2%99&key=13cb58f5884f9749287abbead9c658f2 HTTP/1.1 Host: restapi.amap.com 复制代码
(1)第一行为发起请求信息,其格式为:
- 发起的请求形式(这里使用的是GET,如果为POST的话,这里便为POST),即这里的“GET”
- 一个空格,即“ ”
- 请求的路径(不包括域名,因为域名已经在建立socket连接时确定,如果为GET请求,则参数追加在后面以“?”隔开;如果为POST请求,则请求参数会在body中增加,具体见第四小点),即这里的“/v3/weather/weatherInfo?city=%E9%95%BF%E6%B2%99&key=13cb58f5884f9749287abbead9c658f2”
- 一个空格,即“ ”
- http请求的版本,即“HTTP/1.1”
- \r\n,此处没有显示出来,但是自己在组装报文时,需要增加这个表示一行已经结束
(2)第二行的格式为:
- host字段名,即“Host”
- 一个冒号加一个空格,即“: ”(敲黑板!!冒号后面有一个空格,这个在组装请求报文时,尤为重要)
- host的内容,即“restapi.amap.com”
- \r\n,此处没有显示出来,但是自己在组装报文时,需要增加这个表示一行已经结束
tip:这里其实是请求头部,如果头部参数有多个的话,就按照这种格式进行拼装。例如还有一个“Connection为keep-alive”的头部参数,则以“Connection: keep-alive\r\n”的形式写入输出流中,具体会在后面的例子中展示。
(3)第三行的格式为:(没想到吧!!!这里有第三行)
- \r\n,此处没有显示出来,但是自己在组装报文时, 需要增加这个表示头部参数已写完
(4)如果为POST请求,接下来还需要进行body参数的拼装,这里以form表单为例,拼接上面接口的参数。规则就是“键=值”,键值对间用“&”隔开。
city=长沙&key=13cb58f5884f9749287abbead9c658f2 复制代码
至此一个请求报文便拼装完毕,将其用输出流写出即可获得服务器的响应报文。
我们接着说下这次请求的响应报文
HTTP/1.1 200 OK Server: Tengine Date: Sun, 06 May 2018 08:22:10 GMT Content-Type: application/json;charset=UTF-8 Content-Length: 445 Connection: close X-Powered-By: ring/1.0.0 gsid: 010185222147152559493030300162313551811 sc: 0.013 Access-Control-Allow-Origin: * Access-Control-Allow-Methods: * Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,key,x-biz,x-info,platinfo,encr,enginever,gzipped,poiid {"status":"1","count":"2","info":"OK","infocode":"10000","lives":[{"province":"湖南","city":"长沙市","adcode":"430100","weather":"阵雨","temperature":"25","winddirection":"东北","windpower":"7","humidity":"78","reporttime":"2018-05-06 16:00:00"},{"province":"湖南","city":"长沙县","adcode":"430121","weather":"阵雨","temperature":"25","winddirection":"东北","windpower":"7","humidity":"78","reporttime":"2018-05-06 16:00:00"}]} 复制代码
(1)第一行为响应状态,格式为
- http的版本信息,即“HTTP/1.1”
- 一个空格,即“ ”
- 状态码,即“200”
- 一个空格,即“ ”
- 状态,即“OK”
- \r\n,此处没有显示出来,但是解析响应报文时,需要通过这两个字符进行判断是否一行结束
(2)第二行至第十二行为响应头,每一行的格式为
- 头名称,即“Server”
- 一个冒号加一个空格,即“: ”
- 头部参数值,即“Tengine”
- \r\n,此处没有显示出来,但是解析响应报文时,需要通过这两个字符进行判断是否一行结束
(3)第十三行,格式为
- \r\n,用于区分头部参数和内容的区分
(4)第十四行为响应内容,格式为
这里便是接口给我们的数据,即此处给到我们的天气json数据,而此处json的长度为头部中有一个参数为“Content-Length”决定的,例子中内容的长度为445。值得一提的是,有些接口返回的头部参数并没有“Content-Length”这一头部参数,而是返回了“Transfer-Encoding: chunked”这样的头部参数,则表明是以块的形式给到我们数据。 块的形式会以如下格式,第一行的“10\r\n”表明接下来的一行会有10个字节的内容,第二行便是10字节的内容,同样以“\r\n”结束一行(\r\n这两个字符不算在内容长度中),每一块的格式都按这样的形式,如果遇到“0\r\n\r\n”就说明内容结束。
10\r\n //(注意!!!这里是10是16进制,即如果进行内容读取需要将其进行做10进制的转换) 10字节长度的内容\r\n //结束格式 0\r\n \r\n 复制代码
至此响应报文解析完毕。
3、进行http请求
逼逼叨逼逼叨了这么久,很多小伙伴已经很迫不及待的想知道怎么请求和获取响应了。我们这里便直接上代码,代码很简单,并没有什么知识难点。
public class MySocket { public static void main(String[] args) throws IOException { //如果需要进行https的请求只需要换成如下一句(https的默认端口为443,http默认端口为80) //Socket socket = SSLSocketFactory.getDefault().createSocket("xxx", 443); Socket socket = new Socket("restapi.amap.com", 80); //获取输入流,即从服务器获取的数据 final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取输出流,即我们写出给服务器的数据 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //使用一个线程来进行读取服务器的响应 new Thread() { @Override public void run() { while (true) { String line = null; try { while ((line = bufferedReader.readLine()) != null) { System.out.println("recv : " + line); } } catch (IOException e) { e.printStackTrace(); } } } }.start(); bufferedWriter.write("GET /v3/weather/weatherInfo?city=%E9%95%BF%E6%B2%99&key=13cb58f5884f9749287abbead9c658f2 HTTP/1.1\r\n"); bufferedWriter.write("Host: restapi.amap.com\r\n\r\n"); bufferedWriter.flush(); } } 复制代码
跑起来后会看到控制台输出如下信息,这个时候我们就可以按照第二小结中的格式进行解析到一个模型中,最终返回给UI或是逻辑层去使用。
四、写在最后
OkHttp中使用socket连接后,进行处理响应便是这样的处理逻辑。只是它还有对socket的复用,连接进行限制之类的优化处理,这个在后面的文章中会进行剖析。如果您期待这样的剖析之旅的话,给个“:heart:”加个关注吧!文章中并没有对头部参数进行说明其含义,这里也不打算给出,其实百度一下或google都有很多,需要的时候进行搜查一下即可。记住我,我是猛猛的小盆友:smile:,如果我有理解错误或是写的晦涩难懂的地方请与我联系讨论,共同进步。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 详解 HTTP 报文(二):Web 容器是如何解析 HTTP 报文的
- 报文批量处理方法简介
- 发送arp请求报文
- 详解 http 报文
- MQTT协议 -- 消息报文格式
- Wireshark解密TLS报文
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Game Engine Architecture, Second Edition
Jason Gregory / A K Peters/CRC Press / 2014-8-15 / USD 69.95
A 2010 CHOICE outstanding academic title, this updated book covers the theory and practice of game engine software development. It explains practical concepts and techniques used by real game studios,......一起来看看 《Game Engine Architecture, Second Edition》 这本书的介绍吧!