面试题汇总

发布于:2025-02-21 ⋅ 阅读:(123) ⋅ 点赞:(0)

1. 判断大小端问题

大端:低字节存放在高地址;  小端:低字节存放在低地址 如 : 0x12345678

bool is_little_endian() {
    unsigned int x = 1;
    return ((char*)&x)[0];
}

bool is_big_endian() {
    unsigned int x = 1;
    return !((char*)&x)[0];
}

2. 简要说明UDP数据包包含哪几个部分,画图说明也可以。

源端口号:标识发送方的端口号。

目的端口号:标识接收方的端口号。

长度:表示UDP数据包的总长度。

校验和:用于检测传输错误。

数据:携带应用程序的数据。

检验和:用于检测UDP数据包在传输过程中是否发生错误。校验和覆盖UDP头部和数据部分。

2.多任务系统环境下经常用到函数阻塞和非阻塞状态,其区别是什么?

        阻塞函数会让当前线程(或进程)处于等待状态,在等待期间,当前线程无法执行其他任务,可能会导致系统资源的浪费,如accept()没新客户端连接,会阻塞。

        非阻塞函数不会让当前任务等待,而是立即返回结果,如果没有准备好的数据或资源,非阻塞函数通常返回一个错误代码(例如 -1 或 EAGAIN)。

3. 简单写出多路复用select() 的使用流程。

// 初始化文件描述符集合

fd_set readfds;            // 新建一个文件描述符集合
FD_ZERO(&readfds);            // 把集合清空
FD_SET(server_fd, &readfds);   // 加入 某个描述符

// while 循环里调用select去处理消息

调用select 轮询监视文件描述符集合,返回有反应的描述符数量。

int activity = select(maxfd + 1, &tempfds, NULL, NULL, NULL);

用户再遍历文件描述符,处理有反应的描述符(判断是否有新描述符,有新的就加到描述符表中,并更新max_fd,否则就直接recv接收数据,如果返回0说明客户端关闭了,关掉描述符,并从表中删除)

for (int i = 0; i < max_fd + 1; ++i) {
	if (FD_ISSET(i, &bak_fds)) {
		if (i == listen_fd) {
			new_fd = accept(listen_fd, NULL, NULL);
			if (new_fd < 0) {
				perror("accept");
				break;
			}
			printf("Have a new connection!\n");
			FD_SET(new_fd, &r_fds);
			max_fd = max_fd < new_fd ? new_fd : max_fd;
		} else {
			len = recv(i, buf, sizeof(buf), 0);
			if (len < 0) {
				perror("rcv");
				FD_CLR(i, &r_fds);
				close(i);
				break;
			}
			if (len == 0) {			// 客户端端关闭
				printf("remote client closed!\n");
				FD_CLR(i, &r_fds);
				close(i);
				break;
			}
			buf[len] = 0;
			printf("msg: %s\n", buf);
		}
	}
}

4. Tcp/udp区别。谈谈增强UDP传输安全性的策略?

udp是面向数据报,无连接的,不能保证数据的顺序,可能丢包,属于不可靠的传输,速度快。

tcp是面向字节流,有链接的,三次握手,四次挥手,拥塞控制,滑动窗口等保证数据传输稳定可靠。

UDP传输安全性的策略

目前有如下的开源程序实现利用UDP实现了可靠的数据传输 :RUDP、RTP、UDT。

5. 写出socket编程相关的API

// 创建套接字
tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 参数//IPV4,TCP,默认0
udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 参数//IPV4,UDP,默认0

// IPV4地址结构
struct sockaddr_in {
    sa_family_t    sin_family; // 地址族,AF_INET
    in_port_t      sin_port;   // 端口号,网络字节序
    struct in_addr sin_addr;   // IP 地址
    unsigned char  sin_zero[8];// 填充字节,通常不用
};

// 启用端口复用
ret = setsockopt(tcp_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 将套接字绑定到本地地址和端口
ret = bind(tcp_socket, (const struct sockaddr *) &self, sizeof(self)); 

// 如果基于tcp的话,有listen()监听,accept()接收客户端连接, send,recv 发收数据 close()关闭连接
// connect()连接服务器
// 如果基于UDP ,sendto,recvfrom 收发数据

6. TCP三次握手,包头中的标志变化?

在三次握手过程中,主要涉及 SYNACK 标志。

第一次握手(SYN)客户端:发送一个 TCP 数据包,标志位设置为 SYN=1,ACK=0,序列号(Seq):随机生成的初始序列号 x,标志(Flags):SYN。作用:客户端请求建立连接

第二次握手(SYN-ACK)服务器:收到客户端的 SYN 数据包后,回复一个 TCP 数据包,标志位设置为 SYN=1 和 ACK=1。序列号(Seq):服务器随机生成的初始序列号 y,确认号(Ack):客户端序列号 x + 1,标志(Flags):SYN | ACK,服务器确认客户端的请求,并向客户端发送自己的初始序列号。

第三次握手(ACK)客户端:收到服务器的 SYN-ACK 数据包后,发送一个 TCP 数据包,标志位设置为 ACK=1。序列号(Seq):客户端的序列号 x + 1。确认号(Ack):服务器序列号 y + 1。标志(Flags):ACK。作用:客户端确认服务器的 SYN 数据包,完成连接建立。

7. TCP/IP握手建立过程

第一次,客户端向服务器发送SYN同步包请求,第二次:服务器收到请求后,回复一个SYN同步请求,加上一个ACK表示确认请求,第三次,客户端再次回复一个ACK包确认请求。

8. 产生死锁的必要条件是什么?解决死锁有几种方法?


网站公告

今日签到

点亮在社区的每一天
去签到