http-ethereal-trace-1文件的HTTP报文中的第一条HTTP分组请求报文如下
客户端IP地址:192.168.1.102
服务器IP地址:128.119.145.12
分析报文格式:
请求行:
方法字段:GET
当浏览器请求一个对象时,使用GET方法,绝大部分HTTP请求报文都用GET方法
URL字段:/ethereal-labs/lab2-1.html
请求对象的标识,表示浏览器正在请求的对象,本例是这个html文件
HTTP版本字段:HTTP/1.1
表示浏览器实现的HTTP版本,本例是1.1版本
首部行:
Host: gaia.cs.umass.edu
指明请求对象所在的主机
User-Agent: Mozilla-5.0
指明用户代理,即向服务器发送请求的浏览器类型
Accept:浏览器可接受的MIME类型
Accept-Language: 浏览器可接受的相应的语言种类
Accept-Encoding: 浏览器能够进行解码的数据编码方式
Accept-Charset: 浏览器可接受的字符集(或者说客户机采用的编码格式)
Connection: 告诉服务器,请求完成后,是否保持连接
实体体:
使用GET方法时实体体为空
第二条HTTP分组的响应报文如下:
初始状态行:
协议版本字段:HTTP/1.1
状态码:404表示请求的文档不在服务器上
相应状态信息:Not Found
首部行:
Date: 服务器产生并发送该响应报文的日期和时间
Server: 产生该响应报文的服务器类型
Vary: 判断是否可使用缓存
Accept-Ranges: 表明服务器是否支持指定范围的请求,如bytes表示支持字节请求
Content-Length: 服务器返回的消息正文的长度
Connection: 回送数据后,是否保持连接
Content-Type: 服务器回送数据的类型
实体体:
所请求的对象本身
HTTP报文中与缓存有关的字段:
HTTP 状态码(status code):
200 请求成功,浏览器会把响应回来的信息显示在浏览器端
304 第一次访问一个资源后,浏览器会将该资源缓存到本地;第二次再访问该资源时,如果该资源没有发生改变或失效,那么服务器响应给浏览器 304 状态码,告诉浏览器使用本地缓存的资源
Last-Modified: 表示这个响应资源的最后修改时间。web 服务器在响应请求时,告诉浏览器资源的最后修改时间
If-Modified-Since: 当资源过期时(使用 Cache-Control 标识的 max-age),发现资源具有 Last-Modified 声明,则再次向 WEB 服务器请求时,带上 If-Modified-Since,表示请求时间。WEB 服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源有被改动过,则响应资源内容(写在响应消息包体内),HTTP 200 ;若最后修改时间较旧,说明资源无新修改,则响应 HTTP 304 (无需包体,节省流量),告知浏览器继续使用缓存
http-ethereal-trace-2文件的HTTP报文中的第一条HTTP分组如下
可以看到请求报文没有 If-Modified-Since 字段,其响应报文状态码为200,并且包含请求文件内容,说明没有使用缓存
而第二条分组如下:
请求报文有 If-Modified-Since 字段,其响应报文状态码为304,相应状态描述为Not Modified,且不包含请求文件内容,说明使用了缓存
当客户端请求的HTML文件相当长时,一个 TCP 数据包不能容纳。此时单个 HTTP 响应消息由 TCP 分成几个部分,每个部分包含在单独的 TCP 报文段中
如上图,该HTTP请求的数据被分成4个TCP报文,编号分别为10,11,13,14
共发出了3个GET请求,第一个用于请求顶层的HTML网页,后两个用于请求嵌入的两种图片
根据时间线可以看出,在第一个GET请求的响应报文到达之前,第二个GET请求已经发出,因此二者是并行的
目标网页的IP地址:128.119.245.12
嵌入图片对象cover.jpg的IP地址:134.241.6.82
本实验中主要对基本认证进行分析,基本认证中的认证相关字段:
(1)服务器响应状态码与状态描述:当服务器响应状态码为 401 时,表明服务器资源需要认证
其状态描述为 Unauthorized,表明未通过认证;当响应200 OK时,表明通过认证,正常响应
(2)当用户提供用户名和密码后,重新提出请求时: Authorization: Basic xxxxxxxxxx
Authorization 字段表明在请求中,提供了需要的认证方式和认证信息(已经经过加密)
如图,第对一次GET的响应状态描述为 Unauthorized
WWW-Authenticate字段的值为Basic realm="eth-students only"
如图,查看 Authorization 字段可知第二次请求的认证方式为Basic,服务器响应状态描述为OK
下图是运行MailClient.py时抓取到的SMTP协议
(1)HELO:发送端的主机名
(2)AUTH LOGIN:身份信息验证
(3)User:发送者用户名
(4)Pass:发送者密码
(5)MAIL FROM:发信人
(6)PCRT TO:预期的收信人
(7)DATA:邮件主题
(8)QUIT:断开与邮件服务器的连接
(1)220:服务就绪
(2)235:身份验证成功
(3)250:请求命令完成
(4)334:等待用户输入验证信息
(5)354:开始邮件输入
From: XXXXXX
To: XXXXXX
Subject: XXXXXX
Content-Type: XXXXXX
上述抓包实例如下:
#import socket module
from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM)
#准备一个服务端套接字
serverSocket.bind(('',6789)) #绑定本地地址,设置端口号为6789
serverSocket.listen(1) #使套接字进入监听状态,并设置连接队列大小为1
while True:
#建立连接
print('Ready to serve...')
connectionSocket, addr = serverSocket.accept()
#当监听到客户的连接请求后,服务器就调用accept返回并接收这个连接
#connectionSocket是accept()接收到一个客户端连接请求后返回的一个新的套接字,它代表了服务端和客户端的连接
#后面可以用于读取数据以及关闭连接
try:
message = connectionSocket.recv(1024) #获取客户端发送的报文,一次最多接收1024个字节
filename = message.split()[1] #这两行是在从报文中获取请求的文件名
f = open(filename[1:]) #去掉第一个符号"/"
outputdata = f.read() #读取文件
#Send one HTTP header line into socket
header = ' HTTP/1.1 200 OK\nConnection: close\nContent-Type: text/html\nContent-Length: %d\n\n' % (len(outputdata))
#创建HTTP响应报文
connectionSocket.send(header.encode()) #发送响应消息头部内容
#将请求文件的内容发送给客户
for i in range(0, len(outputdata)):
connectionSocket.send(outputdata[i].encode())
connectionSocket.close()
except IOError:
#如果未找到文件,发送差错报文
header = ' HTTP/1.1 404 Not Found'
connectionSocket.send(header.encode())
#关闭客户端套接字
connectionSocket.close()
serverSocket.close()
Hello world!
请求 HelloWorld.html 文件
请求不存在的文件
from socket import *
from base64 import *
#邮件内容
subject = "I love computer networks!"
contenttype = "text/plain"
msg = "I love computer networks!"
endmsg = "\r\n.\r\n"
# 选择一个邮件服务器,smtp.163.com:123.126.97.4是网易发送邮箱服务
mailserver = "smtp.163.com"
#发件人邮箱地址和收件人邮箱地址
sender_address = "[email protected]"
receiver_address = "[email protected]"
#发送者的账户和密码
username = b64encode("[email protected]".encode()).decode()
password = b64encode("UCOBVJOQDKYYHLJP".encode()).decode()
# 创建一个用户端套接字并和邮件服务器建立TCP连接,25是SMTP的默认端口号
clientSocket = socket(AF_INET,SOCK_STREAM)
clientSocket.connect((mailserver,25))
recv = clientSocket.recv(1024).decode()
print(recv)
if recv[:3] != '220':
print('220 reply not received from server.')
#服务器返回状态码220代表已建立TCP连接,服务就绪
#向服务端发送HELO命令并打印服务器的应答信息
heloCommand = 'HELO Alice\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024).decode()
print(recv1)
if recv1[:3] != '250':
print('250 reply not received from server.')
#服务器返回状态码250代表请求动作正确完成
#发送AUTH LOGIN命令,开始验证身份
clientSocket.sendall('AUTH LOGIN\r\n'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if recv[:3] != '334':
print('334 reply not received from server.')
#服务器返回状态码334代表等待用户输入验证信息
#发送经base64编码的用户名
clientSocket.sendall((username + '\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
print('334 reply not received from server')
#服务器返回状态码334代表等待用户输入验证信息
#发送经base64编码的密码
clientSocket.sendall((password + '\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '235'):
print('235 reply not received from server')
#服务器返回状态码235代表验证成功
#发送MAIL FROM命令,包含发件人的邮箱地址
clientSocket.sendall(('MAIL FROM: <' + sender_address + '>\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
print('250 reply not received from server')
#服务器返回状态码250代表请求动作正确完成
#发送RCPT TO命令,包含收件人的邮箱地址
clientSocket.sendall(('RCPT TO: <' + receiver_address + '>\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
print('250 reply not received from server')
#服务器返回状态码250代表请求动作正确完成
#发送DATA命令,表示即将开始发送邮件内容
clientSocket.send('DATA\r\n'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '354'):
print('354 reply not received from server')
#服务器返回状态码354代表开始邮件输入,以"."结束
#发送邮件内容
message = 'from:' + sender_address + '\r\n'
message += 'to:' + receiver_address + '\r\n'
message += 'subject:' + subject + '\r\n'
message += 'Content-Type:' + contenttype + '\t\n'
message += '\r\n' + msg
clientSocket.sendall(message.encode())
clientSocket.sendall(endmsg.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
print('250 reply not received from server')
#服务器返回状态码250代表请求动作正确完成
#发送QUIT命令断开与邮件服务器的连接
clientSocket.sendall('QUIT\r\n'.encode())
#关闭套接字
clientSocket.close()