计算机系统基础II之:网络
Weiquan Huang不得不吐槽你软的计算机网络教育真的十分零散,建议从计算机系引入计网,并且删除《无人系统设计》捏
2-20 net
这只是入个门
局域网有一个hub/switch,被称为交换机,可以将与其相连的机器进行局部通信;通信的时候是需要标识的,这里在内部是使用Mac来标识地址的
局域网之间可以通过bridge来进行通信
上面的统称为局域网,LAN,因为他们的范围有限;不同的局域网之间可以通过路由器来链接,路由器之间的连接为WAN广域网
如果要从某个主机将数据传给另一个主机,那么网络上是有一些机制的,比如子网掩码可以获取到目标主机和发送主机是否在同一个局域网这个信息(IP地址和子网掩码255.255.255.0取与);网关是局域网和外部网络通信的接口,连接网络的节点:
IP地址?IPv4是32位4字节的地址(其实是按照整型数来存储的),不能作为主机的唯一标识,一般是分层的(类似于你可能在校内网被分配了一个地址,但是校内网在整个世界对外的地址是另一个地址),通常用点十进制方式来表示,以下是一些常用的IP地址:
- 127.0.0.1 localhost主机的回送地址loopback address,便于在同一个主机上的客户与服务端进行通信(localhost:3000 localhost:8080 localhost:3306等)
- 类似于192.168.155.75、10.181.121.137的地址是私有地址,不直接连接到公共互联网
由于IP地址比较抽象,难以标识,因此用域名来让其可视化——看起来更容易记住:域名和IP地址一一对应,同时域名也是分层次的,比如com edu等为第一层,sjtu cmu等为第二层,申请了第二层的用户就可以追加域名层级,创建新的域名
下面是如何用代码将16进制数转化成点十进制IP地址:
sscanf
函数,将给出的字符串,通过格式化字符串(带%d
等格式化符的),格式化成为数字,然后传给第三个及以后的变量,需要用指针传递);格式化字符串支持正则表达式
1 | int convert = sscanf("20191103", "%04d%02d%02d", &year, &month, &day); |
uint32_t htonl(uint32_t hostlong)
函数,传入32位16进制数,h-host, to-to, n-net, l-unsigned long
,转成网络字节顺序的16进制数inet_ntop(AF_INET, (struct in_addr)* inaddr, char *buf, int size)
将IPv4或者IPv6的Internet网络地址转换成为Internet标准格式的字符串(点十进制)
协议portocol是干嘛的?我的理解是用来规范不同不兼容的网络之间的行为的,比如规范地址标识形式(如大端法IP地址)、规范网络包发送形式等

网络包有包头,涵盖了目标地址、起始地址和数据大小这些量;图中协议软件会给数据加上PH标识目标主机的头和FH1标识当前局域网位置的头,经过路由器之后会更改局域网的包头,在之后会解开两个包头,获取数据
套接字socket(这章感觉还不如看书呢,太多莫名其妙的名词了,听课性价比比那些cache memory这种讲原理的低多了…),一个用于连接的端口,是本机的一个进程,使用主机地址:port来标识,比如localhost:3000就是前端进程;客户端主机给的socket一般是随机生成的,但是服务端主机给的socket一般是特定的知名端口号;一个链接是一个客户端socket address和服务器socket address构成的元组 (cliaddr:cliport, servaddr:servport)
2-21 socket
这玩意听起来可能很装逼,我刚开始甚至以为是口袋(pocket)来着,听得云里雾里…,结果常用的翻译是插座🤣
书接上文,socket是一个用于网络通信的接口
该图展示了创建一个client/server socket的步骤:
- client端
getaddrinfo
将域名转换成一个struct addrinfo
的链表,这个链表存放了域名所对应的IP地址,注意不止有一个IP地址socket
会创建一个在本机的socket接口,在软件层的意义就在于是一个file descriptor
,返回一个fd的ID号- 可以通过这个fd号来进行向服务端的请求,即
connect
,连接成功后即可通过这个fd进行读写通信,会中止程序直到连接成功或者超时
- server端
bind
用于将socket与自身的主机IP与端口绑定,这样子意味着这个socket是作为一个服务端的socket,用于监听connect请求;由于服务器socket address一般是固定的,所以这样子做还是有意义的listen
用于将自身标识为服务器端socket,赋予其能够监听客户端请求的功能accept
用于等待客户端的connect请求,它会中止程序,直到超时或者接收到请求
数据结构如下图,AF_INET表示IPv4的地址
代码感觉没必要粘贴,我觉得要用的时候会看懂就行,考试可能也会考相关内容,感觉这个比较像进程+file io,估计没有signal那么难
2-22 web
HTTP、TCP、IP都是网络通信使用的协议,但是作用和层次不同:
- HTTP是应用层的协议,主要定义了浏览器和网站服务器之间的如何通信,request&response
- TCP是传输层协议,负责在双方之间建立可靠的数据传输通道,负责把数据分割成合适的数据包来进行传输,负责把接收到的数据包重组
- IP是网际协议,负责分配IP地址,并根据地址把数据包从源头路由到目的地,就是通过通信地址传输内容
- 所以从上到下,HTTP依赖于TCP,TCP依赖于IP。HTTP面向应用,TCP面向传输,IP面向网络
- 具体来说,HTTP规定要web客户端/服务器要向对面传送的内容,TCP规定如何将要传送的内容打包、传输打包数据、重组打包数据,IP负责提供寻址,方便于TCP的通道打通
具体来说,一次Web请求的工作流程是:
- 浏览器使用HTTP协议组装请求报文
- 请求报文被TCP协议分割成多个数据包,并建立TCP连接
- TCP数据包被IP协议根据IP地址路由到服务器
- 服务器收到数据包后,由TCP协议重组成完整的HTTP请求
- 服务器使用HTTP协议处理请求,组装响应报文
- 响应报文被TCP协议分割成数据包通过IP路由回浏览器
- 浏览器收到数据包后由TCP重组,得到完整的HTTP响应
web内容
- 磁盘文件:称为静态内容,将其内容返回给客户端
- 可执行文件:称为动态内容,将执行后的输出返回给客户端,比如给GPT问问题,它运行好其代码得到输出即可返回给客户端
对于一个url(通用资源定位符),在事务过程中,客户端和服务器使用的是url的不同部分,例如客户端使用前缀http://www.google.com:80 ,来决定客户端与哪一类服务器、服务器地址、端口地址通信,服务器使用/index.html来发现在它文件系统中的文件,确定内容为静态或动态
http://bluefish.ics.cs.cmu.edu:8000/cgi-bin/adder?15000&213
,将参数15000和213传给可执行文件./cgi-bin/adder
服务动态内容
- 客户端如何将参数传递给服务器?
?
分隔了服务器和参数,&
分隔了不同的参数 - 服务器如何将参数传递给子进程?
调用fork
来创建一个子进程,调用execve
执行所对应的可执行文件,调用execve
之前,将CGI环境变量QUERY_STRING设置为15000&213
,程序运行的时候可以通过getenv
函数来引用这个参数
如下是示例程序,其中strchr(buf, '&')
表示获取到buf
字符串的第一个&
的地址 - 服务器如何将其他的参数传递给子进程?用其他的环境变量
- 子进程将它的输出发送到哪里?
运行CGI程序之前,将子进程的输出直接重定向到通向客户端的socketdup2(int oldfd, int newfd)
将newfd重定向到oldfd的file descriptor
微型web服务器
要求如下:‘
代码在2-22 web的PPT里面,建议遇到不会的问chat
思路:
- 首先需要通过socket,经历
getaddrinfo->socket->bind->listen->accept
,等待与客户端的链接;这里一次性只能处理一个请求 - 读取请求头,解析请求头是否为
GET
请求,使用的是大小写敏感的strcasecmp
用来对比字符串 - 判断是静态/动态内容,如果文件不合法或者不可读/执行,那么返回403;文件不存在则返回404
- 如果是错误则需要封装一个输出错误信息的函数
- 对静态/动态的不同内容,来进行不同的参数获取等操作