【基础知识】TCP三次握手四次挥手|unsigned int与int|struct结构体class类大小计算

TCP,UDP不同详解,还有一些其他的

1.TCP三次握手与四次挥手

TCP报文格式

上图中有几个字段需要重点介绍下:
        (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
        (2)确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
        (3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
                (A)URG:紧急指针(urgent pointer)有效。
                (B)ACK:确认序号有效。
                (C)PSH:接收方应该尽快将这个报文交给应用层。
                (D)RST:重置连接。
                (E)SYN:发起一个新连接。(表示这个报文使用来建立新连接用的)
                (F)FIN:释放一个连接。

        需要注意的是:
                (A)不要将确认序号Ack与标志位中的ACK搞混了。
                (B)确认方Ack=发起方Seq+1,两端配对。 

三次握手

TCP服务器进程先创建传输控制模块TCB,准备接受客户进程的连接请求,然后服务器进程就处于LISTEN(监听)状态,等待客户的连接请求 

  1. 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。(TCP规定,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。)
  2. 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,确认序号ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。(请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。)
  3. 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态

 简化一下不看Seq和ack的话过程如下:

  1. 客户端发送SYN给服务端
  2. 服务端返回SYN+ACK给客户端
  3. 客户端确认,返回ACK给服务端

完成三次握手,客户端与服务器开始传送数据。

为什么客户端还要发送一次确认呢?

这主要是为了防止已失效的连接请求报文段突然又传送到了服务器,因而产生错误。

  假定A发出的某一个连接请求报文段在传输的过程中并没有丢失,而是在某个网络节点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误以为A又发了一次新的连接请求,于是向A发出确认报文段,同意建立连接。假如不采用三次握手,那么只要B发出确认,新的连接就建立了。

  由于A并未发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据,因此白白浪费了许多资源。

  采用TCP三次握手的方法可以防止上述现象发生。例如在刚才的情况下,由于A不会向B的确认发出确认,B由于收不到确认,就知道A并没有要求建立连接。

四次挥手

  1. 客户端发送一个报文给服务端(没有数据),用来关闭Client到Server的数据传送,其中FIN设置为1,Sequence Number置为u,客户端进入FIN_WAIT_1状态
  2. 服务端收到来自客户端的请求,发送一个ACK给客户端,Acknowledge置为u+1,同时发送Sequence Number为v,服务端年进入CLOSE_WAIT状态
  3. 服务端发送一个报文给客户端,用来关闭Server到Client的数据传送,其中FIN设置为1,ACK置为1,Sequence置为w,Acknowledge置为u+1,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK状态
  4. 客户端收到FIN后,进入TIME_WAIT状态,接着发送一个ACK给服务端,Acknowledge置为w+1,Sequence Number置为u+1,最后客户端和服务端都进入CLOSED状态

简化一下不看Seq和ack:

  1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
  2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
  3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
  4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,我们也未必把全部数据都发给了对方,所以我们可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方表示同意关闭连接。因此我们的ACK和FIN一般会分开发送。

之前讨论的是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图 

è¿éåå¾çæè¿°

 

2.

 

3.单工,半双工,全双工

单工

单工数据传输只支持数据在一个方向上传输(在同一时间只有一方能接受或发送信息,不能实现双向通信)

举例:电视,广播。

半双工

半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输(它实际上是一种切换方向的单工通信;在同一时间只可以有一方接受或发送信息,可以实现双向通信。)

举例:对讲机。

全双工

全双工数据通信允许数据同时在两个方向上传输。(因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力;在同一时间可以同时接受和发送信息,实现双向通信。)

举例:电话通信。

4.unsigned int 和 int相加

int main()
{
	int a = -6;
	unsigned int b = 4;
	if(a+b > 0)
		printf("a+b>0\n");//这句话被打印
	else
		printf("a+b<0\n");
	int z = a+b;
	if(z > 0)
		printf("z>0");
	else
		printf("z<0");//这句话被打印
}

下面解释一下原因:当int和unsigned in相加时,要将int转化为unsigned int(有人说总是向能表示范围更大的类型转换),而int小于0,所以它的最高位是符号位,为1,所以转化的结果是一个很大的正数,在第一个if语句中,是两个“正数”相加,结果自然就大于0了。而在z = a+b这一句时(相当于把unsigned int转化为int),它把a+b的结果看做一个int类型,而a+b最高位为1,所以z是一个负数,所以打印的是第二个语句。

 

5.struct结构体大小计算(在32/64位操作系统)

预备知识:基本类型占用字节

32位和64位的位数分别是什么意思?

这个位数指的是CPU 里面的通用寄存器的数据宽度为64位,也就是说一个地址占二进制位数是32/64。所以32位系统的指针占大小是4字节,64位系统就是8字节。(有人说自己是64位系统为什么指针大小打印是4字节,因为编译器是32位的,跟编译器而不是自己的电脑有关)

在32位操作系统和64位操作系统上,基本数据类型分别占多少字节呢?

32位操作系统:

char : 1    int :4    short : 2    unsigned int : 4    long : 4    unsigned long : 4    long long : 8     float : 4    double : 8    指针 : 4

64位操作系统

char : 1    int :4    short : 2    unsigned int : 4    long : 8    unsigned long : 8    long long : 8     float : 4    double : 8    指针 : 8

 

默认对齐方式

在默认对齐方式下,结构体成员的内存分配满足下面三个条件

  1. 结构体第一个成员的地址和结构体的首地址相同
  2. 结构体每个成员地址相对于结构体首地址的偏移量(offset)是该成员大小的整数倍,如果不是则编译器会在成员之间添加填充字节(internal adding)。
    1. 成员变量是数组时,按照类型长度对齐,而不是数组长度对齐(比如char a[10]就是按1而不是10来对齐)
    2. 如果成员是struct结构体,根据上一条,并不是按该结构体大小对齐,而是按该结构体中数据类型最大的成员来对齐
    3. union联合体同上
      1. union内存大小需要满足以下2个条件:

        1. 大于或等于union中最长的成员变量的长度(char c[10]算10字节);

        2.  能整除其他成员变量长度(char c[10]算1字节)

  3. 结构体总的大小要是其成员中最大size(注意是最长的数据类型,而不是最长的变量)的整数倍,如果不是编译器会在其末尾添加填充字节(trailing padding)。

指定对齐方式

可以使用#pragma pack(N)来指定结构体成员的对齐方式
对于指定的对齐方式,其成员的地址偏移以及结构的总的大小也有下面三个约束条件

  1. 结构体第一个成员的地址和结构体的首地址相同
  2. 结构体每个成员的地址偏移需要满足:N大于等于该成员的大小,那么该成员的地址偏移需满足默认对齐方式(地址偏移是其成员大小的整数倍);N小于该成员的大小,那么该成员的地址偏移是N的整数倍
  3. 结构体总的大小需要时N的整数倍,如果不是需要在结构体的末尾进行填充。
  4. 如果N大于结构体成员中最大成员的大小,则N不起作用,仍然按照默认方式对齐。

参考:

讲的很细,例子可以看

指定对齐方式例子看这个

6.class类大小计算

空类:1
没有虚函数:sizeof(数据成员)的和
有虚函数:sizeof(数据成员)的和(除静态成员外)+sizeof(Virtual表指针)即4+父类成员

若类中包含成员,则类对象的大小只包括其中非静态成员经过对齐所占的空间,对齐方式和结构体相同。


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值