Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

网络是怎样连接的 #4

Open
Cyrilszq opened this issue Jan 23, 2017 · 0 comments
Open

网络是怎样连接的 #4

Cyrilszq opened this issue Jan 23, 2017 · 0 comments

Comments

@Cyrilszq
Copy link
Owner

最近把《网络是怎样连接的》这本书看了一遍,发现讲得挺清楚的,作为科普还是不错的。所以对于原来我不知道的东西做一些记录、总结。具体内容是关于“从输入URL到页面加载完成的过程中都发生了什么事情?”这个问题回答。虽然这个问题网上有很多答案,但这确实是涉及面太广,很难完全说明白的一个问题。

第一步:浏览器解析URL

这一步主要与HTTP相关,包括生成HTTP信息(请求行,消息头,消息体),平时遇到的比较多,书中提到的基本都是已经知道的了。

第二步:DNS域名解析

具体过程如下:浏览器调用操作系统Socket库,将域名传入Socket库中的域名解析函数,该函数根据域名生成一些信息(包括域名,Class,记录类型),并将这些信息打包传给操作系统内部协议栈,由操作系统协议栈发起网络请求(此时目标服务器为DNS服务器,IP一般已经指定,或自动获取,不需要再查询IP),DNS服务器收到信息后,如果有缓存则直接返回结果,如果没有就从根域名开始查找,一级一级查找,直到得到结果。结果也由操作系统内部协议栈接收,并写入内存保存。顺便提一下,DNS查询是基于UDP协议的。

第三步:建立连接,发起请求(TCP/IP)

先整理大体过程:获得IP后浏览器就可以向目标服务器请求数据,不过这个过程依然是委托给是Socket库。首先调用Socket库的socket组件创建客户端套接字,此时会返回一个描述符(主要为了区别不同的套接字),然后调用connect组件,将描述符,服务器IP,端口作为参数传入。此时协议栈会执行连接操作,成功后协议栈会将对方的IP地址和端口号等信息保存在套接字中。现在套接字连接就建立起来了,剩下的只要将数据送入套接字,这些数据就会传到对方的套接字中。调用Socket库的write,传入描述符和要发送的数据,经过网络,服务器就会收到我们发的数据,做出响应后返回数据给客户端,此时客户端调用Socket库中的read程序委托协议栈完成数据的接收,写入内存,待所有内容接收完毕后后断开连接。这里只是大体流程,具体内容涉及到TCP/IP协议。

下面具体讨论这一过程。上面提到的第一步是调用Socket库的socket组件创建客户端套接字,套接字其实就是通信控制信息,如通信对象的IP地址,端口号,通信的进行状态等,协议栈在执行操作时需要参阅这些信息才能正确通信。可以用netstat -ano命令获取当前系统套接字内容。至于调用socket就是在协议栈开辟内存并写入初始状态,最后会返回一个描述符。应用程序拿到了描述符就可以确定套接字(也就确定了通信信息),接下来只要在创建连接时带上这个描述符,协议栈就知道该去和谁通信。下面一步是调用connect,传入描述符,服务器IP端口号(http默认是80)等信息,协议栈根据这些信息创建TCP头部,其中包括发送方和接收方的端口号,控制位SYN设为1(表示这是建立连接的包),设置适当的序号(在判断数据是否成功送达时用到)和窗口大小(用于滑动窗口模式),并将信息传递给IP模块委托它进行发送。服务器接收到后根据TCP头信息提供的端口号找到相应的套接字,服务器做一系列操作后(包括将套接字的状态改为正在连接),然后返回响应,这个过程和客户端一样,此外还要将ACK位设位1,表示已经收到相应的网络包。客户端收到服务器的响应后还要再返回一个ACK表示确认。其实整个流程就是TCP/IP三次握手的过程。下面一步是调用write,这时TCP会将请求信息切分成一定大小的块,并在每一块上加上TCP头部(包含序号,表示当前发送的是第几个字节的数据),服务器收到后会返回ACK号表示确认收到,(TCP就是依靠序号和ACK号来确保数据传输可靠,因为如果中间某个包丢失,客户端检测ACK号就可以知道丢了哪个包,直接重发就可以了)这个过程将持续到所有数据收发完毕。最后一步是关闭连接删除套接字,与前面连接过程类似,不过是通过FIN标志位表示关闭连接,双方都确认后才关闭连接,然后过一段时间就可以删除套接字。

TCP模块处理完后的包还要交由IP模块,IP模块所做的就是继续在包上添加IP头部(包含发送,接收双方的IP地址)和MAC头部(包含接收,发送方的MAC地址,其中接收方的MAC地址一开始客户端并不知道,可以用ARP查询),这些信息在后面经过交换机,路由器时都要用到,这时一个完整的以太网包就构建好了,IP模块将这个完整的包传递到网卡驱动,网卡驱动会把包交由MAC模块做最后的处理(添加报头起始帧分界符和校验位),然后经由电路将数字信号转换为电信号,发送出去。

顺便记一下用UDP与TCP传输时的区别:TCP会在发送每一个包时都进行确认,而UDP直接把所有数据全部发送出去,接收方只返回一次确认,如果接收方没有确认,发送方直接重发所有的包(显然比较低效)。这种机制比较适用于控制用的短数据,比如一共只有一个包,或者是视频音频数据。

关于传输过程的一些细节:

  • 硬件交互

关于硬件不是很感兴趣,所以这块只是大致看了一下,包括如何将电信号转化成数字信号,如何抑制传输过程中的噪声,交换机如何进行包转发(内部维护一张MAC地址-端口的表),路由器包转发操作(维护一张路由表)等。

  • 关于服务端

包括防火墙,负载均衡,内容分发服务(CDN)。防火墙通过配置包过滤规则对网络包进行过滤;使用CDN时,通过查询路由表可大致估算出客户端距离缓存服务器的距离,进而选择最近的缓存服务器。
而且服务端的TCP模块工作过程也和客户端有所不同,客户端用的是connect发起连接,服务端则是用bind和listen使服务器处于等待连接状态,然后调用accept,待请求到来后立即进行处理。

总结

网络涉及到的东西太多了,各种协议…… 这本书也只是概括着讲了整个流程,不过补了一下这些基础知识之后还是有些好处的,解决了原来的一些困惑。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant