网络是怎样连接的学习笔记-用电信号传输TCP/IP数据(上)

知识点:

TCP、IP协议

协议栈、套接字

ACK、滑动窗口


1、 创建套接字

整体上看,TCP/IP软件分层情况:

图中,Socket库中的解析器用来向DNS服务器发送查询。

ICMP协议用于告知网络包传送过程中产生的错误以及各种控制消息。

ARP协议用于根据IP地址查询相应的以太网MAC地址。

系统为协议栈分配了一块用于存放控制信息的内存空间,这里记录了用于控制通信操作的控制信息,例如通信对象的IP地址(DNS服务器返回的查询结果)、端口号、通信操作的进行状态等。本来套接字就只是一个概念而已,并不存在实体,如果一定要赋予它一个实体,我们可以说这些控制信息就是套接字的实体,或者说存放控制信息的内存空间就是套接字的实体。

在发送数据时,协议栈通过读取套接字通信对象的IP地址和端口号来确定对端等待连接的套接字。套接字中还会存储数据包发送后的计时信息,以便系统确定是否需要因为超时重新发送数据。

应用程序通过调用Socket库中的socket函数申请创建套接字,协议栈响应申请完成创建套接字的实际工作。在这个过程中,协议栈首先向系统申请分配一个用于存放套接字所需控制信息的内存空间,并向其中写入表示初始状态的控制信息(此时并没有写入通信IP和端口号等信息)。

socket函数的原型:int socket(int af, int type, int protocol);其中:af为IP地址类型,即IPv4、IPv6等;type为数据传输方式,即SOCKET_STREAM(流式套接字)、SOCK——DGRAM(数据包套接字)等;protocol为协议类型,即IPPROTO_TCP、IPPTOTO_UDP等。socket函数向应用程序返回套接字描述符,描述符唯一代表一个套接字。

2、 连接服务器

连接实际上是通信双方交换控制信息,在套接字中记录这些必要信息并准备数据收发的一连串操作。

1. 把服务器的IP地址和端口号等信息告知协议栈(实际上就是写入套接字对应的内存中);

2. 客户端向服务器传达开始通信的请求(与服务器互相交换套接字中的控制信息);

3. 分配缓冲区(一块用来临时存放要收发的数据的内存空间)。

注:服务器程序一般会在系统启动时就创建套接字并等待客户端连接。

控制信息大体上分为两类:

1. 客户端和服务器相互联络时交换的控制信息。控制信息位于网络包的开头,因此被称为头部;

2. 保存在套接字中,用来控制协议栈操作的信息。应用程序传递来的信息以及从通信对象接收到的信息都会保存在这里,还有收发数据操作的执行状态等信息也会保存在这里。
应用程序通过调用Socket库中的connect函数申请与服务器端的套接字建立连接,协议栈响应申请将本地套接字与服务器套接字进行连接。

connect函数的原型:int connect(int sockfd, const struct sockaddr*server_addr, socklen_t addrlen);其中:sockfd为套接字描述符(socket返回的值),指定数据发送的套接字;server_addr为服务器IP和端口号的结构体,指定数据发送的目的地;addrlen指定server_addr结构体的长度。

1.     客户端先创建一个包含表示开始数据收发操作的控制信息的头部(设置SYN为1),客户端(发送方)的套接字根据头部中的服务器(接收方)IP地址和端口号确定了应该连接哪个套接字;

2.     当TCP头部创建好之后,接下来TCP模块会将信息传递给IP模块并委托它进行发送;

3.     IP模块执行网络包发送操作后,网络包就会通过网络到达服务器,然后服务器上的IP模块会将接收到的数据传递给TCP模块,服务器的TCP模块根据TCP头部中的信息找到端口号对应的套接字(从处于等待连接状态的套接字中找到与TCP头部中记录的端口号相同的套接字);

4.     服务器的TCP模块生成响应TCP头部数据包(设置SYN为1,设置ACK为1表示已经收到请求包),IP模块接到委托后向客户端返回响应信息;

5.     客户端收到响应信息后,查看SYN为1表示连接成功,向套接字中写入服务器IP地址和端口号等信息;

6.     客户端将ACK置1,生成响应数据包,发送给服务器,向服务器确认收到响应包;

7.      服务器收到上述返回包后,连接操作全部完成。

3、 收发数据

数据收发操作是从应用程序调用write将要发送的数据交给协议栈开始的,协议栈收到数据后执行发送操作。

1.     协议栈不关心数据内容,数据对于协议栈来说就是一段一定长度的二进制序列;

2.      协议栈并不是一收到数据就马上发送出去,而是会将数据存放在内部的发送缓冲区中,并等待应用程序的下一段数据,如果一收到数据就马上发送出去,就可能会发送大量的小包,导致网络效率下降,因此需要在数据积累到一定量时再发送出去;另一个判断要素是时间。当应用程序发送数据的频率不高的时候,如果每次都等到长度接近MSS时再发送,可能会因为等待时间太长而造成发送延迟;

3.      当发送缓冲区中的数据就超过MSS时,数据会被以MSS长度为单位进行拆分,拆分出来的每块数据会被放进单独的网络包中。

4.      使用ACK号确认网络包已收到。TCP模块在拆分数据时,会先算好每一块数据相当于从头开始的第几个字节,在发送数据时将算好的字节数(序号)写在TCP头部中;接收方用整个网络包的长度减去头部的长度就可以得到数据的长度。有了这两个数值,接收方就可以知道发送的数据是从第几个字节开始,长度是多少了,可以确定下一个数据包起始字节的数字,通过与下一个数据包头部中的序号进行比对,就可以实现数据包是否有遗漏的检查。接收方将目前已经收到的自己数写入ACK发回发送方,实现网络包接收确认(在实际的通信中,序号并不是从1开始的,而是需要用随机数计算出一个初始值,这是为了防止攻击者通过预测序号来实现网络攻击。这个初始值实在连接的第一步,即将SYN设置为1时,同时设置在序号中告诉接收方的);

客户端和服务器双方都需要各自计算序号,因此双方需要在连接过程中互相告知自己计算的序号初始值。

5.     根据网络包平均往返时间调整ACK号等待时间(超时时间)。超过ACK等待时间认为数据发送超时,发送方会重新发送数据,当网络拥堵时,ACK返回等待时间会变长,如果不动态调整超时时间,会导致多余的数据重发,加重网络拥堵。TCP会在发送数据的过程中持续测量ACK号的返回时间,如果ACK号返回变慢,则相应延长等待时间;相对地,如果ACK号马上就能返回,则相应缩短等待时间。

6.      通过滑动窗口提高网络利用效率。所谓滑动窗口,就是在发送一个包之后,不等待ACK号返回,而是直接发送后续的一系列包。这样一来,虽然等待ACK号的这段时间被有效利用起来了,但是有可能会出现发送包的频率超过接收方处理能力的情况(接收方缓冲区溢出),通过下面的方法来避免这种情况的发生。接收方通过TCP头部中的窗口字段告诉发送方自己最多能接收多少数据,然后发送方根据这个值对数据发送操作进行控制,这就是滑动窗口方式的基本思路。

7.     ACK与窗口的合并。接收方在发送ACK号和窗口更新(只有应用程序从缓存中拿走数据时才需要更新)时,并不会马上把包发送出去,而是会等待一段时间,在这个过程中很有可能会出现其他的通知操作,这样就可以把两种通知合并在一个包里面发送了。比如将ACK号和窗口更新信息合并为一个包,比如将多个联系的ACK号精简为只发送最后一个ACK号(因为ACK号只是用来确认已接受数据包的最后位置的)。

8.     接收服务器的响应消息。客户端发送请求消息后,会调用read程序委托协议栈读取本地接收缓存,如果缓存为空,协议栈将此委托暂时挂起(协议栈会在此时处理来自其他程序的委托),直到服务器返回的响应消息到达之后再继续执行。

9.     协议栈收到数据后首先检查TCP头部内容,判断数据是否有丢失,如果没有,则将数据块暂存到接收缓冲区,并按照顺序拼接成原始数据,最后交给应用程序。

4、 从服务器断开并删除套接字

完成数据发送的一方会发起断开过程。以服务器发起断开过程为例:

1.     服务器一方的应用程序会调用Socket库的close函数;

2.     服务器的协议栈会生成包含断开信息的TCP头部,具体来说就是将控制位中的FIN比特设为1;

3.     协议栈会委托IP模块向客户端发送数据。同时,服务器的套接字中也会记录下断开操作的相关信息;

4.     当客户端收到服务器发来的FIN为1的TCP头部时,客户端的协议栈会将自己的套接字标记为进入断开操作状态;

5.     为了告知服务器已收到FIN为1的包,客户端会向服务器返回一个ACK号;

6.     客户端应用程序调用read函数委托协议栈读取服务器发来的FIN为1的数据包,这时协议栈不会向应用程序传递数据,而是会告知应用程序来自服务器的数据已经全部收到了。客户端应用程序调用close函数;

7.     客户端协议栈叶生成一个FIN比特为1的TCP包,委托IP模块发给服务器,一段时间后,服务器也会返回ACK号。

8.     通信结束。

通信结束后,套接字不会马上被删除,防止误操作。(举一个误操作的例子:如果服务器最后返回的ACK客户端没有收到,可能客户端会重发一次FIN,而此时如果服务器的套接字已经删除且该端口分配给了另一个应用程序,客户端重发的FIN会导致刚新建的套接字执行关闭操作。)

5、 连接和数据收发小结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值