文章目录
- 第十一周预习报告
- 学习内容
- AI 对学习内容的总结
- 章节概述
- 网络基础知识
- TCP/IP协议栈简介
- Socket编程基础
- 创建服务器程序
- 处理客户端连接请求
- 创建客户端程序
- 错误处理
- 并发处理
- 安全性考量
- 对知识总结的反思
- 网络编程
- Socket 编程
- 思维导图
- 基于 AI 的学习
- 要求
- 学习内容
- 知识点1:TCP/IP协议栈
- 问答
- 知识点2:Socket编程
- 问答
- 学习实践过程遇到的问题与解决方式(AI 驱动,优先使用AI工具解决问题)
- 参考资料
第十一周预习报告
学习内容
- HeadFirstC嗨翻C语言第11章
AI 对学习内容的总结
章节概述
- 第11章主要介绍的是网络编程的基础知识,特别是如何使用C语言编写网络应用程序。这一章节将引导读者了解如何通过网络与其他计算机进行通信,包括如何创建服务器和客户端程序。
网络基础知识
- IP地址:互联网协议地址(Internet Protocol Address),用于唯一标识网络中的设备。IPv4地址为32位,通常表示为四个十进制数,如192.168.1.1。IPv6地址为128位,表示为八组十六进制数,如2001:0db8:85a3:0000:0000:8a2e:0370:7334。
- 域名:域名是IP地址的友好名称,便于人类记忆和使用。域名系统(DNS)将域名转换为IP地址。例如,www.example.com 被解析为 93.184.216.34。
TCP/IP协议栈简介
- 应用层:提供应用程序间的通信,如HTTP、FTP、SMTP等。
- 传输层:负责端到端的通信,如TCP(传输控制协议)和UDP(用户数据报协议)。
- 网络层:负责数据包的路由,如IP(互联网协议)。
- 链路层:负责同一网络内的数据传输,如以太网、Wi-Fi等。
Socket编程基础
- Socket:网络通信的抽象,允许不同主机之间的进程进行通信。在C语言中,使用
socket()
函数创建套接字。int sockfd = socket(AF_INET, SOCK_STREAM, 0);
AF_INET
:指定地址族为IPv4。SOCK_STREAM
:指定套接字类型为流式套接字(TCP)。0
:默认协议(TCP)。
创建服务器程序
-
绑定地址和端口:使用
bind()
函数将套接字绑定到特定的IP地址和端口。struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定到所有可用的网络接口 serv_addr.sin_port = htons(PORT); // 指定端口号if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {perror("bind failed");exit(EXIT_FAILURE); }
-
监听连接:使用
listen()
函数使套接字进入监听状态,等待客户端连接。if (listen(sockfd, 5) < 0) { // 第二个参数是最大连接队列长度perror("listen failed");exit(EXIT_FAILURE); }
-
接受连接:使用
accept()
函数接受客户端连接请求。struct sockaddr_in cli_addr; socklen_t clilen = sizeof(cli_addr); int newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen); if (newsockfd < 0) {perror("accept failed");exit(EXIT_FAILURE); }
处理客户端连接请求
-
读取数据:使用
read()
函数从客户端读取数据。char buffer[1024]; ssize_t n = read(newsockfd, buffer, sizeof(buffer) - 1); if (n < 0) {perror("read failed");exit(EXIT_FAILURE); } buffer[n] = '\0'; // 确保字符串以null终止 printf("Received: %s\n", buffer);
-
发送数据:使用
write()
函数向客户端发送数据。const char *response = "Hello from server"; write(newsockfd, response, strlen(response));
创建客户端程序
-
连接到服务器:使用
connect()
函数连接到服务器。struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {perror("inet_pton failed");exit(EXIT_FAILURE); }if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {perror("connect failed");exit(EXIT_FAILURE); }
-
发送数据:使用
write()
函数向服务器发送数据。const char *message = "Hello from client"; write(sockfd, message, strlen(message));
-
接收数据:使用
read()
函数从服务器接收数据。char buffer[1024]; ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1); if (n < 0) {perror("read failed");exit(EXIT_FAILURE); } buffer[n] = '\0'; // 确保字符串以null终止 printf("Server says: %s\n", buffer);
错误处理
-
检查网络操作的结果:每次网络操作后,检查返回值以确定是否成功。
if (some_network_function() < 0) {perror("some_network_function failed");exit(EXIT_FAILURE); }
-
错误消息的意义及应对策略:
EINVAL
:无效的参数。ECONNREFUSED
:连接被拒绝。ETIMEDOUT
:连接超时。EADDRINUSE
:地址已在使用中。ECONNRESET
:连接被对端重置。EPIPE
:管道破裂(通常是写入已关闭的连接)。
并发处理
-
使用多线程:使用
pthread_create()
创建新线程处理每个客户端连接。pthread_t thread_id; if (pthread_create(&thread_id, NULL, handle_client, (void *)&newsockfd) != 0) {perror("pthread_create failed");exit(EXIT_FAILURE); }
-
使用多进程:使用
fork()
创建新进程处理每个客户端连接。pid_t pid = fork(); if (pid < 0) {perror("fork failed");exit(EXIT_FAILURE); } else if (pid == 0) {// 子进程处理客户端连接handle_client(newsockfd);close(newsockfd);exit(0); } else {// 父进程继续监听新的连接close(newsockfd); }
安全性考量
-
简单的加密方法:使用对称加密算法(如AES)或非对称加密算法(如RSA)对数据进行加密。
// 示例:使用AES加密 AES_KEY aes_key; unsigned char key[AES_BLOCK_SIZE] = "0123456789abcdef"; // 16字节密钥 unsigned char iv[AES_BLOCK_SIZE] = "0123456789abcdef"; // 16字节初始向量 AES_set_encrypt_key(key, 128, &aes_key); AES_cbc_encrypt(plaintext, ciphertext, plaintext_len, &aes_key, iv, AES_ENCRYPT);
-
认证和授权基本概念:
- 认证:验证用户身份,确保用户是他们声称的人。常用方法包括用户名/密码、生物识别、双因素认证等。
- 授权:授予用户特定权限,确保用户只能访问他们被允许访问的资源。常用方法包括基于角色的访问控制(RBAC)、访问控制列表(ACL)等。
对知识总结的反思
网络编程
- 在学习过程中,虽然已经了解了网络编程的基本概念和流程,但在实际开发中可能还需要考虑更多的细节,例如如何优化网络性能、如何处理复杂的网络拓扑结构等。
- 实际操作时,需要特别关注网络安全,避免出现数据泄露或其他安全隐患。
Socket 编程
- 尽管掌握了使用Socket进行网络编程的基本流程,但对于一些高级功能(如非阻塞I/O、异步I/O等)的理解还不够深入。
- 此外,对于跨平台兼容性的考虑也是必不可少的,因为不同的操作系统对Socket API的支持可能存在差异。
思维导图
mindmap网络基础知识IP地址和域名的概念TCP/IP协议栈简介Socket编程基础创建服务器程序监听特定端口等待连接处理客户端连接请求数据发送与接收创建客户端程序连接到服务器的方法发送数据给服务器接收来自服务器的数据错误处理检查网络操作的结果错误消息的意义及应对策略并发处理使用多线程或多进程同时处理多个客户端连接安全性考量简单的加密方法认证和授权基本概念
基于 AI 的学习
要求
- 至少找到自己理解不是太好的2个概念,让AI对自己进行苏格拉底提问
学习内容
知识点1:TCP/IP协议栈
问答
AI: 你能解释一下TCP/IP协议栈的主要组成部分及其作用吗?
我: TCP/IP协议栈由四个主要层次组成:应用层、运输层、网络层和链路层。每个层次都有其特定的功能,共同协作以实现可靠的数据传输。
AI: 很好!那么,TCP/IP协议栈是如何保证数据传输的可靠性?
我: 主要是通过TCP协议,在运输层上提供了可靠的、面向连接的服务。它通过确认应答、流量控制和拥塞控制等机制来确保数据包的有序传递和完整性。
AI: 非常好!那么,你认为在网络编程中,选择TCP还是UDP取决于哪些因素?
我: 选择TCP还是UDP主要取决于具体的应用需求。如果需要高可靠性和顺序保证,则选择TCP;如果可以容忍一定程度的数据丢失但要求低延迟和高效传输,则选择UDP。
AI: 你的回答很全面。总结一下:
- TCP/IP协议栈由四个主要层次组成:应用层、运输层、网络层和链路层。
- TCP/IP协议栈通过TCP协议实现了可靠的数据传输。
- 选择TCP还是UDP取决于具体的应用需求。
知识点2:Socket编程
问答
AI: 你能简要描述一下什么是Socket编程吗?
我: Socket编程是一种用于网络通信的技术,允许不同主机上的两个程序之间进行双向通信。它是基于TCP/IP协议栈的一种抽象接口。
AI: 很好!那么,Socket编程有哪些常见的类型?
我: Socket编程主要有两种常见类型:流式Socket(SOCK_STREAM),对应TCP协议,提供可靠的、面向连接的服务;数据报Socket(SOCK_DGRAM),对应UDP协议,提供不可靠的、无连接的服务。
AI: 非常好!那么,你在使用Socket编程时,可能会遇到哪些常见的错误?
我: 使用Socket编程时,常见的错误包括但不限于:连接拒绝、超时、缓冲区溢出、权限不足等。这些问题往往需要仔细调试和错误处理才能解决。
AI: 你的回答很全面。总结一下:
- Socket编程是一种用于网络通信的技术,基于TCP/IP协议栈。
- Socket编程有流式Socket和数据报Socket两种常见类型。
- 使用Socket编程时,可能会遇到各种错误,需要通过调试和错误处理来解决。
学习实践过程遇到的问题与解决方式(AI 驱动,优先使用AI工具解决问题)
-
问题1:如何在C语言中创建一个简单的TCP服务器?
-
解决方式:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h>void errorHandling(char *message) {fputs(message, stderr);fputc('\n', stderr);exit(1); }int main(int argc, char *argv[]) {int listenfd, connfd;struct sockaddr_in serv_adr;if (argc != 2) {printf("Usage : %s <port>\n", argv[0]);exit(1);}listenfd = socket(PF_INET, SOCK_STREAM, 0);if (listenfd == -1)errorHandling("socket() error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if (bind(listenfd, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)errorHandling("bind() error");if (listen(listenfd, 5) == -1)errorHandling("listen() error");while (1) {connfd = accept(listenfd, NULL, NULL);if (connfd == -1)errorHandling("accept() error");write(connfd, "Hello from server\n", strlen("Hello from server\n"));close(connfd);}close(listenfd);return 0; }
-
问题2:如何在C语言中创建一个简单的TCP客户端?
-
解决方式:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h>void errorHandling(char *message) {fputs(message, stderr);fputc('\n', stderr);exit(1); }int main(int argc, char *argv[]) {int sockfd;struct sockaddr_in serv_adr;if (argc != 3) {printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sockfd = socket(PF_INET, SOCK_STREAM, 0);if (sockfd == -1)errorHandling("socket() error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = inet_addr(argv[1]);serv_adr.sin_port = htons(atoi(argv[2]));if (connect(sockfd, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)errorHandling("connect() error");char message[] = "Hello from client";write(sockfd, message, strlen(message));char buffer[1024];read(sockfd, buffer, sizeof(buffer)-1);buffer[sizeof(buffer)-1] = '\0';printf("Server says: %s\n", buffer);close(sockfd);return 0; }
-
问题3:如何确保数据在网络传输过程中不被篡改或损坏?
-
解决方式:
-
使用校验和(Checksum):
- 校验和是一种简单而有效的数据完整性验证方法。发送方在发送数据之前,计算数据的校验和,并将其附加到数据包中。接收方在接收到数据后,重新计算校验和并与接收到的校验和进行比较。如果两者一致,则认为数据未被篡改或损坏。
- 示例:
#include <stdio.h> #include <stdint.h>uint16_t calculate_checksum(const uint8_t *data, size_t length) {uint32_t sum = 0;for (size_t i = 0; i < length; i += 2) {uint16_t word = (data[i] << 8) | data[i + 1];sum += word;if (sum & 0xFFFF0000) {sum &= 0xFFFF;sum++;}}return ~sum; }int main() {uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};uint16_t checksum = calculate_checksum(data, sizeof(data));// 发送数据和校验和printf("Data: ");for (size_t i = 0; i < sizeof(data); i++) {printf("%02X ", data[i]);}printf("\nChecksum: %04X\n", checksum);// 接收方重新计算校验和uint16_t received_checksum = calculate_checksum(data, sizeof(data));if (received_checksum == checksum) {printf("Data integrity verified.\n");} else {printf("Data has been tampered with or corrupted.\n");}return 0; }
-
使用循环冗余校验(CRC):
- CRC是一种更强大的校验方法,通常用于检测数据传输中的错误。它通过生成多项式来计算校验值,具有更高的检测能力。
- 示例:
#include <stdio.h> #include <stdint.h>uint16_t crc16(const uint8_t *data, size_t length) {uint16_t crc = 0xFFFF;for (size_t i = 0; i < length; i++) {crc ^= data[i];for (int j = 0; j < 8; j++) {if (crc & 0x0001) {crc >>= 1;crc ^= 0xA001; // Polynomial 0x1021} else {crc >>= 1;}}}return crc; }int main() {uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};uint16_t crc = crc16(data, sizeof(data));// 发送数据和CRCprintf("Data: ");for (size_t i = 0; i < sizeof(data); i++) {printf("%02X ", data[i]);}printf("\nCRC: %04X\n", crc);// 接收方重新计算CRCuint16_t received_crc = crc16(data, sizeof(data));if (received_crc == crc) {printf("Data integrity verified.\n");} else {printf("Data has been tampered with or corrupted.\n");}return 0; }
-
使用消息认证码(MAC):
- MAC是一种基于密钥的校验方法,不仅可以检测数据的完整性,还可以验证数据的来源。常见的MAC算法包括HMAC(基于哈希的消息认证码)。
- 示例:
#include <stdio.h> #include <openssl/hmac.h> #include <openssl/sha.h>void hmac_sha256(const unsigned char *key, size_t key_len, const unsigned char *data, size_t data_len, unsigned char *out) {HMAC_CTX *ctx = HMAC_CTX_new();HMAC_Init_ex(ctx, key, key_len, EVP_sha256(), NULL);HMAC_Update(ctx, data, data_len);HMAC_Final(ctx, out, NULL);HMAC_CTX_free(ctx); }int main() {const char *key = "secret_key";const char *data = "Hello, World!";unsigned char mac[SHA256_DIGEST_LENGTH];hmac_sha256((const unsigned char *)key, strlen(key), (const unsigned char *)data, strlen(data), mac);// 发送数据和MACprintf("Data: %s\n", data);printf("MAC: ");for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {printf("%02X", mac[i]);}printf("\n");// 接收方重新计算MACunsigned char received_mac[SHA256_DIGEST_LENGTH];hmac_sha256((const unsigned char *)key, strlen(key), (const unsigned char *)data, strlen(data), received_mac);if (memcmp(mac, received_mac, SHA256_DIGEST_LENGTH) == 0) {printf("Data integrity verified.\n");} else {printf("Data has been tampered with or corrupted.\n");}return 0; }
说明:
- 校验和:适用于简单的数据完整性验证,计算速度快,但检测能力有限。
- CRC:适用于更复杂的错误检测,检测能力更强,广泛应用于通信协议中。
- MAC:不仅验证数据完整性,还能验证数据来源,适用于需要更高安全性的场景。
-
参考资料
- HeadFirstC嗨翻C语言
- 通义千问
- 课程 mindmap
- Mermaid Live Editor