网络编程拟面试题

1.什么是高可靠通信?

高可靠通信是指在通信过程中,数据能够无丢失、无重复、无乱序、无损坏地从发送方传递到接收方,并且能够应对网络丢包、延迟、乱序等异常情况。

2.TCP与UDP的区别有哪些?

| 特性 | TCP | UDP |
| 连接性 | 面向连接 :传输前必须通过三次握手 建立连接,结束后四次挥手释放 | 无连接:无需握手,直接发送数据包 |
| 可靠性 | 可靠 :保证数据不丢失、不重复、按序到达 | 不可靠:不保证送达、不保证顺序、丢包不重传 |
| 控制机制 | 有确认应答、超时重传、流量控制、拥塞控制 | 无任何控制机制,仅做简单校验 |
| 数据模式 | 面向字节流(无边界,连续传输) | 面向数据报(有边界,独立发包) |
| 首部开销 | 20~60 字节(变长,含序号、确认号等) | 固定 8 字节(极短,仅含端口、长度、校验和) |
| 传输效率 | 慢(机制复杂、延迟高) | 极快(开销小、无等待、实时性高) |
| 通信模式 | 仅支持单播(一对一) | 支持单播、广播、组播(一对多) |

典型应用 HTTP/HTTPS、FTP、SMTP、文件传输、远程登录 视频直播、语音通话、游戏、DNS、实时监控

TCP:可靠、慢、像打电话(先连接、再通话、确保听清)。

UDP:不可靠、快、像寄信(直接发、不管是否收到、不管顺序)。

3.简述三种超时检测的办法

1、通过setsockopt()函数设置套接字属性。设置的是fd,5秒没有获取数据则read()会返回-1报错

bash 复制代码
    struct timeval tm = {5,0};
if (setsockopt(fd, soL_SOCKET,SO_RCVTIMEO,&tm,sizeof(tm)) < 0) {
    perror("timeout");
    exit(-1);
}

2、通过select、poll、epoll多路复用机制附带的定时器完成,注意不可与设置套接字属性同时设置超时。以下为select函数举例

bash 复制代码
fd_set fds;    //定义位图
FD_ZERO(&fds);    //位图清空
FD_SET(fd, &fds);    //将需要检测的文件描述符fd插入到位图中
struct timeval tm = {5, 0}; //设置定时器 为5秒
if (select(fd + 1, &fds, NULL, NULL, &tm) == 0) {  //等于0超时,小于0报错,大于0表示fd可读
    //超时处理  注意select每次使用后都要重新更新fds与tm
}

3、通过信号定时器完成,使用setitimer()函数启动定时器,定时器到时后会触发SIGALRM信号。这里不多赘述,本质上与文件描述符无关,只是单纯的定时器。

4.什么是粘包?如何解决粘包带来的问题

粘包:TCP 是字节流协议,没有消息边界,多个独立的数据包可能被合并成一个发送,导致接收方一次读取多个消息,造成数据 "粘在一起"。

解决方法:

1.定长消息:每个消息固定长度,不足部分填充占位符。

2.特殊分隔符:在消息末尾添加约定的分隔符(如\r\n),接收方按分隔符拆分。

3.消息头 + 消息体:消息头包含消息总长度,接收方先读头再按长度读体(工业界最常用)。

注意UDP与SCTP是有边界保护,每次读取都是一个数据包的内容,不存在粘包现象

5.说出OSI七层协议模型,并简述各层的功能

  1. 物理层(Physical Layer ):负责传输比特流,定义物理连接的特性。

  2. 数据链路层(Data Link Layer):提供可靠的数据传输,处理帧的传输和错误检测。

  3. 网络层(Network Layer):负责数据包的路由和转发,实现不同网络之间的通信。

  4. 传输层(Transport Layer):提供端到端的数据传输,确保数据可靠传输,处理数据分段和重组。

  5. 会话层(Session Layer):建立、管理和终止会话连接,处理会话层的控制和同步。

  6. 表示层(Presentation Layer):处理数据的格式和编码,确保数据在不同系统间的兼容性。 7. 应用层(Application Layer):提供用户接口和网络服务,实现特定应用程序的功能

6.编写TCP服务器与客户端的代码框架(只编写函数名即可)

TCP:

服务端:

1.创建套接字(socket());

2.绑定IP、端口号(bind());

3.设置监听套接字(listen());

4.等待客户端连接(accept());

5.收发数据(read()/write()、send()/recv());

6.关闭套接字。

客户端:

1.创建套接字(socket());

2.连接服务端(connect());

3.收发数据(read()/write()、send()/recv());

4.关闭套接字(close())

7.说出TCP/IP四层协议族,以及每层有哪些协议

网络接口层:以太网、PPP、ARP、RARP

网络层:IP、ICMP、IGMP

传输层:TCP、UDP

应用层:HTTP、FTP、SMTP、DNS、Telnet、SSH

8.TCP如何判断对方异常断电?

TCP异常断电后是不会发送挥手数据包的,如果不做处理会导致对方套接字一直是开启状态,可以增加超时检测或增加心跳包功能,一段时间内未收到对方数据包则发送心跳包确认对方是否在线。

9.二层交换机的工作原理

首先二层交换机主要用于在局域网中数据的转发。两台主机可以通过连接同一个交换机进行通信。

①主机A要想与主机B通信需要先知道对方的IP地址,接着根据两方的IP地址通过子网掩码判断是否在同一个局域网内;

②若在同一个局域网,则主机A需要获取主机B的MAC地址,此时主机A会发送ARP协议广播,请求主机B的MAC地址;

③交换机收到ARP的广播后会转发给所有连接在交换机的主机上;

④主机B收到此广播后会进行回应,告知主机A自己的MAC;

⑤主机A收到回应后便会构建出完整的数据包,再次发送数据给交换机;

⑥交换机收到数据后,可以查询交换机自己的网口映射表,判断与该MAC地址相连的对应网口,将数据包转发给主机B。

10.简述任意两种并发服务器的设计,并说明其优缺点以及容易出现的问题

多进程服务器:

原理:每来一个连接,fork()一个子进程处理。

优点:每个进程独立解决通信问题,性能高,可以进行并发(进程隔离,一个连接崩溃不影响其他连接)。

缺点:进程占用空间大,对服务器内存和性能要求高,回收进程资源麻烦。(进程创建 / 切换开销大,连接数多时性能差)。

问题:僵尸进程、资源泄漏

多线程服务器:

原理:每来一个连接,创建一个线程处理。

优点:线程开销比进程小,上下文切换快。

缺点:线程共享地址空间,一个线程崩溃会导致整个进程退出;需处理同步问题。

问题:死锁、数据竞争、条件竞争

11.简述三种IO多路复用方法以及各自的特点

select: 特点:支持跨平台,监听的文件描述符数量有限(默认 1024),每次调用需遍历所有描述符,效率低。

poll: 特点:无文件描述符数量限制,但同样需要遍历,效率和 select 相近。

epoll: 特点:Linux 独有,基于事件驱动,仅返回就绪的文件描述符,支持水平 / 边缘触发,高并发场景效率极高。

12.简述五种IO模型,并说明是同步操作还是异步操作

1.阻塞 IO:同步 IO,recv()会一直阻塞直到数据到达。

2.非阻塞 IO:同步 IO,调用recv()立即返回,数据未就绪则返回错误,需轮询。

3.IO 多路复用(select/poll/epoll):同步 IO,由内核监听多个文件描述符,数据就绪后通知用户进程。

4.信号驱动 IO:同步 IO,内核数据就绪时发送信号通知进程。

5.异步 IO(AIO):异步 IO,用户进程发起 IO 请求后立即返回,内核完成 IO 后通知进程。

13.路由器的工作原理?简述数据包发送出去的流程

工作原理:路由器根据路由表,选择最佳路径转发数据包,实现不同网络间的通信。

数据包发送流程:

1.主机判断目标 IP 是否在同一网段,若不在则发送给默认网关(路由器)。

2.路由器收到数据包,根据目标 IP 查找路由表,选择下一跳。

3.将数据包转发到下一跳路由器,重复步骤 2,直到到达目标网络。

4.目标路由器将数据包转发给目标主机。

是连接两个或多个网络的硬件设备,在网络间起网关的作用,是读取每一个数据包中的地址然后决定如何传送的专用智能性的网络设备。

假设我们电脑需要访问百度服务器。在我们输入网址www.baidu.com时,①首先会先请求DNS服务器,获取百度的ip地址。②接着会通过子网掩码判断百度的ip与自己的ip是否在同一个网段,因为百度在外网,所以不在同一个网段。③那么我们主机就需要将数据包发送给网关,也就是路由器,数据包中的对方MAC地址填写为路由器的MAC地址。④路由器收到此数据包后发现该数据包要对外,先进行NAT端口映射(该功能较为复杂,这里只讲一个最简单的)。NAT端口映射将数据包的源ip与源端口映射成一个新的端口号,保存到路由器的端口映射表中,再将映射后的端口填写到源端口上,将路由器的公网ip填写到源ip上。⑤再查询路由表,找到与自己相连且距离百度服务器最近的路由器,将数据包中的目标MAC改为此路由器的MAC地址,也就是下一跳的MAC地址,源MAC改为自己路由器的MAC,将此数据包转发给下一个路由器。⑥后面的路由器也都查询自己的路由表,转发给下一个路由器,直到发送给百度服务器。⑦百度回发的数据包最终回到自己的路由器上,此时百度回的数据包目标ip写的是路由器公网ip,目标端口是映射后的端口,目标MAC是路由器的MAC地址。⑧路由器收到此数据包后查询自己的端口映射表,将目标ip改为主机的内网ip,目标端口改为主机端口,目标MAC也改为主机的MAC地址,再将此数据包发送给主机。

虽然看着写了很多文字,但依然是最简单的一种介绍,路由器本身协议的控制远比写的复杂,这里只简单介绍了路由器最核心的两个功能的使用:NAT与路由表

14.TCP三次握手与四次挥手的过程

SYN:用于建立连接,该位设为 1,表示希望建立连接,并在其序列号的字段进行序列号初值设定;

FIN:该位设为 1,表示今后不再有数据发送,希望断开连接。

ACK确认序号(acknowledgement number):占32位(4字节),表示收到的下一个报文段的第一个数据字节的序号,如果确认序号为N,序号为S,则表明到序号N-S为止的所有数据字节都已经被正确地接收到了。

SEQ序号(sequence number):字段长 32 位,占4个字节,序号的范围为 [0,4284967296]。

1.由于TCP是面向字节流的,在一个TCP连接中传送的字节流中的每一个字节都按顺序编号
2.首部中的序号字段则是指本报文段所发送的数据的第一个字节的序号,这是随机生成的。
3.序号是循环使用的,当序号增加到最大值时,下一个序号就又回到了0

x随机序号

三次握手(建立连接)

1.客户端发送 SYN 包(seq(sequence number 序号)=x)到服务器,进入 SYN_SENT 状态。

2.服务器收到 SYN,回复 SYN+ACK 包(seq=y, ack=x+1),进入 SYN_RCVD 状态。

(x: 客户端生成的随机序号
1: 客户端给服务器发送的数据的量, SYN标志位存储到某一个字节中, 因此按照一个字节计算,表示客户端给服务器发送的1个字节服务器收到了。)

3.客户端收到 SYN+ACK,回复 ACK 包(ack=y+1),双方进入 ESTABLISHED 状态。

(y:服务器端生成的随机序号
1:服务器给客户端发送的数据量,服务器给客户端发送了ACK和SYN, 都存储在这一个字节中)

四次挥手(断开连接)

4.客户端发送 FIN 包(seq=u),进入 FIN_WAIT1 状态。

5.服务器收到 FIN,回复 ACK 包(ack=u+1),进入 CLOSE_WAIT 状态,客户端进入 FIN_WAIT2 状态。

6.服务器发送完数据后,发送 FIN 包(seq=v),进入 LAST_ACK 状态。

7.客户端收到 FIN,回复 ACK 包(ack=v+1),进入 TIME_WAIT 状态,服务器收到 ACK 后进入 CLOSED 状态。

15.编写epoll多路复用TCP服务器代码(完整代码)

bash 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
 
#define MAX_EVENTS 1024
#define LISTEN_PORT 8888
 
int main() {
    int listen_fd, epoll_fd, nfds;
    struct sockaddr_in server_addr;
    struct epoll_event events[MAX_EVENTS], event;
 
    // 1. 创建监听socket
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) { perror("socket"); exit(1); }
 
    // 2. 绑定地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(LISTEN_PORT);
    if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind"); exit(1);
    }
 
    // 3. 监听
    if (listen(listen_fd, 128) < 0) { perror("listen"); exit(1); }
 
    // 4. 创建epoll实例
    epoll_fd = epoll_create1(0);
    if (epoll_fd < 0) { perror("epoll_create1"); exit(1); }
 
    // 5. 注册监听socket事件
    event.events = EPOLLIN;
    event.data.fd = listen_fd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) < 0) {
        perror("epoll_ctl add listen"); exit(1);
    }
 
    printf("Server listening on port %d...\n", LISTEN_PORT);
 
    // 6. 事件循环
    while (1) {
        nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (nfds < 0) { perror("epoll_wait"); break; }
 
        for (int i = 0; i < nfds; i++) {
            int fd = events[i].data.fd;
 
            // 处理新连接
            if (fd == listen_fd) {
                struct sockaddr_in client_addr;
                socklen_t len = sizeof(client_addr);
                int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);
                if (client_fd < 0) { perror("accept"); continue; }
 
                printf("New connection: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
 
                // 将客户端fd加入epoll
                event.events = EPOLLIN | EPOLLET; // 边缘触发
                event.data.fd = client_fd;
                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
            }
            // 处理客户端数据
            else {
                char buf[1024] = {0};
                ssize_t ret = recv(fd, buf, sizeof(buf)-1, 0);
                if (ret <= 0) {
                    // 客户端断开或错误
                    printf("Client disconnected, fd=%d\n", fd);
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
                    close(fd);
                } else {
                    printf("Recv from fd=%d: %s\n", fd, buf);
                    send(fd, buf, ret, 0); // 回显数据
                }
            }
        }
    }
 
    close(listen_fd);
    close(epoll_fd);
    return 0;
}
相关推荐
yqcoder4 小时前
HTTP 进化论:从“单车道土路”到“磁悬浮列车”
网络·网络协议·http
无限进步_4 小时前
【Linux】Makefile:让编译自动化
linux·运维·自动化
猫头虎4 小时前
【Trea】Trea国内版|国际版|海外版下载|Mac版|Windows版|Linux下载配置教程
linux·人工智能·windows·macos·aigc·ai编程·agi
techdashen4 小时前
Async Rust 近况补课:从 `async-trait` 到原生 async trait
网络·算法·rust
Yang96114 小时前
DXGF-101A:打造稳定可靠的交通通信网络
网络·信息与通信
流浪0014 小时前
告别静态打印:Linux C 实现实时刷新进度条
linux·运维·c语言
小此方4 小时前
Re:Linux系统篇(二十)进程篇·五:深入理解 Linux 进程优先级:从底层逻辑到实战修改
linux·运维·服务器
路溪非溪4 小时前
Linux下物理总线驱动模型之SDIO驱动框架
linux·驱动开发
深圳市九鼎创展科技4 小时前
九鼎创展 X7110 开发板(JH7110):国产 RISC-V 多媒体平台全解析
大数据·linux·人工智能·嵌入式硬件·ubuntu·risc-v