zoukankan      html  css  js  c++  java
  • 谈一谈我的失败的华为实习生面试

    很幸运,在华为的上机考试中一道题,也没有做,然后就去参加面试,鬼知道时怎么回事,方正比其他人幸运多了。

    但可悲的是二面没过,天哪,我知道是什么原因,我先简单谈一谈我的面试经历。

    第一面的时候,起始很随意,就考察一些基本知识,没有什么难度,然后讲一讲自己的项目经历,这些都挺简单的,过了一面,然后做了下性格测试,因为以前的性格测试不合格。

    哦,真是的,这词做完只用了二十分钟,比第一次做的时候快多了,然后性格测试也过了,旁边一哥们被性格测试刷下去了。。。。

    到了总和面的时候,确实没有准备好,因为前一天,忙着把一个设备驱动的源码分析了下,很少去复习一些基本知识,明知道三次握手,四次挥手肯定会考查,没有准备好,那我先谈一谈三次握手和四次挥手吧,挺简单的是,如果当时课上多想想,那面试的时候就不会那么尴尬了。

    三次握手:

     TCP 的连接建立:

    若A 是运行TCP客户程序的务器,而B 为运行服务器程序的服务器。两者最初的状态都是CLOSEDE,状态。

    TCP 的连接是由服务器开始的。B运行服务器程序的进程首先创建传输控制块,说白了,应该就是一大堆sock的结构体集合。首先我们关注这些状态的集合,注意

    这是用来表征连接状态的,要与sock的状态区分开来。如果分析过内核源码会发现 连接状态为 socket->sock->sk_state.

    而描述 socket 的状态集合如下:该集合用来描述 socket->state

    这也就是我们经常所说的socket 编程,这样我们就很容易理解。socket对于用户层来讲,可以直观的看到socket 状态的变化,而sock 是运行在内核中,对上层用户透明。

    好了到这儿,我们继续TCP 连接的建立。

    首先客户端和服务端都处于CLOSED 状态。首先服务器端创建socket ,我不太习惯使用套接字这个东西,简单的讲,我宁愿将他描述为一个用于通信的描述符。

    首先我们需要明白,TCP 和UDP 等协议的基本架构就是C/S 结构,简单的客户服务器模式,所以所谓的套接字编程,就是实现简单的客户服务器程序模型。

    这也就是为什么要先运行服务器端,那服务器怎么知道,是哪个客户向自己发起请求呢,好吧就是绑定端口,基于C/S 模式的通信程序都是这样做的。服务器先绑定一个端口,然后

    不断监听这个端口,来发现是否有客户端发起请求,然后进入LISTEN状态,这个端口是逻辑上的端口。若发现有客户进行请求,则立即处理该请求。

    运行客户程序的进程也会创建传输控制块,我更愿意把它称为sock ,哦,天哪,我感觉人们起的名字真奇妙。好吧,客户端会创建 socket ,然后发出主动连接请求,该请求调用函数为

    connect () 一堆参数,在写socket 的时候你肯定见过的。这时候,服务器端得有个接受函数 accept 函数,若接收到客户端的请求,服务器端的accep 函数会创建一个socket 与之通信,接下来,双方就开始通信了,客户服务器模式就是这样的,挺简单的吧,我们简单说明一下这个过程中发生了什么,三次握手到底是怎么来的,为什么是三次,不是两次一次呢,四次挥手,为什么不是三次,两次,而是四次呢,我尽量讲的简单明白,并结合内核源码加深理解吧。

    首先,我先简单的讲一讲tcp报文的格式吧 简单的说就是tcp 头部的构成,很简单,看代码。

    struct tcphdr {
        __be16    source;// 16位的源端口
        __be16    dest;// 16位的目的端口
        __be32    seq;// 表示此次发送的数据在整个报文段中的起始字节数,序列号,在建立通信时,双方使用一个随机的序列号
        __be32    ack_seq;// 期望下一次收到的第一个数据字节的序号,我们经常说的ack
    #if defined(__LITTLE_ENDIAN_BITFIELD)
        __u16    res1:4,// 保留位 两个字节
            doff:4,//tcp 头部的长度,指明在tcp 头部包含多少个32位的字。即数据部分在本地报文段开始的偏移量,因为首部的长度是可变的,所以数据偏移字段的设立是必须的
            fin:1,//用于释放一个连接,fin=1 表示欲发送的数据已发送完毕,并要求释放传输连接。
            syn:1,// 同步序号,用来发起一个连接。当syn=1 ,而ack=0,时表示这个报文是一个连接请求报文,若对方同意连接,则会在应答报文中使得syn=1,ack=1,可见 syn=1,表示该报文是一个连接请求报文还是一个连接接受报文
            rst:1,//rst=1 ,表示tcp连接中出现了重大问题,必须释放传输连接,而后再重建。该位可以用来拒绝一个非法的报文段或拒绝一个连接请求
            psh:1,//psh=1 表示请求接收端tcp 将此报文段立即送往应用层,而不是将它缓存起来直到整个缓冲区被填满后再向上交付。
            ack:1,//当ack=1 时,确认好才有意义,tcp规定所有链接建立后,在连接后所有传送的报文都必须吧ack 置为1
            urg:1,// 紧急指针,表示本报文的数据的紧急程度,urg=1 表示该报文应该具有高优先级,应尽快被发送。若接收端收到 urg=1 的报文段,他将利用紧急指针的值从报文段提取紧急数据,不再按序交给应用层需。
            ece:1,
            cwr:1;
    #elif defined(__BIG_ENDIAN_BITFIELD)
        __u16    doff:4,
            res1:4,
            cwr:1,
            ece:1,
            urg:1,
            ack:1,
            psh:1,
            rst:1,
            syn:1,
            fin:1;
    #else
    #error    "Adjust your <asm/byteorder.h> defines"
    #endif    
        __be16    window;// 窗口,用来控制流的大小。窗口值的的大小为 0-65535 ,通常由接收端确定,指的是发送报文段的一方的接收窗口大小。窗口值为0 ,表示接收端状态不佳。
        __sum16    check;// 校验和,该校验和是整个报文段,包括首部和数据。
        __be16    urg_ptr;// 紧急指针,urg=1 时才有意义,他指出了紧急数据在报文中的位置,使得接收端能知道紧急数据的字节数。
    };

    连接建立主要分为以下三步:

    1 客户进程向服务器发出连接请求的报文,调用函数 tcp_connect () 发起主动连接,会创建一个tcp 报文,其中SYN=1,同时选择一个 sn 即序列号,表明在即将传输的数据的第一个

    字节序列号为i。TCP 标准规定,对 SYN =1 的报文段要赋一个序列号,即使这个报文没有数据,此时客户端进入 SYN_SENT 状态。更准确的说是进入 TCP_SYN_SNET 状态。

    我们看一看内核源码:

    /* Build a SYN and send it off. */
    int tcp_connect(struct sock *sk)
    {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *buff;
        int err;
    
        tcp_connect_init(sk);
    
        if (unlikely(tp->repair)) {
            tcp_finish_connect(sk, NULL);
            return 0;
        }
    
        buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
        if (unlikely(!buff))
            return -ENOBUFS;
    
        tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
        tp->retrans_stamp = tcp_time_stamp;
        tcp_connect_queue_skb(sk, buff);
        tcp_ecn_send_syn(sk, buff);
    
        /* Send off SYN; include data in Fast Open. */
        err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
              tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);// 构造tcp 报文并发送
        if (err == -ECONNREFUSED)
            return err;
    
        /* We change tp->snd_nxt after the tcp_transmit_skb() call
         * in order to make this packet get counted in tcpOutSegs.
         */
        tp->snd_nxt = tp->write_seq;
        tp->pushed_seq = tp->write_seq;
        TCP_INC_STATS(sock_net(sk), TCP_MIB_ACTIVEOPENS);
    
        /* Timer for repeating the SYN until an answer. */
        inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
                      inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
        return 0;
    }

    2 服务器接收到连接请求后,如果同意连接,则回答此报文。确认报文首部中的SYN=1,ACK=1,序列号为 seq=j,ack_seq=i+1.此时服务器端进入 SYN_RECV 状态。

  • 相关阅读:
    设计模式-单件模式 实现
    设计模式-观察者模式 实现
    设计模式-策略模式 实现
    笔记- 设计模式:设计原则
    EventBus 发布/订阅 机制的 java 实现
    webService 客户端 以wsimport方式生成对应java文件
    【安装mysql】windows安装压缩版mysql5.7.15
    eclipse插件开发入门
    Java程序开发.邱加永2.1节
    Mysql常用表操作 | 单表查询
  • 原文地址:https://www.cnblogs.com/guoyu1024/p/10590407.html
Copyright © 2011-2022 走看看