20242817李臻《Linux⾼级编程实践》第8周
20242817李臻《Linux⾼级编程实践》第8周
一、AI对学习内容的总结
计算机网络概述
1. 计算机网络概述
- 计算机网络的定义:通过通信线路将地理位置不同的多台计算机连接起来,实现资源共享和信息传递。
- 网络的组成:
- 硬件:计算机、网络设备(路由器、交换机等)。
- 软件:网络操作系统、网络协议、应用软件。
- 网络协议:规定通信时信息的格式和规则,常见的协议有TCP/IP、IPX/SPX、NetBEUI等。
- 网络协议分层:
- 物理层:硬件设备的电气特性。
- 数据链路层:数据传输的规程。
- 网络层:IP协议,负责数据包的转发。
- 传输层:TCP和UDP,负责端到端的连接。
- 应用层:定义应用程序的使用规程。
2. TCP/IP协议
- 协议分层模型:
- 应用层:定义应用程序的网络使用规则。
- 传输层:TCP(面向连接、可靠)和UDP(无连接、不可靠)。
- 网络层:IP协议,负责数据包的转发。
- 数据链路层:以太网协议,负责数据帧的传输。
- 物理层:硬件设备的通信。
- TCP和UDP的区别:
- TCP:面向连接、可靠传输,适用于需要高可靠性的场景。
- UDP:无连接、不可靠传输,适用于简单的请求-响应通信和网络条件较好的场景。
3. Client/Server模型
- C/S模型:客户端负责与用户交互,服务器端负责处理请求并返回结果。
- 应用场景:大多数网络应用(如QQ、邮箱服务)都基于C/S模型。
4. Linux网络编程概述
- Linux网络功能:支持多种互联网协议(如TCP/IP),在服务器领域广泛应用。
- 网络协议栈:数据在内核中的处理流程,包括应用层、协议层和接口层。
5. Socket编程
- Socket的定义:一种通信机制,用于跨网络、跨操作系统的进程间通信。
- Socket的类型:
- SOCK_STREAM(TCP):面向连接、可靠传输。
- SOCK_DGRAM(UDP):无连接、不可靠传输。
- SOCK_RAW:原始套接字,允许直接构造协议头部。
- Socket编程的基本系统调用:
socket()
:创建套接字。bind()
:绑定套接字到本地地址和端口。listen()
:监听套接字。accept()
:接受客户端连接请求。connect()
:向服务器发送连接请求。send()
/recv()
:发送和接收数据。close()
:关闭套接字。
- 网络字节序与主机字节序:
- 网络字节序:大端模式。
- 主机字节序:小端或大端模式。
- 转换函数:
htonl()
、htons()
、ntohl()
、ntohs()
。
- 网络地址转换函数:
inet_addr()
:将点分十进制IP地址转换为二进制。inet_ntoa()
:将二进制IP地址转换为点分十进制。
6. Socket编程实例
- TCP服务器和客户端:
- 服务器:监听端口,接受连接请求,处理数据。
- 客户端:连接服务器,发送和接收数据。
- UDP服务器和客户端:
- 服务器:接收数据并返回处理结果。
- 客户端:发送数据并接收服务器的响应。
- 原始套接字:
- 用于直接构造协议头部,监听网络数据包。
- 应用:网络诊断工具(如ping、traceroute)。
7. 高级I/O模型
- 阻塞与非阻塞Socket:
- 阻塞模式:调用会等待操作完成。
- 非阻塞模式:调用立即返回,需轮询检查结果。
- 多路复用:
select()
:轮询文件描述符,检测是否可读写。poll()
:与select()
类似,但更高效。epoll()
:Linux内核提供的高效I/O事件处理机制,适用于大量连接。
8. 并发编程
- 多进程模型:
- 父进程监听连接请求,子进程处理客户端数据。
- 优点:提高并发性,响应时间快。
- 缺点:进程管理开销较大。
- 多线程模型:
- 单进程内创建多个线程处理客户端请求。
- 优点:线程间共享内存,通信效率高。
- 缺点:线程同步复杂。
9. 实例代码
- TCP服务器和客户端:
- 服务器接收客户端发送的整型数据,加1后返回。
- 客户端发送数据并接收服务器的响应。
- UDP服务器和客户端:
- 服务器接收数据并返回处理结果。
- 客户端发送数据并接收服务器的响应。
- 原始套接字:
- 监听网络数据包,打印IP和TCP头部信息。
10. 总结
- 网络编程的核心:Socket编程、协议选择(TCP/UDP)、阻塞与非阻塞模式、并发模型。
- 高级I/O模型:
select()
、poll()
、epoll()
,适用于不同场景。 - 并发编程:多进程和多线程模型,提高服务器性能。
二、 对AI总结的反思与补充
反思
1. 网络协议分层的表述问题
原文将TCP/IP模型划分为五层(包含物理层),但标准的TCP/IP四层模型不单独定义物理层。应明确说明:
- 应用层(HTTP/FTP)
- 传输层(TCP/UDP)
- 网络层(IP/ICMP)
- 网络接口层(以太网/WiFi)
建议增加OSI七层模型对比说明,避免与混合模型混淆。
2. 过时协议引用
原文列举的IPX/SPX、NetBEUI属于已淘汰协议。现代网络应聚焦:
- IPv4/IPv6协议族
- QUIC(HTTP/3底层协议)
- TLS/SSL安全协议
3. TCP服务器并发处理缺失
示例代码未体现多客户端处理,可能导致阻塞。需补充:
// 多线程处理示例
while(1) {int client_fd = accept(...);pthread_t tid;pthread_create(&tid, NULL, handle_client, &client_fd);pthread_detach(tid); // 避免僵尸线程
}
补充
1. IPv6编程扩展
- 双栈支持:通过
AI_V4MAPPED
标志同时支持IPv4/IPv6struct addrinfo hints = {0}; hints.ai_family = AF_INET6; hints.ai_flags = AI_V4MAPPED;
- 流式套接字差异:
struct sockaddr_in6 server_addr; server_addr.sin6_flowinfo = 0; // 流量标识必须显式初始化
2. 现代I/O框架
- epoll的触发模式:
- 水平触发(LT):数据未处理会重复通知
- 边缘触发(ET):只在状态变化时通知,需搭配非阻塞IO
struct epoll_event ev; ev.events = EPOLLIN | EPOLLET; // 启用ET模式
- 零拷贝技术:
sendfile(out_fd, in_fd, &offset, count); // 内核直接传输文件数据
3. 网络诊断增强
- 原始套接字权限管理:
sudo setcap cap_net_raw+ep ./program # 避免root运行
- BPF过滤器:
struct sock_filter code[] = {BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12), // 加载以太网类型字段BPF_JUMP(BPF_JMP|BPF_JEQ, 0x0800, 0, 1), // 匹配IPv4BPF_STMT(BPF_RET|BPF_K, 0xFFFF), // 允许通过BPF_STMT(BPF_RET|BPF_K, 0) // 拒绝其他 };
三、学习思维导图
第九章
mindmaproot((Linux网络编程))计算机网络概述计算机网络组成及特点资源共享自治计算机网络协议网络协议TCP/IP协议IPX/SPX协议NetBEUI协议网络协议分层物理层数据链路层互联网层传输层应用层TCP/IP协议TCP协议UDP协议IP地址Client/Server模型客户端服务器端Linux网络编程概述支持多种协议网络功能丰富网络协议栈协议栈架构数据传输流程Socket编程Socket概述通信机制由IP地址和端口描述Socket编程基本系统调用socketbindlistenacceptconnectsend/recvcloseSocket类型SOCK_STREAMSOCK_DGRAMSOCK_RAWSocket流服务TCP协议服务器端和客户端模型Socket数据包服务UDP协议服务器端和客户端模型原始套接字服务数据链路层原始套接字网络层原始套接字网络编程高级I/O阻塞与非阻塞模式阻塞模式非阻塞模式select/poll/epoll多路复用selectpollepoll非阻塞Socket用法非阻塞连接非阻塞数据发送非阻塞数据读取非阻塞接收连接网络并发编程基于多进程的网络编程父进程子进程基于多线程的网络编程线程池事件处理并发模型的优缺点多进程模型多线程模型select/poll/epoll模型
四、基于AI的学习
1、关于TCP/IP协议
-
我:我在学习LinuxTCP/IP协议知识点,请你以苏格拉底的方式对我进行提问,一次一个问题
-
AI:好的,我们开始吧。那么,请你先告诉我,你目前对TCP/IP协议有什么初步的理解呢?
-
我:TCP/IP协议是互联网的基础协议,它由传输控制协议(TCP)和互联网协议(IP)组成,用于在网络中传输数据。TCP负责可靠的数据传输,IP负责数据包的路由和寻址。
-
AI:很好,那么请你思考一下,TCP/IP协议的主要功能是什么?
-
我:TCP/IP协议的主要功能是实现不同网络之间的互联互通,确保数据能够在复杂的网络环境中可靠、高效地传输。它通过分层架构来实现这一目标,每一层负责不同的功能,比如IP层负责寻址和路由,TCP层负责可靠传输。
-
AI:非常棒!那么,你认为TCP/IP协议的分层架构是如何工作的呢?
-
我:TCP/IP协议采用分层架构,通常分为四层:应用层、传输层、互联网层和网络接口层。数据从应用层开始,逐层向下封装,最终通过网络接口层发送到物理网络;接收时则相反,数据逐层向上解封装,最终到达应用层。
-
AI:很好,那么请你思考一下,TCP和UDP协议的主要区别是什么?
-
我:TCP是一种面向连接的、可靠的传输协议,它通过三次握手建立连接,确保数据按顺序、无差错地传输。UDP是一种无连接的、不可靠的传输协议,它不保证数据的可靠传输,但传输速度快,适用于对实时性要求较高的应用。
-
AI:非常棒!那么,你认为在实际应用中,如何选择使用TCP协议还是UDP协议呢?
-
我:选择TCP还是UDP主要取决于应用的需求。如果应用对数据的可靠性要求很高,比如文件传输、网页浏览等,通常选择TCP;如果应用对实时性要求很高,比如视频流、语音通话等,通常选择UDP。
-
AI:非常棒!那么,你认为在实际开发中,使用TCP/IP协议时,需要特别注意哪些潜在问题呢?
- 我:你的理解是什么?
- AI:在实际开发中,使用TCP/IP协议时,需要特别注意以下潜在问题:
- 网络延迟和丢包:TCP/IP协议在网络层和传输层都有机制来处理丢包和延迟,但这些机制可能会导致额外的延迟。例如,TCP的重传机制可能会导致数据传输时间延长。
- 连接管理:TCP是一种面向连接的协议,需要正确管理连接的建立和关闭。如果连接没有正确关闭,可能会导致资源泄漏。
- 安全性问题:TCP/IP协议本身并不提供加密机制,因此在网络传输中数据可能会被窃取或篡改。需要结合其他安全协议(如TLS/SSL)来保护数据。
- 协议兼容性:不同的操作系统和网络设备可能对TCP/IP协议的实现存在细微差异,这可能导致兼容性问题。
- 性能优化:在高负载情况下,TCP/IP协议的性能可能会受到影响。需要通过优化网络配置、调整协议参数等方式来提高性能。
- 我:针对我该知识点,我理解了吗?我的回答结束了,请对我的回答进行评价总结。
- AI:好的,让我们来总结一下你对TCP/IP协议知识点的理解。
从你的回答来看,你对TCP/IP协议的基本概念、主要功能、分层架构、TCP和UDP的区别,以及实际应用中的选择和潜在问题都有较为清晰的认识。你能够准确地描述TCP/IP协议的工作原理和应用场景,并且能够结合实际开发场景思考可能遇到的问题,这表明你对这个知识点有较为深入的理解。
不过,还可以进一步完善的地方是,对于TCP/IP协议的详细实现机制(如TCP的拥塞控制算法、IP的路由选择算法等)可以再深入研究一下。此外,对于TCP/IP协议的安全性问题(如常见的攻击类型和防御机制)也可以进一步探讨。
总体来说,你对TCP/IP协议的知识点已经有了很好的掌握,继续保持这种学习态度,你会在这个领域取得更大的进步!
2、关于多路复用
-
我:我在学习Linux多路复用知识点,请你以苏格拉底的方式对我进行提问,一次一个问题
-
AI:好的,我们开始吧。那么,请你先告诉我,你目前对多路复用(I/O多路复用)有什么初步的理解呢?
-
我:多路复用是一种允许程序同时管理多个I/O操作的技术,它可以让单个进程同时监视多个文件描述符(如套接字),并根据哪个文件描述符准备好进行读写操作来执行相应的处理。
-
AI:很好,那么请你思考一下,多路复用的主要应用场景是什么?
-
我:多路复用主要用于需要同时处理多个网络连接或I/O操作的场景,比如服务器程序需要同时监听多个客户端的请求,或者同时处理多个文件的读写操作。
-
AI:非常棒!那么,你认为多路复用有哪些主要的实现方式呢?
-
我:多路复用主要有三种实现方式:
select
、poll
和epoll
。select
通过轮询文件描述符集合来检测是否有I/O事件发生;poll
通过数组来管理文件描述符并检测事件;epoll
则通过内核提供的事件通知机制来高效地处理大量文件描述符。 -
AI:很好,那么请你思考一下,
select
、poll
和epoll
的主要区别是什么?
-
我:
select
和poll
都是通过轮询的方式来检测文件描述符的状态,但poll
没有最大文件描述符数量的限制,而select
有。epoll
则是基于事件通知机制,它不需要轮询,效率更高,尤其是在处理大量文件描述符时。 -
AI:非常棒!那么,你认为在实际开发中,如何选择使用
select
、poll
还是epoll
呢?
-
我:在实际开发中,如果需要处理的文件描述符数量较少,
select
和poll
都可以使用。但如果需要处理大量的文件描述符,或者对性能要求较高,epoll
是更好的选择,因为它可以显著提高效率。 -
AI:非常棒!那么,你认为在实际开发中,使用多路复用技术时,需要特别注意哪些潜在问题呢?
- 我:你的理解是什么?
- AI:在实际开发中,使用多路复用技术时,需要特别注意以下潜在问题:
- 资源限制:虽然
poll
和epoll
没有文件描述符数量的限制,但系统资源(如文件描述符表的大小)仍然是有限的。需要合理管理文件描述符,避免资源耗尽。 - 性能问题:虽然
epoll
的性能优于select
和poll
,但在某些情况下,如果文件描述符的活跃度较低,epoll
的性能优势可能不明显。需要根据实际场景选择合适的多路复用机制。 - 事件处理逻辑:多路复用需要处理多种事件(如读事件、写事件、错误事件等),需要设计合理的事件处理逻辑,避免事件处理过程中的死锁或竞态条件。
- 兼容性问题:不同的操作系统和库对多路复用的支持可能有所不同,需要确保代码在目标平台上具有良好的兼容性。
- 复杂性管理:多路复用的代码逻辑相对复杂,需要合理设计程序结构,避免代码难以维护。
- 我:针对我该知识点,我理解了吗?我的回答结束了,请对我的回答进行评价总结。
- AI:好的,让我们来总结一下你对多路复用知识点的理解。
从你的回答来看,你对多路复用的基本概念、主要应用场景、实现方式(select
、poll
、epoll
),以及它们的区别和实际开发中的选择和潜在问题都有较为清晰的认识。你能够准确地描述多路复用的工作原理和应用场景,并且能够结合实际开发场景思考可能遇到的问题,这表明你对这个知识点有较为深入的理解。
不过,还可以进一步完善的地方是,对于多路复用的具体实现细节(如 epoll
的内核实现机制、select
和 poll
的性能瓶颈等)可以再深入研究一下。此外,对于多路复用在不同场景下的优化策略(如动态调整监听的文件描述符集合)也可以进一步探讨。
总体来说,你对多路复用的知识点已经有了很好的掌握,继续保持这种学习态度,你会在这个领域取得更大的进步!
五、基于AI的代码实践
使用Gitee进行代码托管
仓库链接:
https://gitee.com/li-zhen1215/homework/tree/master/Week8
例1
test.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>void main(int argc, char *argv[]) {char ipbuf[20]; // save ip addressunsigned int ipint; // ip address 的整数形式struct in_addr addrstr;char *ch=NULL;while (1) {printf("please input ip address\n");fgets(ipbuf, 20, stdin);if (!strcmp(ipbuf, "exit\n")) break;ipbuf[strlen(ipbuf)-1] = '0'; // 去掉 fgets 输入的 \nipint = inet_addr(ipbuf);printf("The binary of ip address is %s\n", ipbuf);printf("The host byte of binary ip address is %u\n", inet_network(ipbuf));inet_aton(ipbuf, &addrstr);ch = (char *)&addrstr;printf("The inet_aton result is %x\n", addrstr.s_addr);printf("The network byte of ip address is %x.%x.%x.%x\n", (*ch)&0xff, *(ch+1)&0xff, *(ch+2)&0xff, *(ch+3)&0xff);printf("The integer value of ip address is %u\n", ipint);}
}
实践截图
例2
tcpservber.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define PORT 8888int main() {int sock, new_sock;struct sockaddr_in my_addr, client_addr;socklen_t len;char buf[100];char buf2[128];int recdata=0;if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket create error\n");exit(1);}memset(&my_addr, 0, sizeof(my_addr));my_addr.sin_family = AF_INET;my_addr.sin_port = htons(PORT);my_addr.sin_addr.s_addr = INADDR_ANY;if (bind(sock, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1) {perror("bind error\n");exit(1);}if (listen(sock, 5) < 0) {perror("listen error\n");exit(1);}while (1) {len = sizeof(struct sockaddr);if ((new_sock = accept(sock, (struct sockaddr *)&client_addr, &len)) < 0) {perror("accept error\n");exit(1);} else {printf("server get connection from ip:port %s:%d socket id %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), new_sock);}len = recv(new_sock, buf, 100, 0);if (len < 0) {printf("recv error\n");exit(1);} else if (len == 0) {printf("the client quit\n");break;} else {buf[len] = '\0';printf("receive message is %s\n", buf);recdata = atoi(buf);recdata++;sprintf(buf2, "%d", recdata);if (send(new_sock, buf2, strlen(buf2), 0) < 0) {perror("send data failed\n");}}close(sock);close(new_sock);}
}
tcpclient.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8888int main(int argc, char **argv) {int sock;struct sockaddr_in my_addr;int len;char buf[100];char recbuf[100];if (argc < 2) {printf("Usage: %s <ip>\n", argv[0]);exit(1);}if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket create error\n");exit(1);}my_addr.sin_family = AF_INET;my_addr.sin_port = htons(8888);if (inet_aton(argv[1], (struct in_addr *)&my_addr.sin_addr.s_addr) == 0) {perror("change error\n");exit(1);}if (connect(sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0) {printf("connect error\n");exit(1);}printf("connected\n");printf("Input data to send\n ");fgets(buf, 100, stdin);len = send(sock, buf, strlen(buf) - 1, 0);if (len < 0) {perror("send error\n");exit(1);}len = recv(sock, recbuf, 100, 0);recbuf[len] = '\0';if (len < 0) {perror("recv error\n");exit(1);}printf("the received data from server is %s\n", recbuf);close(sock);
}
实践截图:
例3
udpserver.c
/*udpserver.c*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> // 添加这一行
int main(int argc, char **argv){struct sockaddr_in s_addr,c_addr;int sock;socklen_t addr_len;int len;char buf[128];char buf2[128];int recdata=0;if ((sock = socket(AF_INET, SOCK_DGRAM,'0')) == -1) {perror("socket");exit(1);} elseprintf("create socket connected.\n");memset(&s_addr, 0, sizeof(struct sockaddr_in));s_addr.sin_family = AF_INET;s_addr.sin_port = htons(8888);s_addr.sin_addr.s_addr = INADDR_ANY;if (bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr)) == -1) {perror("bind failed\n");exit(1);} elseprintf("bind address to socket.\n");addr_len = sizeof(c_addr);while (1) {memset(buf,0,sizeof(buf));len = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &c_addr, &addr_len);if (len < 0) {perror("recvfrom error.\n");exit(errno);}buf[len] = '\0';printf("recive come from %s:%d message:%s\n\r",inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port), buf);recdata=atoi(buf);recdata++;sprintf(buf2,"%d",recdata);sendto(sock, buf2, strlen(buf2),0, (struct sockaddr *) &c_addr, addr_len);}close(sock); // 这里使用 close 函数return 0;
}
udpclient.c
/*udpclient.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> // 添加这一行
int main(int argc, char **argv){int sock;int addr_len;int len;char buff[128];struct sockaddr_in s_addr;if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {perror("socket create failed");exit(1);} elseprintf("socket create successful.\n");s_addr.sin_family = AF_INET;s_addr.sin_port = htons(8888);memset(s_addr.sin_zero, '\0', sizeof(s_addr.sin_zero));if (argc>2){s_addr.sin_addr.s_addr = inet_addr(argv[1]);strcpy(buff,argv[2]);} else {printf("input server ip and parameter!\n");exit(0);}addr_len = sizeof(s_addr);len = sendto(sock, buff, strlen(buff), 0, (struct sockaddr *) &s_addr, addr_len);if (len < 0) {perror("send error.\n");exit(1);}sleep(1); // 这里使用 sleep 函数len = recvfrom(sock, buff, sizeof(buff) - 1, 0, (struct sockaddr *) &s_addr, &addr_len);if (len < 0) {perror("recvfrom error.\n");exit(1);}printf("receive from server %s\n",buff);close(sock); // 这里使用 close 函数return 0;
}
实践截图:
例4
sk.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <unistd.h>int Open_Raw_Socket(void);
int Set_Promisc(char *interface, int sock);int main() {int sock;sock = Open_Raw_Socket();printf("raw socket is %d\n", sock);char buffer[65535];int bytes_received;socklen_t fromlen;struct sockaddr_in from;struct iphdr *iphdr;struct tcphdr *tcphdr;Set_Promisc("eth0", sock);printf("IP header is %zu \n", sizeof(struct iphdr));printf("TCP header is %zu \n", sizeof(struct tcphdr));while (1) {fromlen = sizeof(from);bytes_received = recvfrom(sock, buffer, sizeof(buffer),0, (struct sockaddr*)&from, &fromlen);printf("\nBytes received: %d\n", bytes_received);printf("Source address: %s\n", inet_ntoa(from.sin_addr));iphdr = (struct iphdr*)buffer;if (iphdr->protocol == IPPROTO_TCP) {printf("Dest address: %s\n", inet_ntoa((struct in_addr){iphdr->daddr}));printf("IP Header Length: %d bytes\n", iphdr->ihl * 4);printf("Protocol: %d\n", iphdr->protocol);printf("Type of Service: %d\n", iphdr->tos);printf("Time to Live: %d\n", iphdr->ttl);printf("Checksum: %d\n", ntohs(iphdr->check));int ip_header_len = iphdr->ihl * 4;tcphdr = (struct tcphdr*)(buffer + ip_header_len);printf("Dest port: %d\n", ntohs(tcphdr->dest));printf("Source port: %d\n", ntohs(tcphdr->source));printf("Seq number: %u\n", ntohl(tcphdr->seq));printf("Ack number: %u\n", ntohl(tcphdr->ack_seq));// 组合TCP标志位u_int8_t flags = ((tcphdr->urg << 5) | (tcphdr->ack << 4) | (tcphdr->psh << 3) | (tcphdr->rst << 2) | (tcphdr->syn << 1) | tcphdr->fin);printf("Flags: 0x%02x (URG:%d ACK:%d PSH:%d RST:%d SYN:%d FIN:%d)\n",flags,tcphdr->urg, tcphdr->ack, tcphdr->psh,tcphdr->rst, tcphdr->syn, tcphdr->fin);}}close(sock);return 0;
}int Open_Raw_Socket(void) {int sock;if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {perror("socket");exit(EXIT_FAILURE);}return sock;
}int Set_Promisc(char *interface, int sock) {struct ifreq ifr;strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1);ifr.ifr_name[IFNAMSIZ - 1] = '\0';if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {perror("ioctl SIOCGIFFLAGS");close(sock);exit(EXIT_FAILURE);}ifr.ifr_flags |= IFF_PROMISC;if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) {perror("ioctl SIOCSIFFLAGS");close(sock);exit(EXIT_FAILURE);}return 0;
}
实践截图:
例5
test.c
#include <sys/types.h>
#include <sys/socket.h>
#include <error.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <errno.h>void main(int argc, char** argv) {int sock;struct sockaddr_in my_addr;int asynflag=1;time_t t1,t2;if((sock=socket(AF_INET,SOCK_STREAM,0))<0){perror("socket create error!\n");exit(1);}my_addr.sin_family=AF_INET;my_addr.sin_port=htons(8888);if(inet_aton(argv[1],(struct in_addr *)&my_addr.sin_addr.s_addr) == 0){printf("%s chage error!\n",argv[1]);exit(1);}t1=time(NULL);/*ioctl (sock, FIONBIO, &asynflag);*/if(connect(sock,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0){if (errno!=EINPROGRESS) {perror("connect error!\n");}}t2=time(NULL);printf("connect used time is %d second\n",(int)(t2-t1));close(sock);
}
实践截图
例7
a.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h> // 包含 poll.h 头文件#define MAXLINE 10240
#ifndef OPEN_MAX
#define OPEN_MAX 40960
#endifvoid handle(struct pollfd* clients, int maxClient, int readyClient);int main(int argc, char **argv) {int servPort = 6888;int listening = 1024;int listenfd, connfd;struct pollfd clients[OPEN_MAX];int maxi;socklen_t socklen = sizeof(struct sockaddr_in);struct sockaddr_in cliaddr, servaddr;char buf[MAXLINE];int ready;int opt = 1;int nready; // 声明 nready 变量if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket error");exit(-1);}if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {perror("setsockopt error");exit(-1);}memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(servPort);if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1) {perror("bind error");exit(-1);}if (listen(listenfd, listening) < 0) {perror("listen error");exit(-1);}clients[0].fd = listenfd;clients[0].events = POLLIN;int i;for (i = 1; i < OPEN_MAX; i++) {clients[i].fd = -1;clients[i].events = 0;}maxi = listenfd + 1;printf("pollechoserver startup, listen on port:%d\n", servPort);printf("max connection is %d\n", OPEN_MAX);for (;;) {ready = poll(clients, maxi + 1, -1);if (ready == -1) {perror("poll error");continue;}for (i = 0; i < maxi; i++) {if (clients[i].fd == -1) continue;if (clients[i].revents & POLLIN) {if (i == 0) { // listenfdconnfd = accept(listenfd, (struct sockaddr *) &cliaddr, &socklen);if (connfd < 0) {perror("accept error");continue;}if (connfd >= OPEN_MAX) {printf("too many connection, more than %d\n", OPEN_MAX);close(connfd);continue;}clients[i].fd = connfd;clients[i].events = POLLIN;} else { // clientfdnready = read(clients[i].fd, buf, MAXLINE);if (nready < 0) {perror("read error");close(clients[i].fd);clients[i].fd = -1;continue;}if (nready == 0) {printf("client close the connection\n");close(clients[i].fd);clients[i].fd = -1;continue;}buf[nready] = '\0';printf("Server received data is %s\n", buf);}}}}
}void handle(struct pollfd* clients, int maxClient, int readyClient) {int connfd;int i, nread;char buf[MAXLINE];if (readyClient == 0)return;for (i = 1; i < maxClient; i++) {connfd = clients[i].fd;if (connfd == -1)continue;if (clients[i].revents & POLLIN) {nread = read(connfd, buf, MAXLINE);if (nread < 0) {perror("read error");close(connfd);clients[i].fd = -1;continue;}if (nread == 0) {printf("client close the connection\n");close(connfd);clients[i].fd = -1;continue;}buf[nread] = '\0';printf("Server received data is %s\n", buf);}}
}
实践截图:
习题1
使用socket()函数创建一个套接字,该函数需要指定地址族(如AF_INET)、套接字类型(如SOCK_STREAM或SOCK_DGRAM)以及协议(如IPPROTO_TCP或IPPROTO_UDP)。接着,通过setsockopt()函数设置套接字的选项,例如允许端口重用(SO_REUSEADDR)。然后,使用bind()函数将套接字绑定到特定的IP地址和端口上,以便监听和接受连接请求。
对于服务器端,调用listen()函数使套接字进入监听状态,准备接受客户端的连接。当有客户端请求连接时,服务器使用accept()函数接受连接,该函数返回一个新的套接字用于与客户端进行数据交换。
数据传输过程中,客户端和服务器端可以使用send()和recv()函数来发送和接收数据。这些函数可以指定发送或接收的数据量,并处理网络字节序的转换。
最后,完成数据交换后,使用close()函数关闭套接字,释放相关资源。在整个过程中,需要对每个函数调用进行错误检查,以确保网络通信的稳定性和可靠性。此外,还可以使用poll()或select()等函数来管理多个套接字,实现非阻塞或多路复用的网络编程模型。
习题2
TCP和UDP是两种主要的传输层协议,它们在可靠性和连接方式上有本质区别。TCP是面向连接的可靠协议,通过三次握手建立连接,提供数据确认、重传和流量控制机制,确保数据有序到达,编程时需要先建立连接再传输数据;而UDP是无连接的不可靠协议,直接发送数据报,不保证顺序和到达,但开销小速度快,编程时直接发送接收数据包无需连接。TCP适用于文件传输、网页浏览等需要可靠性的场景,UDP适合视频流、实时游戏等对延迟敏感的应用。
习题3
IP地址10.20.30.40的二进制形式是00001010.00010100.00011110.00101000;主机字节序在不同系统中可能不同(小端序为0x281e140a,大端序为0x0a141e28);网络字节序(大端序)固定为0x0a141e28;网络地址和主机地址的划分取决于子网掩码,若使用默认的C类掩码255.255.255.0,则网络地址是10.20.30.0,主机地址是0.0.0.40。
习题4
select调用和多进程/线程实现是两种不同的并发处理方式。select属于I/O多路复用技术,通过单进程监控多个文件描述符的状态变化,适合处理大量并发连接但计算量小的场景,优点是资源占用少、避免进程切换开销,缺点是无法充分利用多核CPU且编程复杂度高。多进程/线程则为每个连接创建独立执行单元,能充分利用多核并行处理,适合计算密集型任务,优点是编程模型简单、隔离性好,缺点是进程/线程创建和切换开销大,并发数受系统资源限制。select适合高并发低负载的网络服务,而多进程/线程更适合需要并行计算或强隔离性的应用。
习题5
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[BUFFER_SIZE] = {0};char filename[256] = {0};// 创建 socket 文件描述符if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 绑定 socket 到端口address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}// 接收文件名read(new_socket, filename, 255);printf("Receiving file: %s\n", filename);FILE *file = fopen(filename, "wb");if (file == NULL) {perror("File opening failed");exit(EXIT_FAILURE);}// 接收文件数据int bytes_read;while ((bytes_read = read(new_socket, buffer, BUFFER_SIZE)) > 0) {fwrite(buffer, 1, bytes_read, file);}fclose(file);printf("File transfer completed.\n");close(new_socket);close(server_fd);return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main(int argc, char const *argv[]) {if (argc != 3) {fprintf(stderr, "Usage: %s <filename> <server IP>\n", argv[0]);exit(EXIT_FAILURE);}int sock = 0;struct sockaddr_in serv_addr;char buffer[BUFFER_SIZE] = {0};char *filename = argv[1];char *server_ip = argv[2];// 创建 socket 文件描述符if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {printf("\n Socket creation error \n");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);// 转换 IPv4 地址从文本到二进制if (inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0) {printf("\nInvalid address/ Address not supported \n");return -1;}// 连接到服务器if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {printf("\nConnection Failed \n");return -1;}// 发送文件名send(sock, filename, strlen(filename), 0);printf("Sending file: %s\n", filename);FILE *file = fopen(filename, "rb");if (file == NULL) {perror("File opening failed");exit(EXIT_FAILURE);}// 发送文件数据int bytes_read;while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) > 0) {send(sock, buffer, bytes_read, 0);}fclose(file);printf("File transfer completed.\n");// 发送结束信号const char *end_signal = "END";send(sock, end_signal, strlen(end_signal), 0);close(sock);return 0;
}
实践截图:
习题6
server.c
#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 <pthread.h>#define PORT 8080
#define BUFFER_SIZE 1024void *handle_client(void *client_socket) {int sock = *((int *)client_socket);free(client_socket);char buffer[BUFFER_SIZE] = {0};char filename[256] = {0};// 接收文件名read(sock, filename, 255);printf("Receiving file: %s\n", filename);FILE *file = fopen(filename, "wb");if (file == NULL) {perror("File opening failed");close(sock);return NULL;}// 接收文件数据int bytes_read;while ((bytes_read = read(sock, buffer, BUFFER_SIZE)) > 0) {fwrite(buffer, 1, bytes_read, file);}fclose(file);printf("File transfer completed.\n");close(sock);return NULL;
}int main() {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);pthread_t thread_id;// 创建 socket 文件描述符if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 绑定 socket 到端口address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}while (1) {if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}int *client_sock = malloc(sizeof(int));*client_sock = new_socket;pthread_create(&thread_id, NULL, handle_client, (void *)client_sock);}close(server_fd);return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main(int argc, char const *argv[]) {if (argc != 3) {fprintf(stderr, "Usage: %s <filename> <server IP>\n", argv[0]);exit(EXIT_FAILURE);}int sock = 0;struct sockaddr_in serv_addr;char buffer[BUFFER_SIZE] = {0};const char *filename = argv[1];const char *server_ip = argv[2];// 创建 socket 文件描述符if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {printf("\n Socket creation error \n");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);// 转换 IPv4 地址从文本到二进制if (inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0) {printf("\nInvalid address/ Address not supported \n");return -1;}// 连接到服务器if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {printf("\nConnection Failed \n");return -1;}// 发送文件名send(sock, filename, strlen(filename), 0);printf("Sending file: %s\n", filename);FILE *file = fopen(filename, "rb");if (file == NULL) {perror("File opening failed");exit(EXIT_FAILURE);}// 发送文件数据int bytes_read;while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) > 0) {send(sock, buffer, bytes_read, 0);}fclose(file);printf("File transfer completed.\n");// 发送结束信号const char *end_signal = "END";send(sock, end_signal, strlen(end_signal), 0);close(sock);return 0;
}
习题7
FTP协议采用双通道设计(控制连接和数据连接),通过21端口建立控制连接传输命令,再动态创建数据连接(主动模式20端口/被动模式随机端口)传输文件。Linux中的FTP实现(如vsftpd/proftpd)源码通常包含用户认证、命令解析、文件传输等模块,通过socket编程处理连接,使用状态机管理会话流程,结合系统调用实现文件操作。其设计注重安全(如chroot隔离)、性能(异步I/O)和扩展性(模块化架构),源代码分析可追踪socket绑定、PORT/PASV模式切换及文件读写等核心逻辑的实现细节。
习题8
test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/if_packet.h>#define BUFFER_SIZE 65536int main() {int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));if (sockfd < 0) {perror("socket");exit(1);}unsigned char buffer[BUFFER_SIZE];while (1) {int data_size = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, NULL, NULL);if (data_size < 0) {perror("recvfrom");close(sockfd);exit(1);}struct ethhdr *eth = (struct ethhdr *)buffer;printf("\n以太网头部:源MAC %.2X:%.2X:%.2X:%.2X:%.2X:%.2X → 目标MAC %.2X:%.2X:%.2X:%.2X:%.2X:%.2X 类型 %.4X\n",eth->h_source[0], eth->h_source[1], eth->h_source[2],eth->h_source[3], eth->h_source[4], eth->h_source[5],eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],eth->h_dest[3], eth->h_dest[4], eth->h_dest[5],ntohs(eth->h_proto));if (ntohs(eth->h_proto) == ETH_P_IP) {struct iphdr *ip = (struct iphdr *)(buffer + sizeof(struct ethhdr));printf("IP头部:%s → %s 协议 %d TTL %d\n",inet_ntoa(*(struct in_addr *)&ip->saddr),inet_ntoa(*(struct in_addr *)&ip->daddr),ip->protocol, ip->ttl);if (ip->protocol == IPPROTO_TCP) {struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct ethhdr) + ip->ihl*4);printf("TCP头部:源端口 %d → 目标端口 %d 标志 [%s%s%s%s%s%s] 序列号 %u\n",ntohs(tcp->source), ntohs(tcp->dest),tcp->urg ? "URG " : "", tcp->ack ? "ACK " : "",tcp->psh ? "PSH " : "", tcp->rst ? "RST " : "",tcp->syn ? "SYN " : "", tcp->fin ? "FIN " : "",ntohl(tcp->seq));}}}close(sockfd);return 0;
}
实践截图:
六、学习实践过程遇到的问题与解决方式
1. Socket套接字使用失败
问题描述
不了解Socket套接字使用失败的原因和解决方法。
解决方式
- Socket套接字使用失败(Socket Failure):是指在使用Socket进行网络编程时,由于各种原因导致Socket无法正常工作。这可能包括无法建立连接、数据传输失败、连接中断等情况。Socket套接字使用失败通常会影响程序的正常运行,导致网络通信无法完成。
- 失败的原因:
- 网络问题:网络连接不可用、网络延迟过高或网络配置错误可能导致Socket无法建立连接或数据传输失败。
- 资源限制:系统资源不足(如文件描述符耗尽、内存不足)可能导致Socket创建失败或无法正常工作。
- 错误的API调用:错误的Socket API调用,如绑定错误的地址、端口冲突、错误的协议类型等,可能导致Socket无法正常工作。
- 对端问题:对端服务器或客户端可能关闭连接、拒绝连接或发送错误的响应,导致Socket使用失败。
- 解决方法:
- 检查网络连接:确保网络配置正确,网络连接可用。可以使用
ping
或traceroute
等工具检查网络连通性。 - 检查资源限制:确保系统资源充足,特别是文件描述符和内存资源。可以通过调整系统参数(如
ulimit
)来增加资源限制。 - 验证API调用:仔细检查Socket API调用的参数和逻辑,确保没有错误。可以参考API文档或示例代码进行验证。
- 处理对端问题:确保对端服务正常运行,端口开放且没有防火墙限制。可以使用
telnet
或nc
等工具检查对端端口是否可用。 - 错误处理:在代码中添加适当的错误处理逻辑,捕获并处理可能出现的错误。例如,使用
try-catch
块捕获异常,并根据错误类型进行相应的处理。
- 检查网络连接:确保网络配置正确,网络连接可用。可以使用
- 应用场景:Socket套接字使用失败是网络编程中常见的问题,特别是在复杂的网络环境或高并发场景下。了解失败的原因和解决方法可以帮助开发者更好地调试和优化网络程序,提高系统的可靠性和稳定性。
2. 多路复用
问题描述
不了解多路复用(I/O多路复用)的概念、原理及应用场景。
解决方式
- 多路复用(I/O Multiplexing):是一种允许单个进程同时管理多个I/O操作的技术,它通过监控多个文件描述符(如套接字、文件等),确定哪些文件描述符已经准备好进行读写操作,从而实现高效地处理多个I/O请求。多路复用在高并发网络编程中尤为重要,能够显著提高程序的性能和效率。
- 多路复用的原理:
select
:通过轮询的方式检查一组文件描述符集合,确定哪些描述符已经准备好进行I/O操作。select
的效率较低,因为它需要遍历整个文件描述符集合,且存在最大文件描述符数量的限制。poll
:通过数组管理一组文件描述符,并检查每个描述符的状态。poll
没有最大文件描述符数量的限制,但仍然需要轮询,效率在处理大量文件描述符时不如epoll
。epoll
:基于事件通知机制,内核会主动通知程序哪些文件描述符已经准备好进行I/O操作。epoll
不需要轮询,效率更高,尤其适合处理大量文件描述符的场景。
- 应用场景:
- 高并发服务器:如Web服务器、聊天服务器等,需要同时处理多个客户端的连接请求和数据传输。多路复用技术可以高效地管理这些连接,避免为每个连接创建一个线程或进程,从而节省系统资源。
- 实时数据处理:如金融交易系统、实时监控系统等,需要快速响应多个数据源的输入和输出。多路复用可以确保及时处理每个数据源的I/O操作,提高系统的实时性。
- 资源管理:在资源受限的环境中(如嵌入式系统),多路复用可以有效利用有限的系统资源,同时处理多个任务,提高系统的整体性能。
- 使用多路复用时的注意事项:
- 资源限制:虽然
poll
和epoll
没有文件描述符数量的限制,但系统资源(如文件描述符表的大小)仍然是有限的。需要合理管理文件描述符,避免资源耗尽。 - 性能优化:根据实际应用场景选择合适的多路复用机制。例如,在处理大量文件描述符时,
epoll
的性能优势更为明显。 - 错误处理:在多路复用的代码中,需要添加适当的错误处理逻辑,以应对文件描述符关闭、网络错误等情况。
- 兼容性问题:不同的操作系统和库对多路复用的支持可能有所不同,需要确保代码在目标平台上具有良好的兼容性。
- 代码复杂性:多路复用的代码逻辑相对复杂,需要合理设计程序结构,避免代码难以维护。
- 资源限制:虽然
七、参考资料
- AI工具(你使用的AI工具及其链接)
- 问小白
- Kimi
- 图书
- 《Linux编程基础.李养群》电子版
- 网站
- CSDN
“20242817李臻 原创作品转载请注明出处 《Linux高级编程实践》”
相关文章:
20242817李臻《Linux⾼级编程实践》第8周
20242817李臻《Linux⾼级编程实践》第8周 一、AI对学习内容的总结 计算机网络概述 1. 计算机网络概述 计算机网络的定义:通过通信线路将地理位置不同的多台计算机连接起来,实现资源共享和信息传递。网络的组成: 硬件:计算机、…...
《Java工程师面试核心突破》专栏简介
《Java工程师面试核心突破》专栏简介 🔥 大厂Offer收割机 | 源码级技术纵深 | 90%高频考点覆盖 专栏定位 「拒绝八股文,直击技术本质」 本专栏专为Java中高级工程师量身定制,通过6大核心模块、30个硬核专题,系统性拆解大厂面试…...
Spark-SQL与Hive
Spark-SQL与Hive的那些事儿:从连接到数据处理 在大数据处理领域,Spark-SQL和Hive都是非常重要的工具。今天咱们就来聊聊它们之间的关系,以及怎么用Spark-SQL去连接Hive进行数据处理。先说说Hive,它是Hadoop上的SQL引擎࿰…...
Keil5没有stm32的芯片库
下载完重启就行了,我这里就不演示了,stm已经下载,随便选的一个芯片库演示一下...
Kafka 在小流量和大流量场景下的顺序消费问题
一、低流量系统 特点 消息量较少,吞吐量要求低。系统资源(如 CPU、内存、网络)相对充足。对延迟容忍度较高。 保证顺序消费的方案 单分区 单消费者 将消息发送到单个分区(例如固定 Partition 0),由单个…...
Spark-SQL(四)
本节课学习了spark连接hive数据,在 spark-shell 中,可以看到连接成功 将依赖放进pom.xml中 运行代码 创建文件夹 spark-warehouse 为了使在 node01:50070 中查看到数据库,需要添加如下代码,就可以看到新创建的数据库 spark-sql_1…...
海外服务器安装Ubuntu 22.04图形界面并配置VNC远程访问指南
在云计算和远程工作日益普及的今天,如何高效地管理和使用海外服务器成为了一个热门话题。本文将详细介绍如何在海外的Ubuntu 22.04服务器上安装图形界面,并配置VNC服务来实现远程访问。无论您是开发者、系统管理员,还是只是想要更便捷地管理您的海外服务器,这篇指南都能为您…...
kafka 分区分散在不同服务器上的原理
目录 原理方面在 1- 5,如果对原理理解,可以直接到图例部分,看结果 1. 分区分配机制 2. 副本分配机制 3. 手动控制分区的分布 4.分区(Partition)如何分布在不同的 Broker 上? 5. 主分区(Le…...
JavaScript 中的单例模式
单例模式在 JavaScript 中是一种确保类只有一个实例,并提供全局访问点的方式。由于 JavaScript 的语言特性(如对象字面量、模块系统等),实现单例有多种方式。 常见实现方式 1. 对象字面量(最简单的单例) …...
19_大模型微调和训练之-基于LLamaFactory+LoRA微调LLama3
基于LLamaFactory微调_LLama3的LoRA微调 1. 基本概念1.1. LoRA微调的基本原理1.2. LoRA与QLoRA1.3. 什么是 GGUF 2.LLaMA-Factory介绍3. 实操3.1 实验环境3.2 基座模型3.3 安装 LLaMA-Factory 框架3.3.1 前置条件 3.4 数据准备3.5 微调和训练模型torch.cuda.OutOfMemoryError: …...
【Maven基础】
Maven:一个项目管理工具 前言 传统项目管理存在的问题: 依赖管理混乱 需要自己去网上搜 jar 包,找对版本很痛苦(还容易找错)某个库依赖另一个库(传递依赖),你得自己挨个找齐不小心…...
衡石 ChatBI 用户手册-使用指南
产品概述 衡石 ChatBI 是一款融合了 AI 技术的智能数据分析工具,旨在为企业业务人员提供直观、高效的数据交互体验。通过自然语言处理技术,用户可以直接与数据进行对话,快速获取所需信息,从而为业务决策提供有力支持。此外&…...
DeepSeek+Cursor+Devbox+Sealos项目实战
黑马程序员DeepSeekCursorDevboxSealos带你零代码搞定实战项目开发部署视频教程,基于AI完成项目的设计、开发、测试、联调、部署全流程 原视频地址视频选的项目非常基础,基本就是过了个web开发流程,但我在实际跟着操作时,ai依然会…...
Unreal 如何实现一个Vehicle汽车沿着一条指定Spline路径自动驾驶
文章目录 前言准备工作驾驶原理驾驶轨迹自动驾驶油门控制科普:什么是PID?转向控制科普:点乘和叉乘最终蓝图最后前言 Unreal Engine 的 Chaos Vehicle System(原PhysX Vehicle)是一套基于物理模拟的车辆驾驶系统,支持高度可定制的车辆行为,适用于赛车、模拟驾驶等游戏类…...
开源脚本分享:用matlab处理ltspice生成的.raw双脉冲数据
Author :PNJIE DATE: 2025/04/21 V0.0 前言 该项目旨在使用Matlab处理LTspice的.raw文件,包括动态计算和绘图,部分脚本基于LTspice2Matlab项目: PeterFeicht/ltspice2matlab: LTspice2Matlab - 将LTspice数据导入MATLAB github地址&#x…...
聊透多线程编程-线程互斥与同步-13. C# Mutex类实现线程互斥
目录 一、什么是临界区? 二、Mutex类简介 三、Mutex的基本用法 解释: 四、Mutex的工作原理 五、使用示例1-保护共享资源 解释: 六、使用示例2-跨进程同步 示例场景 1. 进程A - 主进程 2. 进程B - 第二个进程 输出结果 ProcessA …...
Halcon应用:相机标定之应用
提示:若没有查找的算子,可以评论区留言,会尽快更新 Halcon应用:相机标定之应用 前言一、Halcon应用?二、应用实战1、如何应用标定(快速)2、代码讲解(重要)2.1 、我们还是…...
【计算机视觉】CV实战项目- CMU目标检测与跟踪系统 Object Detection Tracking for Surveillance Video
CMU 目标检测与跟踪系统(Object Detection & Tracking for Surveillance Video) 1. 项目概述2. 技术亮点(1)目标检测模型(2)多目标跟踪(MOT)(3)重识别&am…...
报错 | 配置 postcss 出现 报错:A `require()` style import is forbidden.
背景:安装 postcss,配置时,出现报错:A require() style import is forbidden. 翻译:禁止导入require()样式 解决:前头添加 /* eslint-env node */ ,也飘红,…...
[Qt]双击事件导致的问题
有如下代码 #include "mymodel.h" #include <QDebug>myModel::myModel(QObject *parent) : QAbstractTableModel(parent) {status << Qt::Unchecked << Qt::Unchecked << Qt::Unchecked; }int myModel::rowCount(const QModelIndex &pa…...
[SpringBoot]配置文件
通过案例可以不难发现,springboot实际上就是spring的一种辅助工具,帮我们更快地使用spring开发。尤其是配置这块,注解springboot解决了很多繁琐重复的配置操作。 但在实际开发需求,当然不可能只用springboot已经配置好的配置信息。…...
前端框架开发编译阶段与运行时的核心内容详解Tree Shaking核心实现原理详解
前端框架开发编译阶段与运行时的核心内容详解 一、开发编译阶段 开发编译阶段是前端框架将源代码转换为浏览器可执行代码的核心过程,涉及代码转换、优化和资源整合。 模块打包与依赖管理 • 依赖图构建:工具(如Webpack、Vile)通过静态分析生成模块依赖关系图,支持按需加载…...
idea2024.1双击快捷方式打不开
idea2024.1突然双击快捷方式打不开,使用管理员运行也打不开 在安装的idea路径下的bin目录下双击打开idea.bat文件,要是打不开使用txt格式打开,打开后在最后一行加上pause,之后保存。 看看报错信息是不是有一个initializedExcept…...
鸿蒙NEXT开发LRUCache缓存工具类(单例模式)(ArkTs)
import { util } from kit.ArkTS;/*** LRUCache缓存工具类(单例模式)* author 鸿蒙布道师* since 2025/04/21*/ export class LRUCacheUtil {private static instance: LRUCacheUtil;private lruCache: util.LRUCache<string, any>;/*** 私有构造函…...
开源身份和访问管理(IAM)解决方案:Keycloak
一、Keycloak介绍 1、什么是 Keycloak? Keycloak 是一个开源的身份和访问管理(Identity and Access Management - IAM)解决方案。它旨在为现代应用程序和服务提供安全保障,简化身份验证和授权过程。Keycloak 提供了集中式的用户…...
Latex科研入门教程
Introduction 这篇文章适合有markdown基础的人看,不会的人可以先去学一下markdown. 仅适用于科研入门. 本文使用的latex环境为overleaf Latex概况 文件格式 以.tex为结尾的文件可能有多个.tex文件最终只编译一个文件,相当于一个文件控制其他子文件. Latex 代码分为三种&…...
CSS 中实现 div 居中有以下几种常用方法
在 CSS 中实现 div 居中有以下几种常用方法,具体取决于需要 水平居中、垂直居中 还是 两者兼具。以下是详细解决方案: 目录 一、水平居中(Horizontal Centering) 1. 行内块元素(Inline-Blo…...
win11修改文件后缀名
一、问题描述 win11系统中,直接添加.py后缀后仍然是txt文本文件 二、处理方式: 点击上方三个小点点击“选项”按钮 点击“查看”取消“隐藏已知文件类型的扩展名”选项点击“应用” 此时,“.txt”文件后缀显示出来了。将txt删去,…...
【数据结构和算法】3. 排序算法
本文根据 数据结构和算法入门 视频记录 文章目录 1. 排序算法2. 插入排序 Insertion Sort2.1 概念2.2 具体步骤2.3 Java 实现2.4 复杂度分析 3. 快排 QuickSort3.1 概念3.2 具体步骤3.3 Java实现3.4 复杂度分析 4. 归并排序 MergeSort4.1 概念4.2 递归具体步骤4.3 Java实现4.4…...
k8s之 kube-prometheus监控
Kubernetes 中的 kube-prometheus 是一个基于 Prometheus Operator 的完整监控解决方案,它集成了 Prometheus、Alertmanager、Grafana 以及一系列预定义的监控规则和仪表盘,专为 Kubernetes 集群设计。 一、核心组件介绍 Prometheus Operator …...
Docker Compose 和 Kubernetes(k8s)区别
前言:Docker Compose 和 Kubernetes(k8s)是容器化技术中两个常用的工具,但它们的定位、功能和适用场景有显著区别。以下是两者的核心对比: 1. 定位与目标 特性 Docker Compose Kubernet…...
【SpringBoot】HttpServletRequest获取使用及失效问题(包含@Async异步执行方案)
目录 1. 在 Controller 方法中作为参数注入 2.使用 RequestContextHolder (1)失效问题 (2)解决方案一: (3)解决方案二: 3、使用AutoWrite自动注入HttpServletRequest 跨线程调…...
【Easylive】为什么需要手动转换 feign.Response 到 HttpServletResponse
【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版 为什么需要手动转换 feign.Response 到 HttpServletResponse? feign.Response 是 Feign 客户端调用远程服务后返回的原始 HTTP 响应对象,而 HttpServletResponse 是…...
C语言交换函数:为什么必须用指针传递参数?
写一个简单交换两个变量值的函数,我们要理解C语言中参数传递的机制. C语言中的函数参数默认是按值传递,也就是说,如果我写一个函数,如 void swap(int a,int b) {int tmp a;a b;b tmp; }然后在函数内部交换a,b的值,这不会影响到函数外部的变量,因为传递的是值的副本. 就像…...
C#+Visual Studio 2022为AutoCAD 2022开发插件并显示在Ribbon选项卡
1.插件功能开发 (1)建立C#类库项目,添加必要引用,都是autocad二次开发相关的,要注意对引用的库修改其“复制文件”属性为false (2)项目调试使用“属性”打开“启用外部程序”,指定为机器上autocad2022的a…...
全景VR是什么?全景VR有什么热门用途?
全景VR的概念与技术特点 全景VR,即虚拟现实全景,是新型的视觉展示技术。通过拍摄和构建三维模拟环境,使浏览者能够通过网络获得三维立体的空间感觉,仿佛身临其境。全景VR技术的核心在于360全景图像的捕捉和展示,它允许…...
美创科技20周年庆典顺利举行
2025年4月19日 美创科技成立20周年 “稳健前行二十载,创新共赢新未来” 美创科技周年庆典在杭州总部顺利举行 美创科技20周年庆典精彩视频回顾 (点击查看美创科技20周年庆典精彩视频回顾) CEO致辞 20周年再出发,开启新增长周期…...
学习笔记二十二—— 并发五大常见陷阱
⚠️ 并发五大常见陷阱 目录 数据竞争 (Data Race)死锁 (Deadlock)竞态条件 & 饿死现象 (Race Condition & Starvation)悬挂指针 (Dangling Pointer)重复释放 (Double Free)开发自查清单 1. 数据竞争 (Data Race) 专业定义 两个及以上线程在缺乏同步的情况下同时访问同…...
精益数据分析(10/126):深度剖析数据指标,驱动创业决策
精益数据分析(10/126):深度剖析数据指标,驱动创业决策 在创业的旅程中,数据指标是我们把握方向的关键工具。今天,我想和大家一起深入学习《精益数据分析》中关于数据指标的知识,共同探索如何利…...
冒泡排序详解
void bubbleSort(std::vector& arr) { int n arr.size(); for (int i 0; i < n-1 ; i) { // 需要 n-1 轮 原理是 3个元素 两轮比交即可 10个元素9轮比较即可 bool swapped false; // 用于优化,检测是否发生交换 for (int j 0; j < n - i -1 ; j) { //…...
小刚说C语言刷题——1039 求三个数的最大数
1.题目描述 已知有三个不等的数,将其中的最大数找出来。 输入 输入只有一行,包括3个整数。之间用一个空格分开。 输出 输出只有一行(这意味着末尾有一个回车符号),包括1个整数。 样例 输入 1 5 8 输出 8 2.…...
【日志体系】ELK Stack与云原生日志服务
IaaS日志体系:ELK Stack与云原生日志服务 一、技术演进的双重脉络二、架构设计的范式差异三、关键技术突破解析四、前沿发展与行业实践 当某国际电商平台在"黑色星期五"遭遇每秒百万级日志洪峰时,其运维团队通过混合日志架构实现全链路追踪&am…...
spark和hadoop区别联系
区别 设计理念 Hadoop:主要解决大规模数据的存储和处理问题,其核心是 Hadoop 分布式文件系统(HDFS)和 MapReduce 计算模型。HDFS 用于存储大规模数据,MapReduce 用于处理数据,它将数据处理过程分为 Map 和…...
240422 leetcode exercises
240422 leetcode exercises jarringslee 文章目录 240422 leetcode exercises[237. 删除链表中的节点](https://leetcode.cn/problems/delete-node-in-a-linked-list/)🔁节点覆盖法 [392. 判断子序列](https://leetcode.cn/problems/is-subsequence/)🔁…...
【上位机——MFC】菜单类与工具栏
菜单类 CMenu,封装了关于菜单的各种操作成员函数,另外还封装了一个非常重要的成员变量m_hMenu(菜单句柄) 菜单使用 添加菜单资源加载菜单 工具栏相关类 CToolBarCtrl-》父类是CWnd,封装了关于工具栏控件的各种操作。 CToolBar-》父类是CC…...
Spark-SQL连接Hive总结及实验
一、核心模式与配置要点 1. 内嵌Hive 无需额外配置,直接使用,但生产环境中几乎不使用。 2. 外部Hive(spark-shell连接) 配置文件:将hive-site.xml(修改数据库连接为node01)、core-site.xml、…...
20.3 使用技巧9
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的 20.3.13 DataGridView使用日期选择控件 有时为了输入方便或者固定日期格式,可以考虑点击DataGridView中某个单元格时出现…...
逻辑回归(Logistic Regression)
逻辑回归(Logistic Regression) 原理 通过 Sigmoid函数( σ ( z ) 1 1 e − z σ(z) \frac{1}{1e^{-z}} σ(z)1e−z1)将线性回归输出 z w T x b z w^Tx b zwTxb 映射到 [0,1] 区间输出值表示样本属于正类的概率&#…...
weblogic12 部署war包 项目运行报错
问题表现 weblogic12 部署war包项目成功,运行启动成功。但是在使用此项目的时候,点击任何功能都会报错,部分报错如下: at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.…...
重新定义户外防护!基于DeepSeek的智能展开伞棚系统技术深度解析
从“手动操作”到“感知决策”,AI重构城市空间弹性 全球极端天气事件频发,传统伞棚依赖人工展开/收纳,存在响应滞后(暴雨突袭时展开需3-5分钟)、抗风能力弱(8级风损毁率超60%)、空间利用率低等痛…...