t1. 介绍下TCP三次握手、四次挥手

(问题彩蛋):
在这之前问一下大家 当客户端和服务器建立三次握手后,客户端突然断网,这时候服务器会知道客户端掉线了嘛?然后客户端网恢复了,还需要在建立TCP的三次握手嘛? 那时候会有四次挥手嘛?

知道答案的可在评论区留言

  1. 三次握手过程理解

    (1)第一次握手:建立连接时,客户端到服务器,并进入SYN_SENT状态,等待服务器确认。

    (2)第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态。

    (3)第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

有资料需求的可以了解下,有关于包括 数据结构、底层进阶、图形视觉、音视频、架构设计、逆向安防、RxSwift、flutter,算法等方面的资料,面试资料就在群文件里面可自行下载,891点击 488进入 181
有什么问题也可以直接在里面提出来,互相交流,同时内有好友发内推机会,一起共同进步!

  1. 四次挥手过程理解

    (1)第一次挥手:客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u,此时,客户端进入FIN-WAIT-1(终止等待1)状态。

    (2)第二次挥手:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。

    (3)第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

    (4)第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB(传输控制块)后,才进入CLOSED状态。而服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

t1.在不借助临时变量的前提下,交换两个变量的值?

一般而言,我们的代码是这样的:

void swap(int& a, int& b) {
    int tmp = a;
    a = b;
    b = tmp;
}

这是因为在 a = b a = b a=b 语句执行中,只能进行这一次值的转移,转移过后变量 a a a 原来的值就丢失了。所以需要一个辅助变量 t m p tmp tmp 暂存 a a a 的值。同理,我们还可以利用临时变量暂存 a + b a + b a+b 的值,并在一次值转移后通过暂存的两数之和以及已经改变了存储值的一个数相减得到另一个数:

void swap2(int& a, int& b) {
    int tmp = a + b;
    a = b;
    b = tmp - a;
}

我们可以进一步地利用 b b b 代替 t m p tmp tmp 实现这一功能:

void swap3(int& a, int& b) {
    b = a + b; 
    // 现在 b = sum{a, b}

    a = b - a;
    // a = sum{a, b} - a = b
    // 此时 a 中存放的是原来 b 的值

    b = b - a; 
    // b = sum{a, b} - b = a
    // 此时 b 中存放的是原来 a 的值
}

加法与减法互补,所以可以通过先暂存两数之和,再通过减法达到交换的目的;而异或运算与自己互补,更加便利,且不会溢出:

void swap4(int& a, int& b) {
    b = a ^ b; 
    // 现在 b = a ^ b

    a = b ^ a;
    // a = (a ^ b) ^ a = b
    // 此时 a 中存放的是原来 b 的值

    b = b ^ a; 
    // b = (a ^ b) ^ b = a
    // 此时 b 中存放的是原来 a 的值
}

有资料需求的可以了解下,有关于包括 数据结构、底层进阶、图形视觉、音视频、架构设计、逆向安防、RxSwift、flutter,算法等方面的资料,面试资料就在群文件里面可自行下载,891点击 488进入 181
有什么问题也可以直接在里面提出来,互相交流,同时内有好友发内推机会,一起共同进步!

t1. 介绍一下一个进程的内存管理

  1. 在操作系统中,系统会给每个进程分配虚拟地址,虚拟地址的大小与处理器的位数有关,如32位处理器进程可分配4GB的虚拟内存供程序正常运行。这4GB的虚拟内存,存储单元从地址0开始进行排序,此地址为虚拟地址。

  2. 该虚拟地址可分为五个部分:

    (1)栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。线程也有自己维护的栈。

    (2)堆区:程序动态申请的空间,由程序释放或其他方式释放,若没有释放,可能导致内存泄露。

    (3)全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放 。

    (4)文字常量区:常量字符串就是放在这里的,程序结束后由系统释放。

    (5)程序代码区:存放函数体的二进制代码。

文章到这里就结束了,你也可以私信我及时获取面试相关资料。如果你有什么意见和建议欢迎给我留言。

请iOS的小伙伴关注 !喜欢的话给一个赞吧!谢谢!谢谢!谢谢!