我非常怀疑科普文章的质量
大多数介绍这仨关系的文章都是理论一大堆,看完就记住理论了并没有实际理解,此篇绕过理论,就讲实际应用过程
tcp和http是协议!socket是套接字,是接口
怎么样来理解?先说协议:
协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流;其实协议就是一个约定,比如赤壁之战前夕,周瑜晃点(就是欺骗的意思)刘备,诱请刘备过江,想借机杀之,因关羽跟随左右,未敢动手。期间和手下约定好摔杯为号,这个摔杯就是周瑜和手下事先定好的协议; 回到现实中来,在网购异常发达的今天,只要是地址写的正确,就算天涯海角也能把货物给你送到;这个地址包括收件人的地址和手机号,这个地址就是一个协议,不管是哪个快递员只要看到这个地址就能把货物送到目的地;
TCP和http的关系: TCP和http都是协议,http协议的内容包含了tcp的内容,http是一份更详细的协议,定义的内容要多一些;就是你在填写地址的时候,你可以填写从省、市、县、区、镇、村、屯;这个详细地址就是http协议级别。但是你想想,汽车飞机在运送你的快递的时候,它们不需要了解那么详细的信息,飞机汽车只需要把你的快递从一个城市的快递站运送到另一个快递站就可以了,等货物到快递站,再由快递小哥根据详细地址送到你的手里,端口号就相当于手机号,一个程序到一台设备后,得发送到对应的端口号,这样你的数据就接收到了;快递也一样,快递员打电话给你的时刻,货物就送到你的手里了,至于你叫什么其实无所谓,叫什么你都收到货物了,再对一些身份证信息是否正确,应该就属于TSL协议了属于会话层协议,不是重点可以忽略
什么是socket套接字? 说白了就是一个接口,你想实现网络通信的必须依赖socket,而且你发起任何一次http请求,都是一次socket;看看创建一个socket对象需要什么字段
public Socket(String host, int port) throws UnknownHostException, IOException {
this(InetAddress.getAllByName(host), port, (SocketAddress) null, true);
}
需要端口号,和ip就可以了,写一个socket请求
public class Client {
public static void main(String[] args) {
String msg = "Client data";
try {
Socket socket = new Socket("192.168.6.42",8080);
PrintWriter pw = new PrintWriter(socket.getOutputStream());
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw.println(msg);
pw.flush();
String line = is.readLine();
System.out.println("received from server" + line);
pw.close();
is.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们看到代码里创建socket 对象的时候只需要ip和端口就可以了,http:// 消失不见了,还比如android里面常用的okhttp和HttpURLConnection类里面,底层肯定都是用socket连接,翻了一些okhttp3的代码在RealConnection中,找到这里很明显的:Proxy.Type.HTTP ? address.socketFactory().createSocket() : new Socket(proxy); 这里就是创建socket 的代码,也就说你在开发过程中创建的http请求,实际上只是一个socket,根本不需要http://,socket需要的是什么?端口号和ip而已
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
try {
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
} catch (NullPointerException npe) {
if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
throw new IOException(npe);
}
}
}
HttpURLConnection 肯定也是这样,懒得翻代码了,有兴趣的小伙伴自己去看吧
这里就打个比喻: 当你想发快递,发一件物品的时候,需要什么?地址和手机号,在面向对象编程中,我们可以定义一个java类,包含两个属性,地址和手机号,写成java类可以定义成这样:
public class TargetPerson {
String receiveAddress;
String receivePhoneNum;
String sendAddress;
String sendPhoneNum;
}
这个TargetPerson类就可以叫 快递界的套接字,或者快递界的socket
当然socket当中发送者的ip和端口号是自动获取的, 不像我们填写快递地址的时候要把发送人的地址都写上才行
看到这里我想你还是懵懵懂懂,继续详细介绍,看完绝对你会有收获
HTTP和TCP详细协议介绍
我们先来写一个socketServer,打印一下http一个完整请求 SocketServer代码:
public class HttpServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
while (true) {
Socket socket = ss.accept();
BufferedReader bd = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String requestHeader;
int contentLength = 0;
while ((requestHeader = bd.readLine()) != null && !requestHeader.isEmpty()) {
System.out.println(requestHeader);
if (requestHeader.startsWith("GET")) {
int begin = requestHeader.indexOf("/?") + 2;
int end = requestHeader.indexOf("HTTP/");
String condition = requestHeader.substring(begin, end);
}
if (requestHeader.startsWith("Content-Length")) {
int begin = requestHeader.indexOf("Content-Lengh:") + "Content-Length:".length();
String postParamterLength = requestHeader.substring(begin).trim();
contentLength = Integer.parseInt(postParamterLength);
}
}
StringBuffer sb = new StringBuffer();
if (contentLength > 0) {
for (int i = 0; i < contentLength; i++) {
sb.append((char) bd.read());
}
}
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println("HTTP/1.1 200 OK");
pw.println("Content-type:text/html");
pw.println();
pw.println("<h1>successful</h1>");
pw.flush();
socket.close();
}
}
}
运行之后,只要在自己的电脑打开浏览器输入http://localhost:8888/浏览器就会显示
当然在发送回执的时候,你可以多写点内容,不过无所谓啦 在代码里接受到的HTTP请求是这样! 下面是http协议的格式,其中回车符和换行符没有打印出来,已经实际换行了,而且在回执的代码种有写pw.println里的ln就表示换行了 大概知道http的格式是这样就可以了,我写的是socketServer但是接收到的数据确是http格式的,我们就可以根据http格式,来截取需要的数据,再创建HttpResponse对象,对象里定义head、body、method等等,然后再截取socket打印的数据,把数据都添加到HttpResponse里面去,这样我们开发的时候就拿到了一个httpResponse对象,OKHTTP3网络请求库中肯定也有这个过程,懒得找了,感兴趣小伙伴自己找一下吧,加深理解,OKHTTP3中叫 Response 类,看一下他的创建过程就知道了
下面来介绍TCP协议: TCP的格式是这样的;这才是发送的数据在网线中传输时候的真实样子,你上层写的http格式的协议,在底层传输的时候其实是TCP格式的协议;那么这个过程是谁转换的呢?什么时候转换的呢?干这个事儿的就是socket 了
这个图我想大家都不陌生,只要看过类似文章肯定都会把这图晒出来 我们在写java代码的时候,想做网络请求,只能创建socket对象,通过socket输连接数据,就算你用各种网络请求工具,底层肯定也绕不开socket;无法直接创建tcp对象和连接,也压根没有tcp连接,因为tcp只是一个协议,我们创建socket后,是操作系统帮我们把socket对象转换成tcp/ip,然后再发送到网络中去;
就像我前面比喻的,tcp是飞机和汽车,是连接各个城市的,网络中他就是连接到每台电脑的,具体是哪两个设备上的具体哪两个应用发送的数据TCP不需要关注。我们写的具体地址中,肯定有城市之间的信息。就像http协议中,肯定包含了tcp协议需要的信息;
简单解释:普通程序员创建http请求,这个http请求类是高级程序员根据socket对象封装好的。高级程序员把普通程序员创建的http请求对象转换成socket对象,socket对象把自己扔给操作系统,操作系统把socket对象转换成tcp/ip需要的数据,进行网络传输;socket把自己扔给操作系统的过程可能就是每种语言底层socket的具体实现了,有兴趣的小伙伴自己查阅,这部分我还没找到代码
也就是说我们开发过程中,只能写http请求和socket请求,没法直接创建tcp对象,是操作系统帮我们处理的socket对象(我理解的是这样,可能有误)
如何创建tcp/udp连接?
想创建TCP连接就创建Socket socket = new Socket(); 这个对象 想创建UDP连接就创建DatagramPacket packet = new DatagramPacket();这个DatagramPacket 也是广义上的socket,只不过是基于udp的socket
Socket类帮我们封装好了tcp的三次握手过程,只要你创建了一个socket对象开启连接成功,就表示已经三次握手完毕,这是一个可用的tcp连接 同样的DatagramPacket类就是UDP连接,里面就不包含三次握手和四次挥手,同样都是底层帮我们封装好了,起码java程序员不需要操心这个 具体TCP和UDP的区别,去看别的文章吧有的是,这里不做介绍,本篇重点帮你理解tcp、http、socket的关系
最后总结:
1、tcp和http都是协议,只是针对不同的对象,http是两个用户进程之间,tcp是两个设备终端之间 2、高级开发创建http协议相关对象,比如okHttpClient、Request、Response类,实际底层都是socket连接,把Request对象里的http协议数据转换成字符串,发送出去,再把socket接收到的字符串转换成Response类,发送给普通开发者 3、普通开发根据okHttpClient、Request、Response这些类做自己的业务,只关注http请求协议就可以;比如http协议请求头里的get、post方法,普通开发根据post还是get来判断数据是在url中还是表单中; 4、对于TCP来讲,我只传输数据,任何数据内容对我来说都是没有意义的,对我来说都是0和1;当一台设备的操作系统接收到TCP数据之后,把数据转换成socket对象,再根据端口号把socket对象发送给具体的进程,这个进程拿到socket对象后,把数据按照http协议格式解析(这个解析过程就是第二条高级开发干的事),创建成Response返回给普通开发,普通开发就根据接收到的http协议再做具体的业务; 5、普通开发是没法直接写TCP连接的,最底层的只能写socket对象,这个对象就是进程和操作系统之间的桥梁,网络请求都是操作系统发起的,你想让操作系统发起一个网络请求(或者说你想发起一个网络请求),就通过创建socket对象开始。然后操作系统把socket对象里的数据拿出来,填入到TCP协议中去,然后把TCP协议发送到网络中去…
还有哪里不懂欢迎留言,我做解释
socket的使用肯定是要复杂些的,涉及到IO流的读写操作,这里也不做介绍了,自己看吧
|