20211325 2023-2024-1 《信息安全系统设计与实现(上)》第十一周学习笔记
一、任务要求
自学教材第13章,提交学习笔记(10分),评分标准如下 1. 知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容 (4分) “我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题” 核心是要求GPT:“请你以苏格拉底的方式对我进行提问” 然后GPT就会给你提问,如果不知道问题的答案,可以反问AI:“你的理解(回答)是什么?” 如果你觉得差不多了,可以先问问GPT:“针对我XXX知识点,我理解了吗?” GPT会给出它的判断,如果你也觉得自己想清楚了,可以最后问GPT:“我的回答结束了,请对我的回答进行评价总结”,让它帮你总结一下。
2. 问题与解决思路,遇到问题最先使用chatgpt等AI工具解决,并提供过程截图(3分)
3. 实践过程截图,代码链接(2分)
4. 其他(知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等)(1分)
二、教材知识总结
学习目标
通过本章的学习,了解TCP/IP协议及其应用,包括TCP/IP协议栈,ip地址,主机名,DNS,IP数据包和路由器;理解TCP/IP协议簇中的UDP和TCP协议、端口号和数据流;理解套接字,并通过简单编程来加深理解。第二部分介绍web和CGI编程,解释HTTPD服务器,及HTTP编程模型,理解web服务器的配置和运行。
什么是网络编程
网络编程就是使用IP地址,或域名,和端口连接到另一台计算机上对应的程序,按照规定的协议(数据格式)来交换数据。网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。
网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机。数据传递本身没有多大的难度,不就是把一个设备中的数据发送给两外一个设备,然后接受另外一个设备反馈的数据。
TCP/IP协议
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。
TCP/IP(Comer 1988,2001;RFC1180 1991)是互联网的基础。TCP代表传输控制协议。IP 代表互联网协议。目前有两个版本的IP,即IPv4和IPv6。IPv4使用32位地址,IPv6则使用128位地址。
如图所示为 TCP/IP 的各个层级以及每一层级的代表性组件及其功能。
翻译过来就是
TCP/IP 网络中的数据流路径。
IP主机和IP地址
每个主机都有一个本地主机名localhost,默认 IP地址为 127.0.0.1。
本地主机的链路层是一个回送虚拟设备,它将每个数据包路由回同一个localhost。这个特性可以让我们在同一台计算机上运行TCP/IP 应用程序,而不需要实际连接到互联网。
IP地址分为两部分,即 NetworkID 字段和HostID字段。根据划分,IP 地址分为A~E类。
类别 | 最大网络数 | IP地址范围 | 单个网段最大主机数 | 私有IP地址范围 |
---|---|---|---|---|
A | 126(2^7-2) | 1.0.0.1-127.255.255.254 | 16777214 | 10.0.0.0-10.255.255.255 |
B | 16384(2^14) | 128.0.0.1-191.255.255.254 | 65534 | 172.16.0.0-172.31.255.255 |
C | 2097152(2^21) | 192.0.0.1-223.255.255.254 | 254 | 192.168.0.0-192.168.255.255 |
IP协议
IP协议用于在 IP主机之间发送/接收数据包。IP尽最大努力运行。IP 主机只向接收主机发送数据包,但它不能保证数据包会被发送到它们的目的地,也不能保证按顺序发送。这意味着IP 并非可靠的协议。
IP报头格式
路由器
是接收和转发数据包的特殊IP主机。一个IP数据包可能会经过许多路由器,或者跳跃到达某个目的地。每个IP包在IP报头中都有一个8位生存时间(TTL)计数,其最大值为255。
UDP
UDP(用户数据报协议)(RFC768 1980;Comer 1988)在IP上运行,用于发送/接收数据报。与IP类似,UDP不能保证可靠性,但是快速高效。
TCP
TCP(传输控制协议)是一种面向连接的协议,用于发送/接收数据流。TCP也可在IP 上运行,但它保证了可靠的数据传输。
端口编号
端口号是分配给应用程序的唯一无符号短整数。要想使用UDP或TCP,应用程序(进程)必须先选择或获取一个端口号。前1024个端口号已被预留。其他端口号可供一般使用。应用程序可以选择一个可用端口号,也可以让操作系统内核分配端口号。
端口号的范围从0到65535。
使用 netstat -nao
可以查看系统中端口的活动情况。
一些端和服务之间的联系
网络和主机字节序
网络和主机字节序:计算机可以使用大端字节序,也可以使用小端字节序。在互联网上,数据始终按网络序排列,这是大端。在小端机器上,例如基于Intel x86的PC,htons()、htonl()、ntohs()、ntohl()等库函数,可在主机序和网络序之间转换数据。
TCP/IP中的数据流
应用程序层的数据被传递到传输层,传输层给数据添加一个TCP或UDP 报头来标识使用的传输协议。合并后的数据被传递到IP 网络层,添加一个包含 IP地址的IP 报头来标识发送和接收主机。然后,合并后的数据再被传递到网络链路层,网络链路层将数据分成多个帧,并添加发送和接收网络的地址,用于在物理网络之间传输。IP地址到网络地址的映射由地址解析协议(ARP)执行(ARP1982)。在接收端,数据编码过程是相反的。每一层通过剥离数据头来解包接收到的数据、重新组装数据并将数据传递到上一层。发送主机上的应用程序原始数据最终会被传递到接收主机上的相应应用程序。
socket编程
套接字地址
struct sockaddr_in { sa_family_t sin_family; // AF_INET for TCP/IP in_port_t sin_port; // port number struct in_addr sin_addr; // IP address ); struct in_addr { // internet address uint32_t s_addr; // IP address in network byte order );
-
TCP/IP 网络的 sin_family 始终设置为 AF_INET。
-
sin_port包含按网络字节顺序排列的端口号。
-
sin addr是按网络字节顺序排列的主机IP地址。
套接字API
-
套接字描述符
#include <sys/socket.h> int socket (int domain, int type, int protocal); 成功返回文件(套接字)描述符,出错返回-1
-
地址格式 sockaddr_in定义
struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; };
-
将套接字与地址绑定
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr * addr, socklen_t len); 返回值:成功返回0,出错返回-1
-
建立连接
#include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t len); 返回值:成功返回0,出错返回-1
-
监听listen
#include <sys/socket.h> Int listen(int sockfd, int backlog); 返回值:成功返回0,出错返回-1
-
accept获得连接请求并建立连接。
#include <sys/socket.h> Int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len); 返回值:成功返回文件(套接字)描述符,出错返回-1
-
数据传输
send
#include <sys/socket.h> Int send(int sockfd, const void *buf, size_t nbytes, int flags); 返回值:成功返回发送的字节数,出错返回-1
recv
#include <sys/socket.h> int recv(int sockfd, const void *buf, size_t nbytes, int flags); 返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,
recvfrom
#include <sys/socket.h> int recv(int sockfd, void *restrict buf, size_t len, int flag, struct sockaddr *restrict addr, socklen_t *restrict len); 返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,
可以获得发送者的地址,recvfrom通常用于无连接套接字。
Web编程
Web 编程通常包括Web开发中涉及的编写、标记和编码,其中包括Web 内容、Web 客户机和服务器脚本以及网络安全。狭义上,Web编程指的是创建和维护 Web 页面。Web编程中最常用的语言是HTML、XHTML、JavaScript、Perl5和 PHP。
Http编程模型
HTTP是一种基于服务器-客户机的协议,用于互联网上的应用程序。它在TCP上运行,因为它需要可靠的文件传输。
CGI编程
CGI代表通用网关接口(RFC 3875 2004)。它是一种协议,允许 Web服务器执行程序,根据用户输入动态生成Web 页面。使用CGI.Web 服务器不必维护数百万个静态Web 页面文件来满足客户机请求。相反,它通过动态生成Web 页面来满足客户机请求。
在 CGI编程模型中,客户机发送一个请求,该请求通常是一个HTML表单,包含供服务器执行的 CGI程序的输入和名称。在接收到请求后,httpd服务器会派生一个子进程来执行 CGI程序。CGI程序可以使用用户输入来查询数据库系统,如 MySQL,从而根据用户输入生成 HTML 文件。当子进程结束时,httpd服务器将生成的HTML 文件发送回客户机。
编程练习1:使用C语言实现简单UDP传输
使用C/S架构
客户端:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <arpa/inet.h> #define MAXBUF 256 int main(int argc, char const *argv[]) { int s = 0; int n = 0; int reuse = 1; int port = 1987; struct sockaddr_in srv; char buf[MAXBUF] = {0}; /*解析参数*/ if (argc != 2) { printf("Usage:%s ServerIP\n", argv[0]); return -1; } bzero(&srv, sizeof(srv)); srv.sin_family = PF_INET; srv.sin_addr.s_addr = inet_addr(argv[1]); srv.sin_port = htons(port); /*创建 UDP 套节字*/ s = socket(AF_INET, SOCK_DGRAM, 0); if(s<0){ perror("socket"); return -1; } while(1){ memset(buf, 0, MAXBUF); /*读取用户输入到buf中*/ fgets(buf, MAXBUF, stdin); /*通过套节字 s 向服务器发送数据*/ if ((n = sendto(s, buf, strlen(buf), 0, (struct sockaddr *) &srv, sizeof(struct sockaddr))) < 0) { perror("sendto"); return -1; }else{ printf("send to %s(port=%d) len %d:%s\n", argv[1], port, n, buf); } } }
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#define BUFLEN 256
#define PORT 1234
char line[BUFLEN];
struct sockaddr_in me,client;
int sock,rlen,clen =sizeof(client);
int main()
{
printf("1.create a UDP socket\n");
sock :socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
printf("2.fill me with server address and port number\n");
memset((char *)&me,0,sizeof(me));
me.sin_family =AF_INET;
me.sin_port =htons(PORT);
me.sin_addr.s_addr =htonl(INADDR_ANY);//use localhost
printf("3.bind socket to server IP and port\n");
bind(sock,(struct sockaddr*)&me,sizeof(me));
printf("4.wait for datagram\n");
while(1){
memset(line,0,BUFLEN);
printf("UDP server:waiting for datagram\n");
rlen=recvfrom(sock,line,BUFLEN,0,(struct sockaddr *)&client,&clen);
printf("received a datagram from [host:port]=[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
printf("rlen=%d:line=%s\n",rlen,line);
printf("send reply\n");
sendto(sock,line,rlen,0,(struct sockaddr*)&client,clen);
}
}
编译运行:
编程练习2:使用C语言实现简单TCP传输
服务器(由于课本提供代码有些问题,以下是改动之后的正确代码):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> // Added inclusion of unistd.h for close function #define MAX 256 #define SERVER_HOST "localhost" #define SERVER_IP "127.0.0.1" #define SERVER_PORT 1234 // Defined SERVER_PORT here struct sockaddr_in server_addr, client_addr; int mysock, csock; void server_init() { printf("================ server init =================\n"); // 1. Create a TCP socket printf("1 : create a TCP socket\n"); mysock = socket(AF_INET, SOCK_STREAM, 0); if (mysock < 0) { perror("socket call failed"); exit(1); } // 2. Fill server_addr with server's IP and PORT# printf("2 : fill server_addr with server's IP and PORT#\n"); memset((char*)&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Use localhost server_addr.sin_port = htons(SERVER_PORT); // 3. Bind socket to server address printf("3 : bind socket to server address\n"); int r = bind(mysock, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (r < 0) { perror("bind failed"); exit(3); } printf("hostname = %s port = %d\n", SERVER_HOST, SERVER_PORT); printf("4 : server is listening ....\n"); // 4. Listen for incoming connections listen(mysock, 5); // Queue length = 5 printf("================== init done ===============\n"); } int main() { int n; char line[MAX]; server_init(); while (1) { // Try to accept a client request printf("server: accepting new connection ....\n"); socklen_t len = sizeof(client_addr); csock = accept(mysock, (struct sockaddr*)&client_addr, &len); if (csock < 0) { perror("server: accept error"); exit(1); } printf("server: accepted a client connection from\n"); printf("Client: IP=%s port=%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // Processing loop: client_sock <== data ==> client while (1) { n = recv(csock, line, MAX, 0); if (n <= 0) { printf("server: client closed the connection, server loops\n"); close(csock); break; } // Show the received line string printf("server: received n=%d bytes; line=%s\n", n, line); // Echo line to client n = send(csock, line, n, 0); printf("server: sent n=%d bytes; echo=%s\n", n, line); } } return 0; }
客户端:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> // Added inclusion of arpa/inet.h for bzero function #define MAX 256 #define SERVER_HOST "localhost" #define SERVER_PORT 1234 struct sockaddr_in server_addr; int sock, r; int client_init() { printf("================ client init =================\n"); // 1. Create a TCP socket printf("1 : create a TCP socket\n"); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket call failed"); exit(1); } // 2. Fill server_addr with server's IP and PORT# printf("2 : fill server_addr with server's IP and PORT#\n"); memset((char*)&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Use localhost server_addr.sin_port = htons(SERVER_PORT); // 3. Connect to server printf("3 : connecting to server ...\n"); r = connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (r < 0) { perror("connect failed"); exit(3); } printf("4 : connected OK to\n"); printf("-----------------------------\n"); printf("Server hostname=%s PORT=%d\n", SERVER_HOST, SERVER_PORT); printf("-----------------------------\n"); printf("================ init done ================\n"); } int main() { int n; char line[MAX], ans[MAX]; client_init(); printf("********** processing loop ************\n"); while (1) { printf("input a line : "); bzero(line, MAX); // Zero out line[] fgets(line, MAX, stdin); // Get a line from stdin line[strlen(line) - 1] = '\0'; // Kill \n at end if (line[0] == '\0') // Exit if NULL line exit(0); // Send line to server n = write(sock, line, MAX); printf("client: wrote n=%d bytes; line=%s\n", n, line); // Read a line from sock and show it n = read(sock, ans, MAX); printf("client: read n=%d bytes; echo=%s\n", n, ans); } return 0; }
编译运行:
三、课堂代码实现
本次课堂代码已包含在课本代码实现中