一个完全的HTTP要求的全过程
此举例说明为开诚布公,正确引导大伙儿进到思索情况。
如果你按键入www.baidu.com ,浏览器接受到这一新闻以后,浏览器依据自身的优化算法鉴别出你需要浏览的URL,为您展现出去检索网页页面和广告宣传,那麼这种经历了什么全过程呢?
大概全过程如下所示:
- (1)浏览器查看 DNS,获得网站域名相对应的IP地址; 实际全过程包含浏览器检索本身的DNS缓存文件、检索电脑操作系统的DNS缓存文件、载入当地的Host文档和向当地DNS服 务端开展查看等。
- (2)浏览器得到网站域名相对应的IP地址之后,浏览器向网络服务器要求创建连接,进行三次握手;
- (3)TCP/IP连接创建下去后,浏览器向服务器发送HTTP要求;
- (4)网络服务器接受到这一要求,并依据途径主要参数投射到指定的要求CPU开展解决,并将处置结果及对应的主视图回到给浏览器;
- (5)浏览器分析并3D渲染主视图,若碰到对js文件、css文件及图片等静态数据网络资源的引入,则反复以上流程并向网络服务器要求这种网络资源;
- (6)浏览器依据其要求到的网络资源、数据信息3D渲染网页页面,最后向客户展现一个完全的网页页面。
下边,大家从底到上去一层层了解这个问题。
互联网参考模型
开放式系统互连通讯参考模型(英文:Open System Interconnection Reference Model,简称:OSI;通称为OSI模型)是一种数据模型,由国际海事组织明确提出,一个尝试使各种各样电子计算机在全世界范畴内互联为互联网的规范架构。界定于ISO/IEC 7498-1。(节选自wiki百科)
7 | 应用层 application layer | 例如HTTP、SMTP、SNMP、FTP、Telnet、SIP、SSH、NFS、RTSP、XMPP、Whois、ENRP、TLS |
6 | 表示层 presentation layer | 例如XDR、ASN.1、SMB、AFP、NCP |
5 | 会话层 session layer | 例如ASAP、ISO 8327 / CCITT X.225、RPC、NetBIOS、ASP、IGMP、Winsock、BSD sockets |
4 | 网络层 transport layer | 例如TCP、UDP、RTP、SCTP、SPX、ATP、IL |
3 | 链路层 network layer | 例如IP、ICMP、IPX、BGP、OSPF、RIP、IGRP、EIGRP、ARP、RARP、X.25 |
2 | 数据链路层 data link layer | 例如以太网接口、令牌环、HDLC、帧中继、ISDN、ATM、IEEE 802.11、FDDI、PPP |
1 | mac层 physical layer | 例如路线、无线、金属 |
通常我们觉得OSI模型的最上边三层(应用层、表示层和会话层)在TCP/IP组里是一个应用层。
因为TCP/IP有一个相对性较差的会话层,由TCP和RTP下的开启和关掉联接构成,而且在TCP和UDP下的各类运用给予不一样的端口,这种作用可以被单独的应用软件(或是这些应用软件所采用的库)提升。与此类似的是,IP是依照将它下边的互联网作为一个黑盒子的思维设计方案的,那样在探讨TCP/IP的过程中就可以把它当做一个单独的层。
TCP/IP 参考模型
4 | 应用层 application layer | 例如HTTP、FTP、DNS (如BGP和RIP那样的路由器协议,虽然因为各种的缘故他们各自运作在TCP和UDP上,依然可以将他们当作链路层的一部分) |
3 | 网络层 transport layer | 例如TCP、UDP、RTP、SCTP (如OSPF那样的路由器协议,虽然运作在IP上还可以看做是链路层的一部分) |
2 | 互联网互联层 internet layer | 针对TCP/IP而言这也是互联网协议(IP) (如ICMP和IGMP那样的务必协议虽然运作在IP上,也依然可以看成是互联网互联层的一部分;ARP不运作在IP上) |
1 | 网站访问(连接)层 Network Access(link) layer | 例如以太网接口、Wi-Fi、MPLS等。 |
下边一张图更有利于你的了解
HTTP 协议与 TCP/IP 协议
**HTTP 是 TCP/IP 参考模型中应用层的在其中一种完成。**HTTP 协议的链路层根据 IP 协议,网络层根据 TCP 协议:HTTP 协议是基于 TCP/IP 协议的应用层协议。
TCP/IP 协议必须向程序猿给予可编的 API,该 API 便是 Socket,它是对 TCP/IP 协议的一个主要的完成,几乎任何的计算机软件都保证了对 TCP/IP 协议族的 Socket 完成。
Socket是过程通信的一种方法,即启用这一互联网库的一些API函数完成遍布在不一样服务器的有关过程间的数据传输。
- 流格式套接字(SOCK_STREAM) 流格式套接字(Stream Sockets)也叫“面对连结的套接字”,它根据 TCP 协议,在源代码中应用 SOCK_STREAM 表明。
- 数据信息报格式套接字(SOCK_DGRAM) 数据报格式套接字(Datagram Sockets)也叫“无衔接的套接字”,根据 UDP 协议,在源代码中应用 SOCK_DGRAM 表明。 TCP与UDP 协议差别与优势与劣势 TCP 是面对连结的传送协议,创建联接时要通过三次握手,断开时要通过四次握手,正中间传送数据时也需要回应 ACK 包确定,多种多样体制保障了数据信息可以恰当抵达,不容易遗失或出差错。 UDP 是是非非联接的传送协议,沒有创建联接和断开的全过程,它仅仅简易地把数据信息丢到互联网中,也不用 ACK 包确定。 假如只考虑到稳定性,TCP 确实比 UDP 好。但 UDP 结构类型比 TCP 更为简约,不容易推送 ACK 的回复信息, 都不 会给数据分派 Seq 编号,因此 UDP 的传递高效率有时候会比 TCP 高于许多,程序编写中完成 UDP 也比 TCP 简易。 与 UDP 对比,TCP 的生命力取决于流控制,这确保了传输数据的准确性。
最终要表明的是:TCP 的速率没法超过 UDP,但在收取和发送一些种类的数据资料时有可能贴近 UDP。例如,每一次互换的信息量越大,TCP 的传输速度就越贴近于 UDP。
共享大量互联网最底层基本原理知识要点,內容包含Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体播放,CDN,P2P,K8S,Docker,TCP/IP,协同程序,DPDK这些。后台管理私聊【构架】获得
TCP/IP 协议、HTTP 协议和 Socket 有什么不同?
从包括范畴看来,他们的传承关联是如此的:
从纵向看来,他们的传承关联是如此的:
有关TCP/IP和HTTP协议的关联,有一段很容易了解的详细介绍:
我们在传送数据时,可以只应用(网络层)TCP/IP协议,可是这样的话,要是没有应用层,便无法识别数据信息內容,假如要想使传送的数据信息更有意义,则需要应用到应用层协议,应用层协议有很多,例如HTTP、FTP、TELNET等,还可以自身界定应用层协议。WEB应用HTTP协议作应用层协议,以封装形式HTTP文字信息内容,随后应用TCP/IP做网络层协议将它发至互联网上。
Socket是什么呢,事实上Socket是对TCP/IP协议的封装形式,Socket自身并并不是协议,反而是一个启用插口(API),根据Socket,大家能够应用TCP/IP协议。
TCP/IP仅仅一个协议栈,如同电脑操作系统的运行机制一样,务必要实际完成,与此同时也要给予对外开放的操作插口。这一如同电脑操作系统会带来规范的程序编程插口,例如win32程序编写插口一样,TCP/IP也需要给予可供程序猿做网络开发常用的插口,这就是Socket程序编写插口。”
TCP/IP 和 HTTP 的数据结构
HTTP 做为 TCP/IP 参考模型的网络层,把 HTTP 放进 TCP/IP 参考模型中,他们的承继构造是如此的:
在 TCP/IP 参考模型中他们的总体的数据结构是:IP 做为以太网接口的立即最底层,IP 的顶部和数据信息合下去做为以太网接口的数据信息,一样的 TCP/UDP 的顶部和数据信息合下去做为 IP 的数据信息,HTTP 的顶部和数据信息合下去做为 TCP/UDP 的数据信息。
IP 的数据结构和互动步骤
众所周知在一个取得成功的 HTTP 请求中,服务端可以在一个请求中获得到客户端 IP 详细地址,还可以获得到客户端请求的服务器的 IP 详细地址。殊不知这也是如何保证的呢?这就有赖于 IP 协议书了,在 IP 协议中要求了,IP 的脑袋需要包括源 IP 详细地址和目地 IP 详细地址,这也是为啥 TCP/IP 参考模型中IP 处于互联网互连层,在其中一个因素是可以精准定位服务端详细地址和客户端地址,大家来说一下 IP 的数据结构:
可以很清楚的见到源 IP 详细地址和目地 IP 详细地址,在 IP 的头顶部各占 32 位,而 IPV4 的 IP 详细地址是用点试十进制表明的,例如:192.168.1.1,在 IP 头顶部用二进制表明得话,恰好是 4 个字节数 32 位。
32 位可以透露的 IP 详细地址是有局限的,应用了 IP 网络地址转换技术性 NAT。例如 ABC 三个住宅小区的任何机器设备很有可能公共了一个外网地址 IP,根据 NAT 技术性分到每一户一个私有化 IP 详细地址,大伙儿在小区域内沟通交流时很有可能应用的是私有化 IP 详细地址,可是向外沟通交流时就用外网地址 IP。
TCP 的数据结构和互动步骤
大家通常说的 HTTP 的 3 次握手和 4 次招手全是由 TCP 来进行的,实际上这都没 HTTP 啥事,可是有许多人喜爱那么说,严格意义上来说大家应当说 TCP 的 3 次握手 4 次招手。要弄清楚 TCP 的互动步骤,最先要清晰 TCP 的数据结构,下面大家来说一下 TCP 的数据结构:
以上 TCP 的数据结构图针对后边理解 HTTP 的互动步骤十分关键,我们要记牢 5 个重要的部位:
SYN:创建联接标志 ACK:回应标志 FIN:断开标志 seq:seq number,推送编号 ack:ack number,回应编号
服务端运用运行后,会在特定端口号监视客户端的联接请求,当客户端试着建立一个到服务端特定端口号的 TCP 联接,服务端接到请求后接纳数据信息并解决完业务流程后,会向客户端做出回应,客户端接到回应后接纳回应数据信息,随后断开,一个完全的请求步骤就完成了。那样的一个完全的 TCP 的生命期会历经下列 4 个流程:
1,创建 TCP 联接,3 次握手
客户端推送SYN, seq=x,进到 SYN_SEND 状态
服务端回复SYN, ACK, seq=y, ack=x 1,进到 SYN_RCVD 状态
客户端回复ACK, seq=x 1, ack=y 1,进到 ESTABLISHED 状态,服务端接到后进到 ESTABLISHED 状态 2,开展传输数据
客户端推送ACK, seq=x 1, ack=y 1, len=m
服务端回复ACK, seq=y 1, ack=x m 1, len=n
客户端回应ACK, seq=x m 1, ack=y n 1
3,断掉 TCP 联接, 4 次招手
服务器 A 推送FIN, ACK, seq=x m 1, ack=y n 1,进到 FNI_WAIT_1 状态
服务器 B 回复ACK, seq=y n 1, ack=x m 1,进到 CLOSE_WAIT 状态,服务器 A 接到后 进到 FIN_WAIT_2 状态
服务器 B 推送FIN, ACK, seq=y n 1, ack=x m 1,进到 LAST_ACK 状态
服务器 A 回复ACk, seq=x m 1, ack=y n 1,进到 TIME_WAIT 状态,等候服务器 B 很有可能规定重新传输 ACK 包,服务器 B 接到后关掉联接,进到 CLOSED 状态或是规定服务器 A 重新传输 ACK,客户端在一定的時间内收走到服务器 B 重新传输 ACK 包的标准后,断开进到 CLOSED 状态
为什么TIME_WAIT状态必须通过2MSL(较大报文格式段存活时长)才可以回到到CLOSE状态?
尽管按大道理,四个报文格式都推送结束,我们可以直接进入CLOSE状态了,可是人们务必假定互联网不是靠谱的,一切都很有可能产生,例如有可能最后一个ACK遗失。因此TIME_WAIT状态是用于再发很有可能遗失的ACK报文格式。
客户端与服务端创建联接、传送数据和断开等全靠这好多个标志,例如 SYN 还可以被拿来做为 DOS 进攻的一个方式,FIN 可以用于扫描仪服务端特定端口号。
HTTP 的数据结构
Socket 是 TCP/IP 的可编 API,HTTP 的可编程 API 的完成要依靠 Socket。HTTP 是HTML文件传输协议,HTTP 的头和数据信息看上去更为形象化,在大部分情形下,他们全是标识符或是字符串数组,因此针对绝大部分人而言理解 HTTP 的头和数据类型看起来非常简单。的确,HTTP 的数据类型理解下去很容易,上一部分是头,下部分是人体。
HTTP 的请求时的数据结构和回应时的数据结构总体上是一样的,可是有一些微小的差别,大家先来说一下 HTTP 请求时的数据结构:
HTTP 回应时的数据结构:
如今大家应用谷歌游览器请求某度,按住F12,来比照理解以上框架图,下边是请求某度
大家就可以简便的理解 HTTP 的数据结构了。
共享大量互联网最底层基本原理知识要点,內容包含Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体播放,CDN,P2P,K8S,Docker,TCP/IP,协同程序,DPDK这些。后台管理私聊【构架】获得
Linux下的socket演试程序流程
下边用最根本的Socket来开展服务端与客户端的互动,使你理解的更加清楚。
插口详细说明:
方式名 | 主要用途 |
socket(): | 建立socket |
bind(): | 关联socket到本地地址和端口号,通常由服务端启用 |
listen(): | TCP专用型,打开监视方式 |
accept(): | TCP专用型,网络服务器等候客户端联接,一般是堵塞态 |
connect(): | TCP专用型,客户端积极连接网络 |
send(): | TCP专用型,传送数据 |
recv(): | TCP专用型,获取数据 |
sendto(): | UDP专用型,传送数据到特定的IP地址和端口号 |
recvfrom(): | UDP专用型,获取数据,回到数据信息远端IP地址和端口号 |
close(): | 关掉socket |
根据TCP协议书完成CS端
应用Socket开展通信网络的全过程
① 服务器程序流程将一个套接字关联到一个特殊的端口,并根据此套接字等待和监视客户的联接请求。
② 客户程序流程依据服务器程序流程所属的服务器和端口号传出联接请求。
③ 假如一切正常,服务器接纳联接请求。并得到一个新的关联到不一样端口详细地址的套接字。
④ 客户和服务器根据读、写套接字开展通信。
客户机/服务器方式
在TCP/IP网络运用中,通讯的2个过程间相互作用力的具体方式是客户机/服务器模式*(client/server),即客户像服务项目其明确提出请求,服务器接纳到请求后,给予对应的服务项目。
服务器:
(1)最先服务器方需先运行,开启一个通讯安全通道并告之远程服务器,它想要在某一详细地址和端口上接受客户请求
(2)等待客户请求抵达该端口
(3)接受服务项目请求,解决该客户请求,服务项目进行后,关掉此新过程与客户的通讯链接,并停止
(4)回到第二步,等待另一个客户请求
(5)关掉服务器
客户方:
(1)开启一个通讯安全通道,并联接到服务器所属的服务器指定的端口
(2)向服务器推送请求,等待并接受回复,再次明确提出请求
(3)请求完毕后关掉通讯频带并停止
实际完成,新创建服务器端socket_server_tcp.c
实际编码如下所示: socket_server_tcp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define PORT 3040 //端口号
#define BACKLOG 5 //较大监视数
int main() {
int iSocketFD = 0; //socket句柄
int iRecvLen = 0; //接受取得成功后的传参
int new_fd = 0; //创建联接后的句柄
char buf[4096] = {0}; //
struct sockaddr_in stLocalAddr = {0}; //本地地址信息内容框架图,下边有主要的特性取值
struct sockaddr_in stRemoteAddr = {0}; //另一方详细地址信息内容
socklen_t socklen = 0;
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //创建socket SOCK_STREAM意味着以tcp方法实现联接
if (0 > iSocketFD) {
printf(\"建立socket不成功!n\");
return 0;
}
stLocalAddr.sin_family = AF_INET; /*该特性表明接受远程服务器或其它设备传送*/
stLocalAddr.sin_port = htons(PORT); /*端口号*/
stLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); /*IP,引号內容表明远程服务器IP*/
//关联详细地址结构体和socket
if (0 > bind(iSocketFD, (void *) &stLocalAddr, sizeof(stLocalAddr))) {
printf(\"关联不成功!n\");
return 0;
}
//打开监视 ,第二个主要参数是较大监视数
if (0 > listen(iSocketFD, BACKLOG)) {
printf(\"监视不成功!n\");
return 0;
}
printf(\"iSocketFD: %dn\", iSocketFD);
//在这儿堵塞了解接受到信息,主要参数分别是socket句柄,接受到的地点信息内容及其尺寸
new_fd = accept(iSocketFD, (void *) &stRemoteAddr, &socklen);
if (0 > new_fd) {
printf(\"接受不成功!n\");
return 0;
} else {
printf(\"接受取得成功!n\");
//推送內容,主要参数分别是联接句柄,內容,尺寸,更多信息(设成0就可以)
send(new_fd, \"这也是服务器接受取得成功后送回的信息内容!\", sizeof(\"这也是服务器接受取得成功后送回的信息内容!\"), 0);
}
printf(\"new_fd: %dn\", new_fd);
iRecvLen = recv(new_fd, buf, sizeof(buf), 0);
if (0 >= iRecvLen) //对端关掉联接 回到0
{
printf(\"对端关掉联接或是接受不成功!n\");
} else {
printf(\"buf: %sn\", buf);
}
close(new_fd);
close(iSocketFD);
return 0;
}
新创建客户端端socket_client_tcp.c socket_client_tcp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define PORT 3040 //总体目标详细地址端口号
#define ADDR \"10.6.191.177\" //总体目标详细地址IP
int main() {
int iSocketFD = 0; //socket句柄
unsigned int iRemoteAddr = 0;
struct sockaddr_in stRemoteAddr = {0}; //对端,即总体目标详细地址信息内容
socklen_t socklen = 0;
char buf[4096] = {0}; //储存接受到的数据信息
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //创建socket
if (0 > iSocketFD) {
printf(\"建立socket不成功!n\");
return 0;
}
stRemoteAddr.sin_family = AF_INET;
stRemoteAddr.sin_port = htons(PORT);
inet_pton(AF_INET, ADDR, &iRemoteAddr);
stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
//联接方式: 传到句柄,总体目标详细地址,和尺寸
if (0 > connect(iSocketFD, (void *) &stRemoteAddr, sizeof(stRemoteAddr))) {
printf(\"连接失败!n\");
//printf(\"connect failed:%d\",errno);//不成功时也可打印出errno
} else {
printf(\"联接取得成功!n\");
recv(iSocketFD, buf, sizeof(buf), 0); ////将获取数据打入buf,主要参数分别是句柄,储存处,较大长短,更多信息(设成0就可以)。
printf(\"Received:%sn\", buf);
}
close(iSocketFD);//关掉socket
return 0;
}
下边就是我的编译程序及运作实际效果:
编译程序指令如下所示:
gcc -o server socket_server_tcp.c
gcc -o client socket_client_tcp.c
#运行命令
./server #最先运行
./client #其次运行
根据UDP协议书完成CS端
**根据UDP(面对无联接)的socket程序编写——**数据信息报式套接字(SOCK_DGRAM) 互联网间通信AF_INET,典型性的TCP/IP四型实体模型的通讯全过程
服务器:(线程同步的【每10秒会打印出一行#号】 与 循环系统监视) socket_server_udp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
void * test(void *pvData)
{
while(1)
{
sleep(5);
printf(\"################################n\");
}
return NULL;
}
int main(void)
{
pthread_t stPid = 0;
int iRecvLen = 0;
int iSocketFD = 0;
char acBuf[4096] = {0};
struct sockaddr_in stLocalAddr = {0};
struct sockaddr_in stRemoteAddr = {0};
socklen_t iRemoteAddrLen = 0;
/* 建立socket */
iSocketFD = socket(AF_INET, SOCK_DGRAM, 0);
if(iSocketFD < 0)
{
printf(\"创建socket不成功!n\");
return 0;
}
/* 填好详细地址 */
stLocalAddr.sin_family = AF_INET;
stLocalAddr.sin_port = htons(12345);
stLocalAddr.sin_addr.s_addr = 0;
/* 关联详细地址 */
if(0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr)))
{
printf(\"关联详细地址不成功!n\");
close(iSocketFD);
return 0;
}
pthread_create(&stPid, NULL, test, NULL); //完成了线程同步
while(1) //完成了循环系统监视
{
iRecvLen = recvfrom(iSocketFD, acBuf, sizeof(acBuf), 0, (void *)&stRemoteAddr, &iRemoteAddrLen);
printf(\"iRecvLen: %dn\", iRecvLen);
printf(\"acBuf:%sn\", acBuf);
}
close(iSocketFD);
return 0;
}
手机客户端: socket_client_udp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
int main(void)
{
int iRecvLen = 0;
int iSocketFD = 0;
int iRemotAddr = 0;
char acBuf[4096] = {0};
struct sockaddr_in stLocalAddr = {0};
struct sockaddr_in stRemoteAddr = {0};
socklen_t iRemoteAddrLen = 0;
/* 建立socket */
iSocketFD = socket(AF_INET, SOCK_DGRAM, 0);
if(iSocketFD < 0)
{
printf(\"创建socket不成功!n\");
return 0;
}
/* 填好服务器端详细地址 */
stLocalAddr.sin_family = AF_INET;
stLocalAddr.sin_port = htons(12345);
inet_pton(AF_INET, \"10.6.191.177\", (void *)&iRemotAddr);
stLocalAddr.sin_addr.s_addr = iRemotAddr;
iRecvLen = sendto(iSocketFD, \"这是一个检测字符串数组\", strlen(\"这是一个检测字符串数组\"), 0, (void *)&stLocalAddr, sizeof(stLocalAddr));
close(iSocketFD);
return 0;
}
检测:
1、编译程序网络服务器:由于有线程同步,因此服务端过程要开展pthread编译程序
gcc socket_server_udp.c -pthread -g -o server_udp #手机客户端和上边同样
复制代码
实行結果如下所示:
右下方为手机客户端反复实行
服务端有主线任务程和纵线程,主线任务程,打印出手机客户端推送的要求;纵线程每过5秒左右打印出一排#号。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。