声网 C++ 一面 面经
1. 自我介绍,说说你的求职意向和职业规划
回答框架:
- 教育背景和技术栈
- 项目经历和技术特长
- 为什么选择声网/云平台方向
- 短期目标(1-3年):技术深度、业务理解
- 长期规划(3-5年):技术专家/架构师
2. 介绍一下你的实习项目,重点说说遇到的技术难点
回答要点:
- 项目背景和业务场景
- 你负责的模块和技术选型
- 遇到的具体问题(性能、并发、稳定性)
- 解决方案和技术手段
- 项目成果和个人收获
3. 说说你对实时音视频系统的理解,客户端和服务端的交互流程是怎样的
答案:
核心流程:
- 信令交互:客户端连接信令服务器(WebSocket/TCP)房间管理:创建、加入、离开用户状态同步:上线、下线、静音
- 媒体协商:SDP(Session Description Protocol)交换协商编解码器(H.264、VP8、Opus)协商分辨率、码率、帧率
- 媒体传输:RTP/RTCP协议传输音视频数据UDP为主,TCP作为备选服务端转发或混流
- 质量控制:带宽探测和自适应码率丢包重传(NACK)前向纠错(FEC)拥塞控制(GCC算法)
架构示例:
客户端 → 信令服务器(房间管理、状态同步)
↓
媒体服务器(SFU/MCU)
↓
其他客户端
SFU vs MCU:
- SFU(Selective Forwarding Unit):选择性转发,服务端不解码
- MCU(Multipoint Control Unit):混流,服务端解码再编码
4. 你们的服务是如何部署的?如何实现全球就近接入和负载均衡
答案:
部署架构:
- 多地域部署:北美、欧洲、亚太等
- 每个地域多个可用区
- 边缘节点:靠近用户,降低延迟
DNS负载均衡:
- GeoDNS:根据用户地理位置返回最近的IP
- 健康检查:故障节点自动摘除
- 权重分配:按服务器性能分配流量
就近接入策略:
- 智能调度:客户端上报位置信息调度服务器计算最优节点考虑因素:延迟、负载、成本
- 延迟探测:客户端ping多个节点选择延迟最低的节点定期重新探测
- 动态切换:监控连接质量质量下降时切换节点平滑迁移,不中断服务
负载均衡:
- L4负载均衡:LVS、HAProxy
- L7负载均衡:Nginx
- 一致性哈希:会话保持
优化策略:
- CDN加速:静态资源
- 专线优化:跨地域传输
- 多路径传输:提高可靠性
5. 如果全国有多个服务节点,客户端发起请求,有什么优化策略
答案:
就近接入优化:
- DNS智能解析:根据客户端IP返回最近节点缺点:DNS缓存导致不准确
- HTTP DNS:客户端直接请求调度服务器返回最优节点IP绕过运营商DNS劫持
- 延迟探测:
struct NodeInfo {
string ip;
int port;
int latency; // 延迟(ms)
};
NodeInfo selectBestNode(vector<NodeInfo>& nodes) {
// 并发ping所有节点
for (auto& node : nodes) {
node.latency = pingNode(node.ip, node.port);
}
// 选择延迟最低的
return *min_element(nodes.begin(), nodes.end(),
[](const NodeInfo& a, const NodeInfo& b) {
return a.latency < b.latency;
});
}
负载均衡优化:
- 服务端负载上报:节点定期上报CPU、内存、连接数调度服务器根据负载分配
- 客户端负载感知:服务端返回当前负载客户端选择负载低的节点
- 动态扩缩容:监控负载,自动扩容K8s HPA(Horizontal Pod Autoscaler)
容灾策略:
- 主备切换:主节点故障,切换到备节点
- 多活架构:多个节点同时服务
- 降级策略:高峰期降低服务质量
6. 说说C++的智能指针,shared_ptr是线程安全的吗(保留原题)
答案:
三种智能指针:
- unique_ptr:独占所有权
- shared_ptr:共享所有权,引用计数
- weak_ptr:不增加引用计数,打破循环引用
shared_ptr线程安全性:
- 引用计数的增减是线程安全的(atomic操作)
- 对象本身的读写不是线程安全的
- 指针本身的修改不是线程安全的
示例:
shared_ptr<int> p = make_shared<int>(42); // 线程安全:拷贝shared_ptr shared_ptr<int> p2 = p; // 引用计数原子增加 // 不安全:修改对象 *p = 100; // 多线程需要加锁 // 不安全:修改指针本身 p = make_shared<int>(200); // 多线程需要加锁
使用建议:
- 优先用unique_ptr
- 需要共享时用shared_ptr
- 注意循环引用,用weak_ptr
7. TCP和UDP的区别,分别适用于什么场景(保留原题)
答案:
区别:
连接 |
面向连接 |
无连接 |
可靠性 |
可靠(确认、重传) |
不可靠 |
顺序 |
保证顺序 |
不保证 |
速度 |
慢 |
快 |
开销 |
大(20字节头部) |
小(8字节头部) |
应用 |
HTTP、FTP、邮件 |
视频、游戏、DNS |
TCP特点:
- 三次握手建立连接
- 确认应答、超时重传
- 流量控制、拥塞控制
- 适合对可靠性要求高的场景
UDP特点:
- 无连接,直接发送
- 不保证可靠性和顺序
- 低延迟
- 适合实时性要求高的场景
应用场景:
- TCP:文件传输、网页浏览、邮件
- UDP:实时音视频、在线游戏、DNS查询、直播
实时音视频为什么用UDP:
- 低延迟优先于可靠性
- 丢包可以容忍(人眼人耳有容错)
- 应用层实现选择性重传(NACK)
8. 说说TCP三次握手的过程,为什么需要三次(保留原题)
答案:
三次握手过程:
- 客户端发送SYN,进入SYN_SENT状态
- 服务端收到SYN,回复SYN+ACK,进入SYN_RCVD状态
- 客户端收到SYN+ACK,回复ACK,进入ESTABLISHED状态
- 服务端收到ACK,进入ESTABLISHED状态
为什么需要三次:
- 确认双方收发能力:第一次:服务端确认客户端发送能力第二次:客户端确认服务端收发能力第三次:服务端确认客户端接收能力
- 防止旧连接请求:客户端发送SYN1,网络延迟客户端超时重发SYN2,建立连接延迟的SYN1到达服务端如果只有两次握手,服务端会建立连接,浪费资源三次握手,客户端不会回复ACK,连接不会建立
- 协商初始序列号:双方交换初始序列号(ISN)防止数据混乱
9. 场景题:服务端没有启动,客户端发送TCP连接请求,抓包会看到什么
答案:
抓包结果:
- 客户端发送SYN包
- 服务端回复RST(Reset)包
- 连接失败
详细过程:
- 客户端:
connect()→ 发送SYN - 服务端:端口未监听 → 内核回复RST
- 客户端:收到RST →
connect()返回错误(Connection refused)
Wireshark抓包示例:
1. 192.168.1.100 → 192.168.1.200 TCP SYN Seq=0 2. 192.168.1.200 → 192.168.1.100 TCP RST, ACK Seq=0 Ack=1
其他场景:
- 服务端主机不可达:ICMP Destination Unreachable
- 防火墙拦截:无响应,客户端超时重传
- 服务端队列满:不回复,客户端超时
10. 说说C++的拷贝构造和赋值运算符,浅拷贝和深拷贝的区别
答案:
浅拷贝:
- 只拷贝指针值,不拷贝指针指向的内容
- 多个对象共享同一块内存
- 析构时会double free
深拷贝:
- 拷贝指针指向的内容
- 每个对象有独立的内存
- 安全,但开销大
示例:
class String {
char* data;
size_t len;
public:
// 构造函数
String(const char* str) {
len = strlen(str);
data = new char[len + 1];
strcpy(data, str);
}
// 浅拷贝(默认)
// String(const String& other) : data(other.data), len(other.len) {}
// 深拷贝
String(const String& other) {
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
// 拷贝赋值
String& operator=(const String& other) {
if (this != &other) {
delete[] data; // 释放旧内存
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
return *this;
}
// 移动构造(C++11)
String(String&& other) noexcept
: data(other.data), len(other.len) {
other.data = nullptr;
ot
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++八股文全集 文章被收录于专栏
本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。
查看15道真题和解析