【网络是怎样连接的】第1章 - 浏览器生成消息(4)

2018-06-13  本文已影响0人  邱杉的博客

1.4 委托协议栈发送消息

要发送给Web服务器的HTTP消息是一种数字信息(digital data),因此也可以说是委托协议栈来发送数字信息。收发数字信息这一操作不仅限于浏览器,对于各种使用网络的应用程序来说都是共通的。因此,这一操作的过程也不仅适用于Web,而是适用于任何网络应用程序。

向操作系统内部的协议栈发出委托时,需要按照指定的顺序来调用Socket库中的程序组件。

建立管道的关键在于管道两端的数据出入口,这些出入口称为套接字。我们需要先创建套接字,然后再将套接字连接起来形成管道。实际的过程是下面这样的。首先,服务器一方先创建套接字,然后等待客户端向该套接字连接管道。当服务器进入等待状态时,客户端就可以连接管道了。

当数据全部发送完毕之后,连接的管道将会被断开。管道在连接时是由客户端发起的,但在断开时可以由客户端或服务器任意一方发起。其中一方断开后,另一方也会随之断开,当管道断开后,套接字也会被删除。到此为止,通信操作就结束了。

创建、连接、通信、关闭这4个操作都是由操作系统中的协议栈来执行的,浏览器等应用程序并不会自己去做连接管道、放入数据这些工作,而是委托协议栈来代劳。

书中出现了Socket、socket、套接字(英文也是socket)等看起来非常容易混淆的词,其中小写的socket表示程序组件的名称,大写字母开头的Socket表示库,而汉字的“套接字”则表示管道两端的接口。

图1.17 数据通过类似的结构来流动.png 图1.18 客户端和服务器之间收发数据操作的情形.png

创建套接字阶段

调用socket之后,控制流程会转移到socket内部并执行创建套接字的操作,完成之后控制流程又会被移交回应用程序。

套接字创建完成后,协议栈会返回一个描述符,应用程序会将收到的描述符存放在内存中。

当创建套接字后,我们就可以使用这个套接字来执行收发数据的操作了。这时,只要我们出示描述符,协议栈就能够判断出我们希望用哪一个套接字来连接或者收发数据了。(一台计算机可能同时存在多个套接字)
应用程序是通过“描述符”这一类似号码牌的东西来识别不同套接字的。

连接阶段:把管道接上去

connect(描述符, 服务器 ip 地址, 端口号)

我们需要委托协议栈将客户端创建的套接字与服务器那边的套接字连接起来。应用程序通过调用Socket库中的名为connect的程序组件来完成这一操作。

描述符是和委托创建套接字的应用程序进行交互时使用的,并不是用来告诉网络连接的另一方的,因此另一方并不知道这个描述符。

如果说描述符是用来在一台计算机内部识别套接字的机制,那么端口号就是用来让通信的另一方能够识别出套接字的机制。

客户端在创建套接字时,协议栈会为这个套接字随便分配一个端口号。接下来,当协议栈执行连接操作时,会将这个随便分配的端口号通知给服务器。

描述符:应用程序用来识别套接字的机制
IP地址和端口号:客户端和服务器之间用来识别对方套接字的机制

通信阶段:传递消息

断开阶段:收发数据结束

当浏览器收到数据之后,收发数据的过程就结束了。接下来,我们需要调用Socket库的close程序组件进入断开阶段(图1.18④)。最终,连接在套接字之间的管道会被断开,套接字本身也会被删除。

HTTP协议将HTML文档和图片都作为单独的对象来处理,每获取一次数据,就要执行一次连接、发送请求消息、接收响应消息、断开的过程。因此,如果一个网页中包含很多张图片,就必须重复进行很多次连接、收发数据、断开的操作。对于同一台服务器来说,重复连接和断开显然是效率很低的,因此后来人们又设计出了能够在一次连接中收发多个请求和响应的方法。在HTTP版本1.1中就可以使用这种方法,在这种情况下,当所有数据都请求完成后,浏览器会主动触发断开连接的操作。


Linux下的Socket编程(主要包括TCP部分)

TCP C&S交互过程.png

所以两者的区别在与客户端在创建了套接字之后不进行地址绑定,而是直接连接服务器端。

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

socket()函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。如果函数调用成功,会返回一个表示这个套接字的文件描述符,失败的时候返回–1。

参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信的协议族,通信的协议族在文件sys/socket.h定义,包含下表所示的值,以太网应该设置PF_INET这个域,在程序设计的过程中会发现有的代码使用了AF_INET这个值,在头文件中AF_INET和PF_INET的值是一致的。

参数type用于设置套接字通信的类型,主要有SOCK_STREAM(流式套接字),SOCK_DGRAM(数据包套接字)。

函数socket()并不总是执行成功,有可能会出现错误,错误的原因有很多种,可以通过errno获得。在TCP中可以通过socket(AF_INET,SOCK_STREAM,0)返回一个TCP的套接字文件操作符。

上一篇 下一篇

猜你喜欢

热点阅读