TCP报文

来源:互联网 发布:linux grep 命令 编辑:程序博客网 时间:2024/06/04 22:23

1、IP数据包的格式
包含的信息:
版本、首部长度、总长度
标识、标志、偏移量(这些在分片时会用到)
生存时间(8位,最大255,表示在因特网中最多经过的路由器的数值为255,当为1时只能在局域网中传播)、协议(标明ICMP、IGMP、TCP、UDP等)、首部校验和
源地址、目的地址
2 UDP数据报的格式
源端口号、目的端口号、长度、校验和
3 TCP
源端口、目的端口、{数据偏移,选项(MSS)}长度、检验和
序号、确认号、6个控制位(ACK、FIN、SYN、URG、PSH、RET)
窗口(接收窗口,接收方允许发送方发送的数据量)


解释一个名词:MSS(最大报文段长度)数据段的最大长度,不包括首部长度,,默认是536,加上20字节的头部,256


TCP报文



序号:指出段中数据在发送端数据流中的位置。

当发送syn标记时将消耗一个序号;

2,  发送fin标记时将消耗一个序号;

3,  数据是每个字节一个序号;

确认号:指出本机希望下一个接收的字节的序号。
头长:指出以32比特为单位的段头长度。它是针对便长的“选项”域设计的。
码位:有些报文段是用于传输数据的,但有些报文段仅仅携带了确认信息,另一些报文段携带的是建立和关闭连接的请求。它使用“码位”来指出段的目的与内容。这6个比特各位的意义如图:
                          URG,紧急指针字段可用
                          ACK,确认字段可用,只有当ACK=1时,确认序号字段才有意义
                          PSH,本报文段请求急迫(PUSH)操作
                          RST,连接复位
                          SYN,序号同步,用于建立连接中的同步,当SYN=1,而ACK=0时,是一个连接请求,对方赞成连接则在返回的报文中使SYN=1, ACK=1;SYN=1默认这是一个连接请求或毗邻接管报文而ACK用来区分是那种报文。
                          FIN,发送方字节流结束FIN=1注解欲发送的直接串发完。并请求断开连接。


由于有时发送方希望终止操作,不想接收方接收完所有的比特。如远程主机上的程序错误时就需要这种信号。TCP将数据指定为“紧急”型,接收方收到这种数据后,会不必排队而尽快通知应用程序。URG比特为1时,表明它是紧急的,其中的“紧急指针”指出了紧急数据在报文段中的结束位置。
当所有的紧急数据消失之后,TCP软件会告诉应用程序恢复正常的操作状态。PUSH位提供了数据强迫传输机制,以强迫传输当前流中的数据,而不必等待缓冲区满。远程登录中,终端的击键信号就是用PUSH操作来提供的。
窗口(滑动窗口):用于通知接收端接收缓冲区的大小。
理解TCP的面向连接特性:
一条TCP连接是一条虚电路,它通过主机IP地址和端口号来标识一条连接。如(192.168.2.37,21;192.168.2.30,21)就标识了一条TCP连接。所以两个程序可以共享主机上的同一端口,因为连接不同。
连接的建立和拆除需要得到对方的认可,而UDP中,一方发送数据是不需要得到对方的认可的。
连接能保持状态并实现可靠性,而无连接不能保持状态。一般来说,控制信息是通过TCP来传递的。
它是在无连接的协议,IP协议之上来实现面向连接的。



4 TCP的连接和释放过程

客户机A开始处于CLOSED(关闭状态)
服务器开B始CLOSED,后进入listen(收听)状态,等待客户连接请求
A向B发送SYN=1,A的序列号为seq=x的连接请求报文段,不携带数据,但消耗一个序列号
此时A进入到SYN-SENT(同步已发送)状态

B收到后,向A发送确认信号,SYN=1,ACK=1,ack=x+1,seq=y,不携带数据,但消耗一个序列号
此时B进入到SYN-RCVD(同步收到)状态

A收到后,还要给B发送确认(ACK=1,ack=y+1,seq=x+1),不携带数据不消耗,携带数据消耗序列号
A进入到ESTABLISHED(已建立连接状态)

B收到确认后进入(ESTABLISHED)
这里有一个问题:
为什么A还要向B发送一个确认信号

防止已经失效的A的连接请求又发送到B,B以为A要建立连接,就一直等待A发数据,但A没有这样的请求,就不会理B的确认信号,这样导致B资源浪费

TCP连接的释放
A主动关闭TCP连接,A发送连接释放报文,FIN=1,seq=u,不携带数据也消耗序列号
A进入到FIN-WAIT1(终止等待1)

B收到后向A发出确认,ACK=1,seq=w,ack=u+1
B进入到CLOSE-WAIT(关闭等待)
A收到确认后进入FIN-WAIT2(终止等待2)


此时TCP连接处于半关闭
A已经没有数据发送了,但是B发送数据A任然要接收
B发送完数据后
B发送释放连接数据报,FIN=1、ACK=1,ack=u+1,seq=w(B必须重复上次发送过的确认信号),此时B进入到(LAST-ACK)状态
A向B发出确认信号(ACK=1,ack=w+1,seq=u+1),A进入到TIME-WAIT(时间等待)
B收到后CLOSED
A经过2MSL(最长报文段寿命)也结束

这里问题
1 为什么要设置2MSL
1》
为了保证A发送的最后一个ACK可以到达B,ACK可能丢失,B收不到就会超时重传FIN+ACK,A能在2MSL收到这个重传的信号,重新发出ACK启动计时器
2》
防止已失效的报文段出现在本连接中,2MSL所有报文段都会消失


————————————————————————————————————————————————————————————————————————————————————

套接字SOCKET

大体流程:
服务器:调用函数socket创建一个套接字,函数bind将这个套接字与服务器的公认地址绑定,
listen将这个套接字转换成倾听套接字,然后调用accept来等待接受客户机的请求
客户机:调用函数socket创建一个套接字,调用connect与服务器建立连接
最后二者之间通过读写套接字来通信

具体:

socket:参数:协议簇(AF_INET)Internet(TCP/IP)
类型:SOCKET_STREAM
SOCKET_DGRAM
协议:0,默认协议

linux系统中套接字的操作主要是:在内核中创建一个套接字数据结构,然后返回一个套接字描述符标志这个套接字数据结构,这个结构中包含连接的各种信息,对方的地址,TCP状态,发送和接收缓存区

函数bind将本地地址与套接字绑定在一起
服务器和客户机都可以调用函数bind来绑定套接字地址,但一般是服务器调用函数bind来绑定自己的公认端口,一般不指定IP,表示愿意接收来自任何网络接口的客户机连接,意思是有多个网络接口的机器,来自任意一个网络接口的数据都可以接收。

客户机不用指定自己的套接字地址的端口号,当客户机调用函数connect进行TCP连接,系统会自动为他分配一个未用的端口号的,一般不要指定特定的端口号

listen
将一个套接字转为倾听套接字

由于socket创建的套接字是主动套接字,可以用它来进行主动连接,但是不能接受连接请求,而服务器的套接字必须能接受客户机的请求,listen将主动套接字转换成一个被动套接字

其中还包括一个重要的参数,请求队列的长度,backlog表示这个套接字能够接受的最大数目的未接收的连接(已经完成三次握手,但还没有被应用程接收处理)

accept
函数accept从倾听套接字的完成连接队列中接收一个连接
由于倾听套接字是专为接收客户机连接请求,完成三次握手而用的,所以TCP协议不能使用倾听套接字的标志描述这个连接,
意思就是返回一个新的套接字描述符,来标志这个连接,与客户进行通信


connect
指定与服务器信息相关的socket
客户机不用指定自己的套接字地址的端口号,当客户机调用函数connect进行TCP连接,系统会自动为他分配一个未用的端口号的,一般不要指定特定的端口号
1024-5000之间

close
调用了函数close进程将不能访问这个套接字,但是不表示TCP协议删除了这个套接字,TCP将继续使用这个套接字,将尚未发送的数据传递到对方,然后发送fin数据段,执行关闭,之后才删除这个套接字


read write
里想说明下缓存区

从网络上读、写的数据操作是由TCP协议在内核中完成的,然后TCP套接的缓存区,从内核中接收数据放在自己里面,read,write则从套接字的缓存区中,将数据拷贝到用户缓存区

这里buff的参数也是用户缓存区的参数

readline一次读取一个字符,检测这个字符是否有回车符,如果是,返回,读取了一行
read是系统调用,每次执行需从内核拷贝到用户缓存区,一次拷贝一个效率低的很
为了提高效率,read_line调用函数get_char来读取一个字符,由函数get_char从内核读取数据
get_char调用函数read一次从内存读取一块数据,保存在静态缓存区中,read——line调用getchar读取一个字符时,检查静态缓存区,没有在从内存读


UDP连接
服务器首先调用函数socket建立一个数据报类型的套接字,调用bind绑定端口,然后调用recvfrom接收UDP客户机的数据

 

recvfrom
可以再参数中获得发送者的地址
sendto
可以再参数中指定接收者的地址

因为UDP是非面向连接的,所以系统不再套接字结构中记录接收者的地址信息,需要在每次调用发送函数sendto时指定接收方的地址
接收数据包时,如果需要知道发送者的地址,可以再接收函数recvfrom中提供空间,由内核来填充,不需要,则这个参数置0即可


UDP协议没有为udp套接字设置发送缓冲区,函数sendto将数据从用户缓冲区拷贝到系统缓冲区,也可以说UDP缓冲区只要一个缓冲区大小,这个大小用来限制可以发送的最大UDP数据报的大小
但是有接收缓冲区队列

UDP允许接收空数据报,此时函数recvfrom的返回值为0
TCP中读操作返回0,表示读到了文件结束符,表示对方结束发送了

服务器是循环服务器
UDP服务器与客户机没有固定的连接,UDP服务器能够交替的处理多个客户机的请求,前后两次循环处理的可以使来自不同客户机的请求,一个UDP客户机崩溃,不会照成服务器服务失效
TCP客户机可以独占服务器,服务器只能处理完一个客户机的请求,在处理下一个,当一个客户机主机崩溃时,可能照成服务器的服务失效


服务器无法获得自己的IP(除非绑定时,设置本地IP)
客户机不用建立连接
应用程序首次调用sendto时,udp协议为之分配一个自由端口,这个端口号在整个通信过程中都不变,不存在本地IP,这个地址在IP协议发送数据报时,根据路由情况选择,然后填充到UDP数据报的首部,所以数据报们,使用相同的端口,但IP可能不同


客户机可以接收来自任何主机的数据报,只要这个主机知道他的本地端口号,所以客户机要检查发送过来的数据报的发送者,只接受服务器的

0 0