大家好!😊 欢迎来到C++面试系列的第二十六期------网络篇!网络知识是面试中的高频考点,尤其对C++后端开发岗位至关重要。今天,我将继续详细拆解10个常见问题,助你轻松上岸!💡 记得收藏哦,面试前翻一翻,信心满满!🚀
一、Socket网络编程步骤
- TCP协议流程
- 服务端:创建socket → 绑定bind → 监听listen → 接受accept → 读写recv/send → 关闭close
- 客户端:创建socket → 连接connect → 读写recv/send → 关闭close
- UDP协议流程
- 服务端:创建socket → 绑定bind → 接收recvfrom
- 客户端:创建socket → 发送sendto
二、核心接口详解
| 函数 | 作用 |
|---|---|
socket() |
创建套接字 |
bind() |
绑定IP和端口 |
listen() |
设置监听队列 |
accept() |
接受客户端连接 |
connect() |
发起连接请求 |
recv()/send() |
TCP数据收发 |
recvfrom()/sendto() |
UDP数据收发 |
setsockopt() |
设置套接字选项 |
三、Socket在网络层级中的位置
socket 的精准定位:应用层 ↔ 传输层的"接口层"
socket 的核心作用是屏蔽传输层 / 网络层的底层细节,让应用层程序无需直接操作 TCP/UDP/IP 协议,只需调用 socket API 就能实现网络通信。具体定位拆解:
不属于传输层,但封装了传输层核心能力
传输层的核心是 TCP/UDP 协议,而 socket 是 "操作 TCP/UDP 的工具":
你在 socket 编程中指定SOCK_STREAM(对应 TCP)、SOCK_DGRAM(对应 UDP),本质是通过 socket 选择传输层协议;
TCP 的三次握手、四次挥手、数据重传,UDP 的数据报发送,都是操作系统通过 socket 接口帮应用层完成的,应用层无需关心传输层的底层逻辑。
不属于应用层,但为应用层提供唯一入口
应用层的程序(如你写的 C++ 服务端 / 客户端、浏览器、聊天软件)本身是应用层逻辑,但所有网络通信必须通过 socket 接口发起 ------ 没有 socket,应用层程序无法直接和传输层交互。
间接用到网络层 / 数据链路层,但仅为封装调用
socket 编程中你会指定 IP 地址(网络层)、端口(传输层),但这些参数是通过 socket 传递给底层协议栈的,socket 本身不处理 IP 路由、MAC 地址解析(ARP)等网络层 / 数据链路层工作,仅作为 "参数传递通道"。
Socket属于** 传输层(Transport Layer)**的编程接口,是对TCP/UDP协议的封装抽象
四、Socket常用类(C++)
cpp
// Linux系统常用
#include <sys/socket.h>
#include <netinet/in.h>
// Windows系统常用
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
💡 注意:C++标准库未提供原生Socket类,通常需调用操作系统API或使用第三方库(如boost::asio)
五、高并发场景优化方案
-
线程池技术
cpp// 伪代码示例 ThreadPool pool(4); // 4个工作线程 while(true) { SOCKET client = accept(server); pool.enqueue([client]{ handle_request(client); }); } -
IO多路复用(select/poll/epoll)
-
非阻塞IO+事件驱动
-
连接复用(Keep-Alive)
六、洪泛攻击(Flood Attack)
通过伪造大量虚假IP的SYN请求 耗尽服务器资源:
攻击流量∝1服务器响应时间 \text{攻击流量} \propto \frac{1}{\text{服务器响应时间}} 攻击流量∝服务器响应时间1
防御方案:
✅ SYN Cookie机制 ✅ 流量清洗 ✅ 连接数限制
七、Nagle算法
作用 :合并小数据包减少网络拥堵
触发条件 :
{待发送数据<MSS前一包未确认 \begin{cases} \text{待发送数据} < \text{MSS} \\ \text{前一包未确认} \end{cases} {待发送数据<MSS前一包未确认
禁用方法:setsockopt(sock, TCP_NODELAY, 1)
八、select/poll/epoll对比
| 机制 | 时间复杂度 | 最大连接数 | 触发方式 |
|---|---|---|---|
| select | O(n)O(n)O(n) | FD_SETSIZE | 轮询 |
| poll | O(n)O(n)O(n) | 无限制 | 轮询 |
| epoll | O(1)O(1)O(1) | 系统上限 | 回调 |
🔥 epoll的优势:
- 无需遍历所有fd
- 支持边缘触发(ET)和水平触发(LT)
- 内核事件通知机制
九、性能优化技巧
-
零拷贝技术 :
sendfile() -
内存池管理:避免频繁malloc
-
缓冲区设计 :环形缓冲区减少拷贝
cppclass RingBuffer { char* buffer; int read_pos, write_pos; }; -
SO_REUSEPORT选项:端口复用
-
避免内存拷贝 :
writev()/readv()
十、TCP服务端创建示例
cpp
#include <iostream>
#include <sys/socket.h>
int main() {
// 1. 创建socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 绑定地址
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8080);
bind(server_fd, (sockaddr*)&addr, sizeof(addr));
// 3. 监听连接
listen(server_fd, 5);
// 4. 接受请求
sockaddr_in client_addr{};
socklen_t len = sizeof(client_addr);
int client_fd = accept(server_fd, (sockaddr*)&client_addr, &len);
// 5. 数据处理...
}
十一、TCP客户端创建示例
cpp
#include <sys/socket.h>
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8080);
connect(sock, (sockaddr*)&server_addr, sizeof(server_addr));
// 发送数据...
send(sock, "Hello Server!", 13, 0);
}
💪 掌握这些核心知识点,网络面试不再慌! 觉得有用的话点个赞吧~ ✨
// 祝你面试顺利,offer拿到手软!🚀