【C++】面试:网络编程

C++ 中高级面试、后台开发高频考点,socket、TCP/UDP、IO多路复用为核心,EPoll/_select 为面试重灾区,结合项目场景命题较多。


1. socket 编程(TCP server/client)

1.1 TCP 服务端流程

cpp 复制代码
// 1. 创建socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0);

// 2. 绑定地址
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));

// 3. 监听
listen(server_fd, 128);

// 4. 接受连接
int client_fd = accept(server_fd, NULL, NULL);

// 5. 读写数据
read(client_fd, buffer, size);
write(client_fd, buffer, size);

// 6. 关闭
close(client_fd);
close(server_fd);

1.2 TCP 客户端流程

cpp 复制代码
// 1. 创建socket
int sock = socket(AF_INET, SOCK_STREAM, 0);

// 2. 连接服务器
connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

// 3. 读写
write(sock, buffer, size);
read(sock, buffer, size);

// 4. 关闭
close(sock);

1.3 三次握手 / 四次挥手

三次握手

  1. SYN -> 服务器
  2. <- SYN+ACK
  3. ACK -> 建立连接

四次挥手

  1. FIN -> 关闭发送
  2. <- ACK
  3. <- FIN(关闭接收)
  4. ACK -> 确认关闭

1.4 面试追问

  • 为什么是三次? 双方都需要确认对方接收能力
  • 为什么是四次? 关闭是半关闭,需要分别确认

2. TCP vs UDP

2.1 核心区别

特性 TCP UDP
连接 面向连接 无连接
可靠性 可靠 不可靠
顺序 有序 乱序
开销
速度
场景 文件/邮件 视频/游戏

2.2 TCP 保证可靠

  • 序号 + 确认
  • 超时重传
  • 流量控制(滑动窗口)
  • 拥塞控制

2.3 UDP 应用

  • DNS 查询
  • DHCP
  • 视频流
  • 在线游戏

3. IO 多路复用(select/poll/epoll)

3.1 select

cpp 复制代码
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(server_fd, &readfds);

struct timeval tv = {5, 0};
int ret = select(max_fd+1, &readfds, NULL, NULL, &tv);

if (FD_ISSET(server_fd, &readfds)) {
    // 处理新连接
}

缺点

  • 最大文件描述符 1024
  • 每次都要遍历全部 fd
  • 用户态/内核态拷贝

3.2 poll

cpp 复制代码
struct pollfd fds[1024];
fds[0].fd = server_fd;
fds[0].events = POLLIN;

int ret = poll(fds, nfds, 5000);

if (fds[0].revents & POLLIN) {
    // 处理事件
}

改进

  • 无限 FD 数量(理论)
  • 数组代替位图

3.3 epoll(重点)

cpp 复制代码
int epfd = epoll_create(1);

struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = server_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &ev);

struct epoll_event events[1024];
int nfds = epoll_wait(epfd, events, 1024, -1);

for (int i = 0; i < nfds; i++) {
    if (events[i].data.fd == server_fd) {
        // 处理新连接
    }
}

三种模式

  • LT:水平触发(默认,阻塞)
  • ET:边缘触发(高效,需一次读完)
  • ONESHOT:只触发一次

3.4 对比

特性 select poll epoll
FD 数量 1024 无限制 无限制
效率 O(n) O(n) O(1)
跨平台 仅 Linux
边缘触发

4. 线程池

4.1 组件

  • 任务队列
  • 工作线程集合
  • 管理线程(分配任务)

4.2 实现模板

cpp 复制代码
class ThreadPool {
private:
    queue<Task*> tasks;
    vector<thread> workers;
    mutex mtx;
    condition_variable cv;
    bool stop;
public:
    ThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            workers.emplace_back([this] {
                while (true) {
                    Task* task = nullptr;
                    {
                        unique_lock<mutex> lock(mtx);
                        cv.wait(lock, [this] { return stop || !tasks.empty(); });
                        if (stop && tasks.empty()) return;
                        task = tasks.front();
                        tasks.pop();
                    }
                    task->execute();
                }
            });
        }
    }
    void submit(Task* t) {
        {
            lock_guard<mutex> lock(mtx);
            tasks.push(t);
        }
        cv.notify_one();
    }
    ~ThreadPool() {
        stop = true;
        cv.notify_all();
        for (auto& w : workers) w.join();
    }
};

4.3 面试参数

  • 线程数量 = CPU核数 * (1 + 等待时间/工作时间)
  • IO 密集 > CPU 密集

5. Reactor 模型

5.1 单 Reactor

复制代码
┌─────────────┐
│  Reactor   │  ← 单select/epoll监听
└─────────────┘
     ↓↑
┌──────────────┐
│  Handler    │  ← 连接处理
└──────────────┘

5.2 多 Reactor(主从)

复制代码
┌─────────────┐
│  Main      │  ← 主 Reactor(监听)
│  Reactor   │
└─────────────┘
     ↓
┌─────────────┐
│  Sub       │  ← 从 Reactor(多线程)
│  Reactor   │
└─────────────┘

5.3 组件

  • acceptor:接收连接
  • reactor:分发事件
  • handler:业务处理
  • reader/writer:读写

6. HTTP 协议

6.1 请求格式

复制代码
GET /index.html HTTP/1.1\r\n
Host: www.example.com\r\n
User-Agent: Mozilla/5.0\r\n
\r\n
(可选body)

6.2 响应格式

复制代码
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 123\r\n
\r\n
<html>...</html>

6.3 状态码

  • 1xx:信息
  • 2xx:成功(200)
  • 3xx:重定向(301/302)
  • 4xx:客户端错误(404)
  • 5xx:服务端错误(500)

6.4 常见 Header

  • Host、User-Agent
  • Content-Type、Content-Length
  • Cookie、Set-Cookie

7. 长连接与短连接

7.1 短连接

  • 每次请求建立一个 TCP 连接
  • 完成后立即关闭
  • 适合请求偶尔

7.2 长连接

  • 多个请求复用一个 TCP 连接
  • 通过 Keep-Alive 保持
  • 适合频繁请求

7.3 心跳

  • 定期发送小数据包
  • 检测连接存活
  • 防止连接被防火墙关闭

8. 粘包与拆包

8.1 问题

  • TCP 是字节流,无消息边界
  • 一次 read 可能包含多条/半条消息

8.2 解决方案

  • 固定长度:指定每个报文长度
  • 分隔符:\r\n 或自定义
  • Header + Body:Header 含 Body 长度

9. 阻塞 vs 非阻塞

9.1 四种 IO 模型

模型 阻塞 非阻塞 多路复用 信号驱动
accept 阻塞 非阻塞 select -
read 阻塞 EAGAIN select SIGIO

9.2 设置方法

cpp 复制代码
// 非阻塞
int flags = fcntql(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

9.3 异步 IO

cpp 复制代码
aio_read(&cb);

10. 常见网络攻击与防御

10.1 DDoS

  • 分布式拒绝服务
  • 防御:流量清洗、IP 限流、CDN

10.2 SYN Flood

  • 半连接队列耗尽
  • 防御:SYN Cookie、减少超时时间

10.3 CC 攻击

  • HTTP 请求耗尽资源
  • 防御:验证码、限速、IP 黑名单

🔥 本章综合高频追问

  1. :Epoll LT vs ET 区别?

    :LT 只要有数据就通知,ET 只通知一次直到buffer清空。

  2. :为什么单线程 Epoll 高效?

    :无需线程切换,纯事件驱动。

  3. :TCP 粘包如何解决?

    :定长、分隔符、Header+Body。

  4. :TIME_WAIT 过多?

    :复用 TIME_WAIT 的 sockets(SO_REUSEADDR)。


📝 模块总结

本模块覆盖网络编程核心 Socket、TCP/UDP、IO多路复用、Reactor,结合项目深入理解 Epoll、线程池。可应对中高级网络编程面试。