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三次握手,包头中的标志变化?
在三次握手过程中,主要涉及 SYN 和 ACK 标志。
第一次握手(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包确认请求。