android 网络一般使用的都是 http 或 https,使用的网络框架一般为 volley 或者 retrofit,而他们封装的是 HttpURLConnection、HttpClient 或 okhttp,现在基本是 Retrofit + Okhttp 的模式,一般会配合RxJava一起使用。我们知道,http协议是基于TCP/IP协议而成的,TCP又是依赖于socket的,平常所说的TCP三次握手和四次挥手,也是这里用到的。说到TCP,那么还有TUP,这两个的区别一个稳定,一个不稳定;一个速度慢,一个速度快;比如 http 这种接口是基于TCP,而直播流等一般用的是TUP,具体区别可以网上搜下。
Socket 最早学的时候,用两个类分别表示服务端和客户端,一般是服务端先运行,然后是客户端,通过管道流进行数据交互,聊天室就是根据这个原理进行开发的
Service:
public static void main(String[] args) throws IOException {
//1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
ServerSocket serverSocket = new ServerSocket(123);
InetAddress address = InetAddress.getLocalHost();
String ip = address.getHostAddress();
System.out.println("~~~服务端已就绪,等待客户端的链接~,服务端ip地址: " + ip);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream(); //获取输入流
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine()) != null) {//循环读取客户端的信息
System.out.println("客户端发送过来了信息 " + info);
}
socket.shutdownInput();//关闭输入流
socket.close();
}
Client:
public static void main(String[] args) throws IOException {
InetAddress address = InetAddress.getLocalHost();
String ip = address.getHostAddress();
Socket socket = new Socket(ip, 123);
OutputStream os = socket.getOutputStream();//字节输出流
PrintWriter pw = new PrintWriter(os);//将输出流包装为打印流
pw.write("客户端:~" + ip + "~ 链接服务器!!");
pw.flush();
socket.shutdownOutput();//关闭输出流
socket.close();
}
运行后,会打印
~~~服务端已就绪,等待客户端接入~,服务端ip地址: 172.18.18.221
客户端发送过来的信息 客户端:~172.18.18.221~ 接入服务器!!
上面的例子很简单,仅仅是用socket简单的通讯。那 HTTP 呢,Okhttp也是直接依赖socket来通讯的,它们是怎么封装的?http 分为请求和返回两部分,分别包括请求行、请求头、请求体 以及 响应行、响应头、响应体。
HttpURLConnection 或 okhttp 都是封装好了socket,如果我们自己通过封装socket进行http请求呢?我们可以自己把请求头需要的信息给拼接起来
private static void socketHttp() {
String dd = "GET http://www.baidu.com HTTP/1.1" +
"\r\n" +
"Host: www.baidu.com" +
"\r\n" +
"User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:23.0) Gecko/20100101 Firefox/23.0" +
"\r\n" +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" +
"\r\n" +
"Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3" +
"\r\n" +
"Accept-Encoding: gzip, deflate" +
"\r\n" +
"Connection: keep-alive" +
"\r\n" +
"\r\n";
try {
Socket socket = new Socket(InetAddress.getByName("www.baidu.com"),80);
OutputStream os = socket.getOutputStream();
os.write(dd.getBytes());
InputStream is = socket.getInputStream();
int count = 0;
byte[] b = new byte[1024];
while((count = is.read(b))!=-1) {
String ss = new String(b, 0, count, "UTF-8");
System.out.println(ss);
}
} catch (IOException e) {
e.printStackTrace();
}
}
dd 这个字符串封装了请求所需要的信息,然后通过流把它传入到socket中,最终从服务端接收到数据,看下打印的值
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=1
Content-Encoding: gzip
Content-Length: 3371
Content-Type: text/html
Date: Mon, 26 Apr 2021 06:41:48 GMT
Etag: "1cd6-5480030886bc0"
Expires: Mon, 26 Apr 2021 06:41:49 GMT
Last-Modified: Wed, 08 Feb 2017 07:55:35 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Server: Apache
Set-Cookie: BAIDUID=A4FBAFE3170052D28CEBD2A8A41C52AD:FG=1; expires=Tue, 26-Apr-22 06:41:48 GMT; max-age=31536000; path=/; domain=.baidu.com;
version=1
Set-Cookie: BAIDUID=A4FBAFE3170052D213C84A206C935E68:FG=1; expires=Tue, 26-Apr-22 06:41:48 GMT; max-age=31536000; path=/; domain=.baidu.com;
version=1
Vary: Accept-Encoding,User-Agent
ksjfkdjakjfdkjskjfkjdskj(乱码)
返回的内容,最后几行是乱码,就没有多截取,这里看返回的前几行,它就是响应行和响应头,返回报文的第一行是响应行,包含 服务器HTTP协议的版本、响应状态代码及代码的文本描述;第二行到下面的乱码之间是相应头,也叫消息报头,可以截取出来,让后根据冒号拆分成一个个属性;最下面是相应体,也就是相应正文。
网络通讯的核心很简单,但是如果做成一个三方库,那需要考虑的事情就多了,牵涉各种细节及容错,学习三方库抓住核心及重点就可以了,没必要面面俱到。