先声明一下,文章是我自己整理的笔记,内容是张小方先生的讲稿。欢迎关注张先生主持的【高性能服务器开发】WeChat gongzhong号。
张先生的博客地址是http://blog.csdn.net/analogous_love
张小方 的知乎 Live:轻松搞定技术面试中常见的网络通信问题https://www.zhihu.com/lives/922110858308485120?utm_source=qq&utm_medium=social
本live列举的这些网络问题涵盖了技术面试中关于网络通信的80%的面试题,深入理解并掌握这些原理,不仅能帮您写出高质量的网络通信程序,同时能应付面试中绝大多数网络通信题目,助您的升职加薪一臂之力。
本次 Live 主要包括以下内容 • TCP/IP协议栈层次与三次握手、四次挥手需要知道的细节 • TCP与UDP适用场景 • linux网络模型 • epoll_event结构中epoll_data_t的fd与ptr使用场景 •Windows网络模型 •异步connect •select可以检测网络异常吗 •epoll的水平模式和边缘模式 •阻塞与非阻塞socket的设置与区别 •send/recv返回值问题 •如何编写正确的收与发数据代码 •收发缓冲区如何设计 •SO_SNDTIMEO、SO_RCVTIMEO、TCP_NODELAY、SO_REUSEADDR和SO_REUSEPORT、SO_LINGER选项 •shutdown与优雅关闭 •错误码EINTR •tcp粘包问题 •信号SIGPIPE与EPIPE错误码 •gethostbyname阻塞与错误码 •SO_KEEPALIVE选项与心跳包设计技巧 •如何设计断线重连机制 •如何清除无效的死链 •网络框架中定时器不同实现 •http协议格式、head、get与post方法细节 •http、socks4与socks5代理编码实现 •你问我答互动环节 •总结
技术面试中常见的网络通信细节问题解答
1. TCP/IP协议栈层次结构
2. TCP三次握手需要知道的细节点
3. TCP四次挥手需要知道的细节点(CLOSE_WAIT、TIME_WAIT、MSL)
4. TCP与UDP的区别与适用场景
5. linux常见网络模型详解(select、poll与epoll)
6. epoll_event结构中的epoll_data_t的fd与ptr的使用场景
7. Windows常见的网络模型详解(select、WSAEventSelect、WSAAsyncSelect)
8. Windows上的完成端口模型(IOCP)
9. 异步的connect函数如何编写
10.select函数可以检测网络异常吗?
11.你问我答环节一
12. epoll的水平模式和边缘模式
13. 如何将socket设置成非阻塞的(创建时设置与创建完成后设置),非阻塞socket与阻塞的socket在收发数据上的区别
14. send/recv(read/write)返回值大于0、等于0、小于0的区别
15.如何编写正确的收数据代码与发数据代码
16.发送数据缓冲区与接收数据缓冲区如何设计
17.socket选项SO_SNDTIMEO和SO_RCVTIMEO
18.socket选项TCP_NODELAY
19.socket选项SO_REUSEADDR和SO_REUSEPORT(Windows平台与linux平台的区别)
20.socket选项SO_LINGER
21.shutdown与优雅关闭
22.你问我答环节二
23.socket选项SO_KEEPALIVE
24.关于错误码EINTR
25.如何解决tcp粘包问题
26.信号SIGPIPE与EPIPE错误码
27.gethostbyname阻塞与错误码获取问题
28.心跳包的设计技巧(保活心跳包与业务心跳包)
29.断线重连机制如何设计
30.如何检测对端已经关闭
31.如何清除无效的死链(端与端之间的线路故障)
32.定时器的不同实现及优缺点
33.你问我答环节三
34.http协议的具体格式
35.http head、get与post方法的细节
36.http代理、socks4代理与socks5代理如何编码实现
37.ping
38.telnet
39.你问我答环节四
40.总结
--------------------------------------------------------------------------------------
技术面试中常见的网络通信细节问题解答
1. TCP/IP协议栈层次结构
2. TCP三次握手需要知道的细节点
答:1、三次握手,如果前两次有某一次失败,会重新从第一次开始,重来三次。
2、三次握手,如果最后一次失败,服务器并不会重传ack报文,而是直接发送RTS报文段,进入CLOSED状态。这样做的目的是为了防止SYN洪泛攻击。详情参见http://blog.csdn.net/libaineu2004/article/details/79020031
3、发起连接时如果发生TCP SYN丢包,那么系统默认的重试间隔是3s,这期间不会返回错误码。
4、如何模拟tcp挥手失败?答案是iptables命令可以过滤数据包,丢弃所有的连接请求,致使客户端无法得到任何ack报文。
3. TCP四次挥手需要知道的细节点(CLOSE_WAIT、TIME_WAIT、MSL)
答:http://blog.csdn.net/libaineu2004/article/details/78886213 再谈应用环境下的TIME_WAIT和CLOSE_WAIT
http://blog.csdn.net/libaineu2004/article/details/78886182 CLOSE_WAIT状态的原因与解决方法
http://blog.csdn.net/libaineu2004/article/details/78803068 TCP面试常见题:time_wait状态产生的原因,危害,如何避免
4. TCP与UDP的区别与适用场景
答:TCP协议栈本身是可靠,不会丢包,不会乱序,失败会重发。UDP需要应用层做协议来保证可靠性。视频可以用UDP。
必须使用udp的场景:广播。
5. linux常见网络模型详解(select、poll与epoll)
答:select和poll本质上没有区别,都是轮询,但是poll没有最大设备描述符数量的限制。
6. epoll_event结构中的epoll_data_t的fd与ptr的使用场景
答:在epoll模型中使用了一个struct epoll_event的结构体:
typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ };
7. Windows常见的网络模型详解(select、WSAEventSelect、WSAAsyncSelect)
8. Windows上的完成端口模型(IOCP)
9. 异步的connect函数如何编写
参考博客:http://blog.csdn.net/analogous_love/article/details/60761528
//关于tcp连接的异步connect实现流程如下: //(1)设置socket连接为非阻塞. //(2)调用connect函数.返回0表明连接成功.如果返回-1,同时errno为EINPROGRESS表明正在建立连接. //(3)使用select , epoll等 , 当描述符可写的时候检查连接状态. #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <sys/epoll.h> #include <netinet/in.h> #include <string.h> void setnonblock(int fd) { int flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_NONBLOCK); } int main(){ const char* ip = "127.0.0.1"; short port = 9999; //设置连接地址 struct sockaddr_in addr; socklen_t socklen = sizeof(struct sockaddr_in); memset(&addr , 0 , sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(ip); addr.sin_port = htons(port); //创建描述符 int fd = socket( AF_INET , SOCK_STREAM , 0); if (fd < 0){ printf("socket() error\n"); return -1; } //设置描述符为非阻塞 setnonblock(fd); //连接 int res; res = connect(fd , (struct sockaddr*)&addr , socklen); if (res == 0){ printf("connect ok(1)\n"); } else if (res == -1 && errno != EINPROGRESS){ printf("connect err(1)\n"); close(fd); return -1; } else { int epfd; //创建epoll描述符 epfd = epoll_create(1024); if ( (epfd = epoll_create(1024) ) == -1){ printf("epoll_create() err\n"); close(fd); return -1; } //添加关注事件 struct epoll_event ev; ev.events = EPOLLOUT; ev.data.fd = fd; epoll_ctl( epfd , EPOLL_CTL_ADD , fd , &ev); //编写网络程序的时候,epoll是程序的主循环.我们这里为了测试,连接上或connect超时(75秒)就break掉. //正常的流程是写一个处理connect结果的回调函数. int event_max = 1; struct epoll_event events[event_max]; int i; while (1){ res = epoll_wait( epfd , events , event_max , -1); if (res > 0){ for ( i = 0 ; i < res ; i++){ if ( events[i].data.fd == fd && ( events[i].events & EPOLLOUT) ){ //29(EPOLLOUT|EPOLLERR|EPOLLHUP) //4(EPOLLOUT) //检查是否连接成功 int optval; socklen_t optlen = sizeof(optval); int res1 = getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optlen); if ( res1 < 0 || optval){ close(fd); close(epfd); printf("connect err(2)\n"); return -1; } else { printf("connect ok(2)\n"); } } } break; } } close(fd); close(epfd); } }