尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

奇安信网络开发笔试题深度拆解:TCP/IP与编程核心考点

奇安信网络开发笔试题深度拆解:TCP/IP与编程核心考点 要说网络安全圈里那些笔试面试奇安信算是相当有代表性的一个。2019年春招那会儿网络开发岗的试题我印象挺深后来跟不少同行聊起来发现大家对这套题的评价高度一致基础、扎实、不炫技但真能考出水平差距。这篇文章我把当时的试题结构、核心考点、答题思路以及一些后来复盘才想明白的细节都整理出来。不管你是准备投奇安信还是想摸底网络安全公司对网络开发工程师的要求这份拆解都能给你一个比较完整的参考。1. 整体设计与出题思路拆解先说结论奇安信2019春招网络开发试题整体风格偏重网络底层原理和Linux环境下的实战能力对纯理论背诵几乎没有考察反而是绕开常见面经题从工程视角出发设计了不少极易暴露真实水平的题目。1.1 岗位定位与考察目标网络开发这个岗位在奇安信的体系里并不是单纯的业务后端开发而是更偏向网络协议栈、数据面处理、流量分析、高性能网关这一类的方向。这就决定了笔试题目不会去考Spring Boot或者MySQL索引优化而是把重点放在TCP/IP协议栈、Socket编程、IO模型、报文处理、内核网络参数这类离网络更近的知识上。出题人想筛出来的人不是会背八股文的而是真正在网络这一层摸爬滚打过的人。举个例子有一道题问TCP三次握手中SYN队列和Accept队列的区别如果只答出“半连接队列”和“全连接队列”这两个名词大概率只能拿一半分。要拿全分得把内核参数tcp_max_syn_backlog、somaxconn、tcp_abort_on_overflow的作用和调优经验写出来。1.2 试题结构与难度梯度整套题大概是这样的结构单选、多选、填空、简答、编程。难度不是均匀分布的而是阶梯式上升。前面的选择题主要用来快速过滤基础不扎实的候选人每道题都埋了一两个非常容易看错的坑。多选题更狠少选多选都不得分这个规则直接劝退了一大批想蒙混过关的人。简答题和编程题才是重头戏。简答题里有一道关于TCP粘包问题的处理看似是老生常谈但出题人加了一个条件要求在大流量场景下给出设计方案。这就把难度从“知道概念”拔高到了“能做架构设计”。编程题则是两道LeetCode中等难度的题但限定用C/C实现也拦住了不少习惯用Python刷题的人。1.3 为什么要这么设计实际上这套题的设计逻辑跟奇安信的业务高度相关。安全设备的核心是流量处理和协议解析比如防火墙、IDS、WAF底层全是网络数据包的处理逻辑。如果开发工程师对TCP状态机理解不透做出来的流量重组模块一定会有各种边界问题如果不理解内核协议栈的工作方式做高并发代理时就不知道怎么调优。所以出题人其实是在用试题模拟工作场景不会处理粘包的人到了真实的数据解析场景大概率会出Bug不懂TIME_WAIT状态的人做长连接服务时一定会被端口耗尽问题折磨。2. 核心知识模块与应试准备要点既然知道出题方向了那备考的核心知识模块就很清楚了。下面这些内容不管你是准备笔试还是纯粹想补网络开发的基础都值得花时间吃透。2.1 TCP/IP协议栈不只背状态图TCP三次握手、四次挥手、状态迁移图这些当然要背熟但光背不够。奇安信这类公司特别爱考两个点一是异常场景下的状态变化二是协议细节在工程中的体现。异常场景方面比如客户端断网、服务端进程崩溃、半开连接这些情况下TCP连接的状态分别怎么变很多人以为进程崩溃内核会发FIN却忽略了如果整台机器断电对端要等TCP KeepAlive超时才知道连接已经断了。协议细节方面一个典型的题是一个TCP报文的最大载荷是多少这就涉及MSS的概念。MSS MTU - IP首部 - TCP首部以太网场景下就是1460字节。但出题人往往会加一个VLAN Tag的干扰项如果没意识到802.1Q会多4字节MSS就要减到1456这就是典型的坑。还有一道关于TCP序号回绕的题。TCP序号是32位的理论上是4GB就会回绕。在高速网络上序号回绕的时间非常短这就引出了PAWSProtect Against Wrapped Sequences机制。这个知识点属于冷门但工程上处理高速流量时非常关键。2.2 网络编程IO模型是分水岭同步阻塞、同步非阻塞、IO多路复用、异步IO这几种模型的区别和适用场景几乎是必考内容。有一个高频题select、poll、epoll有什么区别为什么epoll性能更好普通的答法是select有1024个fd限制poll用链表没有限制epoll用事件驱动。但要想拿高分得从三个细节展开select和poll每次调用都要把fd集合从用户态拷贝到内核态epoll通过mmap避免了重复拷贝。select和poll是轮询所有fd复杂度是O(n)epoll是事件通知复杂度是O(1)。epoll的LT和ET模式如何选择处理ET模式时要注意什么比如必须用非阻塞IO且要循环读直到返回EAGAIN。再往深一层出题人也可能让你对比epoll和io_uring。这个如果答上来就是加分项因为io_uring是更新的异步IO框架很多高性能网络组件已经在用了。2.3 网络安全基础加解密和报文分析既然是网络安全公司加解密的基础肯定要考。对称加密、非对称加密、哈希算法的应用场景HTTPS握手的流程数字证书的校验链路这些属于基本盘。报文分析题也很有特色。给出一个Hex格式的TCP报文让你解析出源端口、目的端口、序号、确认号、标志位并且判断这是握手的哪一步。这类题不复杂但要求对TCP首部格式非常熟悉。如果你平时只用Wireshark看报文从来没手动拆过现场很容易手忙脚乱。2.4 Linux内核网络参数网络开发的隐藏考点有一道题要求简述Linux系统下如何优化高并发TCP连接涉及的文件参数包括fs.file-max系统级文件句柄上限。net.ipv4.ip_local_port_range本地端口范围默认32768到60999高并发客户端模式下很容易耗尽端口。net.ipv4.tcp_tw_reuse让处于TIME_WAIT状态的连接复用注意它跟tcp_tw_recycle的区别后者在NAT环境下有严重问题。net.core.somaxconnAccept队列上限配合listen函数的backlog参数使用。net.ipv4.tcp_max_syn_backlogSYN半连接队列上限遭受SYN Flood时这个参数作用很大。如果你只背出这些参数的名字不加任何解释分不会高。但如果能补充说明实际调优场景比如“我曾经在处理一个高并发短连接服务时因为TIME_WAIT过多导致连接建立缓慢后来通过开启tcp_tw_reuse和调整端口范围解决”这就是完全不同的评价维度。3. 实操题目参考与解题示例这一部分我挑了当时笔试里最有代表性的几道题还原题目内容给出解法和思路。这些题现在来看也依然有参考价值甚至有些点我在后来的工作中真的踩过坑验证了出题人的用心。3.1 简答题如何处理TCP粘包和拆包题目在TCP长连接通信中粘包和拆包如何产生如何处理要求给出至少两种方案并说明各自的优缺点。这道题我愿称之为“网络开发必答题”。TCP是流式协议没有消息边界如果应用层协议设计不当接收方就不知道每次应该读多少字节导致多条消息粘在一起或一条消息被拆成两次接收。正确做法分几个层面方案一定长消息。应用层协议规定每个包固定长度比如1024字节不足补零。接收方固定读1024字节解析一条消息。优点是实现简单缺点是浪费带宽短消息也要填满包。方案二特殊分隔符。比如用\r\n或者自定义的0x7E作为消息结束标志。适合文本协议比如HTTP头部就是用\r\n分隔的。缺点是消息内容里不能包含分隔符否则要做转义。方案三长度字段前置。这是最通用的方案每个包的前4字节也可以是2字节看消息最大长度存消息体长度接收方先读4字节得到长度再读对应长度的数据。HTTP的Content-Length、自定义二进制协议基本都是这么设计的。再往深了说接收方读数据时因为有半包问题需要用缓冲区把数据攒着解析出一个完整消息就处理一个。这里有两种做法使用带缓冲的IO库比如Netty的ByteBuf或者Java的ByteBuffer手动管理读写索引。自己实现一个环形缓冲区适合C/C场景避免频繁搬运数据。出题人真正想听的其实是后面这部分你设计缓冲区时怎么保证大批量数据场景下的性能。如果只是照本宣科说“加一个长度字段”那只能说明你做过但没想透彻。3.2 简答题TCP四次挥手为什么需要TIME_WAIT这道题是经典题了但出题人加了二问大量TIME_WAIT状态连接对服务端有什么影响怎么优化先答基础TIME_WAIT的作用有两个——保证最后一个ACK能到达对端如果丢了可以重发让旧连接的报文段在网络中消失防止影响新连接。再答工程大量TIME_WAIT主要是服务端主动关闭连接导致的。比如服务端设置了较短的keepalive超时频繁断开空闲连接就可能积累大量TIME_WAIT。每个TIME_WAIT连接默认持续2MSL约2分钟相当于把端口和连接资源占住了。虽然TIME_WAIT不影响服务端监听端口接受新连接但在高并发场景下如果客户端连接服务端的多个端口客户端的本地端口就会出现不够用的情况。优化方案开启net.ipv4.tcp_tw_reuse允许TIME_WAIT连接用于新的TCP连接注意是客户端场景适用。调整tcp_fin_timeout减少TIME_WAIT的等待时间但不要设得太小否则可靠性下降。如果是服务端主动断开导致的TIME_WAIT过多可以调整应用层逻辑让客户端主动断开连接。用连接池复用长连接减少频繁创建和断开。这里有个细节很多人不知道Linux系统的TIME_WAIT超时时间默认是60秒并不是理论上的2MSL120秒这是协议栈实现的折中方案。知道这个细节答题时就能体现你对实际系统行为的理解。3.3 编程题实现一个带超时的非阻塞连接题目使用C语言在Linux环境下实现一个带超时的非阻塞TCP客户端连接函数。函数原型为int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_ms); 成功返回0失败返回-1并设置errno超时返回-1且errno为ETIMEDOUT。这道题考的知识点非常多非阻塞IO、select/poll/epoll、getsockopt获取SO_ERROR等。我给一个参考实现#include sys/socket.h #include sys/select.h #include fcntl.h #include errno.h #include string.h #include stdio.h int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_ms) { int flags, ret; fd_set wfds; struct timeval tv; socklen_t len; int err 0; // 先把socket设为非阻塞 flags fcntl(sockfd, F_GETFL, 0); if (flags -1) { return -1; } if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) -1) { return -1; } ret connect(sockfd, addr, addrlen); if (ret 0) { // 连接立即成功恢复阻塞模式并返回 fcntl(sockfd, F_SETFL, flags); return 0; } if (errno ! EINPROGRESS) { fcntl(sockfd, F_SETFL, flags); return -1; } // 连接正在建立等待可写事件 FD_ZERO(wfds); FD_SET(sockfd, wfds); tv.tv_sec timeout_ms / 1000; tv.tv_usec (timeout_ms % 1000) * 1000; ret select(sockfd 1, NULL, wfds, NULL, tv); if (ret 0) { if (ret 0) { errno ETIMEDOUT; } fcntl(sockfd, F_SETFL, flags); return -1; } // 检查连接是否成功 len sizeof(err); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, err, len) -1) { fcntl(sockfd, F_SETFL, flags); return -1; } if (err ! 0) { errno err; fcntl(sockfd, F_SETFL, flags); return -1; } // 恢复原来的阻塞模式 fcntl(sockfd, F_SETFL, flags); return 0; }有几个非常容易出错的点第一connect返回EINPROGRESS后必须用select或poll等待可写事件。不能用读事件因为TCP连接建立成功对select来说是可写事件不是可读事件。第二select返回后不能直接认为连接成功了必须用getsockopt拿SO_ERROR检查错误码。因为连接可能是在select返回的同时失败的比如对端RST了。第三用完恢复socket的阻塞状态是个好习惯不然调用方后续使用这个fd时会感到困惑。这个细节虽然不影响功能正确性但能体现你的工程素养面试官问起来也有加分。3.4 编程题判断一个数是不是2的幂这道题看起来过于简单甚至有点不像奇安信的风格但它考察的是位运算的敏感度能在O(1)时间解决int is_power_of_two(unsigned int n) { return n 0 (n (n - 1)) 0; }原理很简单一个数是2的幂它的二进制表示里只有一个1。n - 1会把最低位的1变成0把后面的0全变成1两者按位与的结果就是0。这个知识点在网络开发里其实很常用。比如配置内存池大小、分页机制、哈希表的容量往往要求是2的幂就能用位运算快速实现取模a % n等价于a (n - 1)前提是n是2的幂。这个优化在某些需要高性能计算的场景下意义很大。4. 备考资源与实操经验总结笔试过了回头看我发现真正拉开差距的往往不是那些网上能搜到的经典面经题而是那些看起来不起眼、但直接对应真实工程经验的细节题。这节我把有效的备考方式和踩坑记录整理一下。4.1 高效备考路径第一把《Unix网络编程》卷一的第四、五、六章彻底吃透。这三章覆盖了Socket基础、TCP客户端服务端通信、IO复用内容就是笔试的重灾区。只看一遍不够书上的几个关键示例反射服务器、并发服务器一定要亲自敲一遍代码跑通改一改参数试试效果。第二用Wireshark抓包验证协议行为。自己写一个简单的HTTP客户端抓包看三次握手、HTTP请求响应、四次挥手的过程然后设置不同的场景比如关掉服务端再启动观察RST和重传。这比死背协议状态图有用得多因为看过的报文才记得住细节。第三动手配置Linux网络参数。找一台虚拟机按网上推荐的TCP优化参数挨个设置一遍再压测对比效果。不用追求性能数据多好看关键是理解每个参数改了之后影响了什么。第四练习编程题时务必用纯C或C。如果用Python刷题笔试现场让你用C写个链表反转你很可能因为语法不熟而出各种低级错误。C语言读写Socket、操作字节流的代码尽量达到不查文档就能写出来的熟练度。4.2 笔试中的时间分配策略整体时间大概120分钟选择题和填空题控制在40分钟以内给后面的简答和编程留足时间。我在考场上就吃过亏前面的题每一道都慢慢琢磨结果最后编程题写了一半就交卷了。建议策略是先花5分钟通读全卷标注出简答题里能快速答的题。选择题用排除法拿不准就跳不要花超过2分钟在一道选择上。简答题每道控制在10-15分钟尽量分点作答列出核心关键词就够了。编程题每道至少留20-30分钟。先写思路再写代码最后花时间自查边界条件。4.3 考场易踩的坑第一个坑是选择题的多选陷阱。很多选项看起来是对的但把数字稍微改了一点儿比如TCP首部是20字节还是24字节如果不仔细就选错了。TCP首部可选部分最长40字节所以含选项的首部最长是60字节。这个细节经常被用来出迷惑项。第二个坑是简答题答题结构。我见过不少答案内容都对但完全是散文式的没有层次。阅卷人不是来欣赏文采的建议分点、分段、先结论后展开关键术语加粗。比如答粘包问题先写“根本原因是TCP流式传输无消息边界”再写方案再写优缺点对比。第三个坑是编程题的编译错误。考场的IDE提示往往不太友好代码里如果用了非标准的头文件或者函数编译不过就是零分。建议提前把自己常用的C代码模板准备好包括头文件、错误处理宏、打印日志函数进了考场直接套用模板能节省不少时间。第四个坑是忽略边界条件。比如链表反转往往有一堆人忘记处理空链表和单节点链表字符串操作很多人忘记考虑末尾的\0。这些失分点是完全可以避免的。4.4 事后复盘与扩展方向几年过去再回头看这套题我感触最深的一点是它考察的能力跟实际工作内容高度重合。后来我做网络数据处理处理TCP流的粘包、调整内核参数优化转发性能、定位连接状态异常那些原理其实都是试卷上已经考过的东西。如果你有幸拿到了奇安信的Offer入职后可以沿着这几个方向继续深挖学习DPDK的收发报文框架理解用户态协议栈的构建思路。研究Linux内核netfilter框架了解防火墙规则匹配的实现。看一些开源项目的源码比如Nginx的事件驱动模型、Netty的Reactor模型。阅读TCP/IP协议栈相关的RFC文档特别是TCP Fast Open、BBR拥塞控制算法等。这些内容每一个都能展开成一篇长文也是从笔试合格走向真正合格的必经之路。笔试只是起点真正的挑战永远在实际业务里面对真实流量时才会发现试卷上的每个知识点都对应着生产环境中的一个真实事故。
返回列表