计算机网络学习笔记

目录

第一章 绪论

第二章 应用层

套接字实验


第一章 绪论

本文作为记录学习过程的笔记,以《计算机网络——自顶向下的方法》为参考教材。

待续。。。

第二章 应用层

URI:URI是由某个协议方案表示的资源的定位标识符,绝对URI指涵盖全部必要信息。格式如:

URL:区分URI,可以说URL是URI的一个子集,指资源在互联网上的地址。

cookie:待续。。。

C/S模式:


P2P模式:

套接字实验

实验一:用python开发一个简单的Web服务器,它仅能处理一个请求:

  1. 当一个客户(浏览器)联系时创建一个套接字;
  2. 从这个连接接收HTTP请求;
  3. 解释该请求以确定所请求的特定文件;
  4. 从服务器的文件系统获取请求文件;
  5. 创建一个由请求的文件组成的HTTP响应报文,报文前面由首部行;
  6. 经TCP连接向请求的浏览器发送响应,如果浏览器请求的文件不存在服务器中,返回“404 NOT FOUND”的差错报文。

从网站渠道获取相关代码,但调试过程遇到各种报错。

问题1:[WinError10061]由于目标计算机积极拒绝,无法连接。

 该问题分析是本机的网络安全设置阻止了非法连接,我没找到在哪个设置放行连接,因此作罢,改用浏览器作为客户端请求。

问题2:[WinError10038]在一个非套接字上尝试了一个操作。

 该问题是指在套接字不存在后进行的‘recv’接收,因此无法进行,该原因是代码中:

serverSocket = socket(AF_INET,SOCK_STREAM)
serverSocket.bind(("127.0.0.1",9999))
serverSocket.listen(1)   #听一个连接用户
connectionSocket, addr = serverSocket.accept()      
while True:
    print('wait for connection..')
    try:
        data = connectionSocket.recv(1024)   #接受1K的数据
        print(data)   #data是一个get的http报文
        if not data:
            continue
        filename = data.split()[1]
        f = open(filename[1:],encoding=("utf-8"))
        outputdata = f.read()
        outputdata = 'HTTP/1.1 200 OK\r\nServe:MakeByMyself\r\nConten-Type:HTML\r\n\r\n'+ outputdata  #接收以后回复报文
        #connectionSocket.send(header.encode())
        for i in range (0,len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()

​

connectionSocket, addr = serverSocket.accept()   不在while循环体内,而每次while循环都会connectSocket.close()关闭套接字,因此后续操作会产生在非套接字上尝试了一个操作的报错。将connectionSocket, addr = serverSocket.accept() 放到while循环体内即可解决。

问题3:显然浏览器中的报文比编译器中的工整好看,但却缺少了‘Response Headers’

 该问题主要因为服务器response报文只有“HTTP/1.1 200 OK”一个状态,浏览器不显示在response报文中,主动添加response报文格式即可。

 最终运行下列代码:

from socket import *

serverSocket = socket(AF_INET,SOCK_STREAM)   #建立socket套接口对象
serverSocket.bind(("127.0.0.1",9999))        #使用bind函数将主机地址和端口号绑定
serverSocket.listen(1)                       #单线程,等待一个连接用户
#connectionSocket, addr = serverSocket.accept()    注释掉
while True:
    print('wait for connection..')
    connectionSocket, addr = serverSocket.accept()    #接受连接
    try:
        data = connectionSocket.recv(1024)            #接受1K的数据
        print(data)                                   #data是一个get的http报文
        if not data:                                  #等待数据传输
            continue
        filename = data.split()[1]            #将请求数据分割出文件名,[1]取数组第一行
        f = open(filename[1:],encoding=("utf-8"))     #打开文件
        outputdata = f.read()                         #读出数据结合response报文一起传
        outputdata = 'HTTP/1.1 200 OK\r\nServe:MakeByMyself\r\nConten-Type:HTML\r\n\r\n'+ outputdata  #接收以后回复报文
        for i in range (0,len(outputdata)):           #循环调用send()将数据传到客户端
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()                      #关闭连接
                                #\r\n是区分转行,而\r\n\r\n是报文结束标志
    except IOError:                                   #请求文件不存在,报404错误
        outputdata = 'HTTP/1.1 404 NOT \r\nFOUNDServe:MakeByMyself\r\nConten-Type:HTML\r\n\r\n'                        
        for i in range(0, len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()

得到报文,并分析报文:

报文分析:

HTTP的请求报文由3部分组成(请求行、请求头、请求体),同理其响应报文也由3部分组成(响应行、响应头、响应体)。

请求行:包括①request method,此处用‘GET’方法,GET和POST是最常见的HTTP方法;

②request URL;③协议名称及版本号。 

请求头:包含下图所有内容

  •  Accept   请求报文通过一个Accept报文属性告诉服务器,客户端接收什么类型的响应;Accept属性的值可以为一个或多个MIME类型的值,

MIME格式:大类型/小类型[;参数]     Accept:text/plain  

比如:text/html,html文件             text/css,css文件              image/*,所有图片文件

  • Accept-Encoding: 声明浏览器接受的压缩编码类型,如gzip, deflate
  • Accept-Language:浏览器通知服务器,浏览器支持的语言。各国语言(国际化i18n)
  • Cache-Control:用于 HTTP 请求和响应中的,通过指定指令实现缓存机制。缓存指令是单向的,意味着在请求中设置的指令,不一定被包含在响应中。其参数有

        public:响应可以被任何对象(客户端、代理服务器等)缓存

        no-cache:强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)

        no-store:不使用任何缓存

        max-age:缓存最大周期

        must-revalidate:一旦资源过期,成功向原始服务器验证之前,不能缓存响应后的任何数据

  • User-Agent:浏览器通知服务器,客户端浏览器与操作系统相关信息
  • ser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) uChrome/69.0.3497.100 Safari/537.36
  • Connection:表示客户端与服务连接类型;Keep-Alive表示持久连接,close已关闭 
  •  Content-Length:请求体的长度
  •  Host:请求的服务器主机名
  • Content-Type:请求的与实体对应的MIME信息。如果是post请求,会有这个头,默认值为application/x-www-form-urlencoded,表示请求体内容使用url编码
  • Sec-Fetch-Dest:表示请求的目的地,即如何使用获取的数据;
  • Sec-Fetch-Mode:该请求头表明了一个请求的模式;
    取值范围:
    cors:跨域请求;
    no-cors:限制请求只能使用请求方法(get/post/put)和请求头(accept/accept-language/content-language/content-type);
    same-origin:如果使用此模式向另外一个源发送请求,显而易见,结果会是一个错误。你可以设置该模式以确保请求总是向当前的源发起的;
    navigate:表示这是一个浏览器的页面切换请求(request)。 navigate请求仅在浏览器切换页面时创建,该请求应该返回HTML;
    websocket:建立websocket连接;
  • Sec-Fetch-Site:

    表示一个请求发起者的来源与目标资源来源之间的关系;

    取值范围:
    cross-site:跨域请求;
    same-origin:发起和目标站点源完全一致;
    same-site:有几种判定情况,详见说明;
    none:如果用户直接触发页面导航,例如在浏览器地址栏中输入地址,点击书签跳转等,就会设置none

  • Sec-Fetch-User:取值是一个Boolean类型的值,true(?1)表示导航请求由用户激活触发(鼠标点击/键盘),false(?0)表示导航请求由用户激活以外的原因触发;
    取值范围:?0 或 ?1

  • Upgrade-Insecure-Requests 请求头向服务器发送一个客户端对HTTPS加密和认证响应良好,若取值:1,则可以升级连接为HTTPS,

请求体:作为向目标服务器提交数据的部分,可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1¶m2=value2”的方式传递请求参数。

1.响应行
(1)响应的协议和版本号
(2)响应状态码
(3)响应状态描述符
2.响应头
(1)key:value 不同的响应头,有不同含义,此 处 是 空 行
3.响应体

服务器---->> 传给客户端的数据,可以是文字、文件、图片等各种数据类型。

抓包

使用wireshark抓包,利用过滤器筛选出端口号,挑选本机网络回路。

三次握手:

关于三次握手和四次挥手的详解文章有许多,这里选其中一个作为链接。

https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc

抓包显示的三次握手:

操作59:客户端53415——>服务器9999发送:标记位[SYN]:

  • 序号Seq=0设为X;
  • Win=65535当前窗口大容纳字节;
  • Len=0,即不带数据;
  • MSS=65495,MSS表示最大段的最大值,它是65535-20(IP典型包头)-20(TCP典型包头)=65495,也就是说当一次传输数据大于65495时,TCP会将其分段传输。
  • WS=256,即原有窗口大小扩容256倍
  • SACK_PERM=1: 允许选择确认,SACK信息包含接收方已经成功接收的数据块的序列号范围。而SACK_PERM字段为1表明,选择开启了SACK功能。

操作60:服务器9999——>客户端53415发送[SYN,ACK]:

  • Seq=0设为Y,Ack=1(这是上一个Seq,X+1)表示确认可以建立新连接。

操作61:客户端53415——>服务器9999发送[ACK]:

  • Seq=1(即X+1),Ack=1(即Y+1),客户端接收到来自服务器端的确认后,明确了从客户端到服务器的数据传输是正常的,并同意连接。

三次握手结束后,操作62:客户端53415——>服务器9999发送[PSH,ACK]:开始传输数据,可见TCP将其封装为PDU即协议数据单元,进行传输,此次数据长度Len=700,可以推测下一个服务器的Ack=X+len=701.

四次挥手:需强调的是,四次挥手不同于三次握手需由客户端发起,它既可以由客户端发起也可以由服务器发起,此实验由服务器发起的挥手动作。

关注重点不在于四次挥手,而是四次挥手后发生的[TCP Retransmission]报错,报错原因是TCP port number reused,即服务器已经把端口号释放,但客户端在请求重传。我初以为是挥手后客户端需要等待2MSI时间,但此等到是在四次挥手期间的,再细看端口号:53409,是比53415前的序列,可以判断是该传输发生错误,等待定时器超时,然后重传,但此次重传时间却在四次挥手以后,故产生此错误。

PS:客户端的端口号是循环使用的,每次传输后端口号+1,到达65535后循环使用。

TCP是可靠的传输连接,但它是在不可靠的传输环境建立可靠连接,那肯定是会产生错误的,TCP也有一系列的维护传输出错的措施。

待续。

套接字实验二 UDPping程序

要求:用python编写一个客户ping程序,该程序:

  1. 该客户将发送简单的Ping报文,接收一个从服务器返回的pong报文,并确定发送ping报文到接收pong报文为止的时延,记为RTT;
  2. 标准的ping使用ICMP协议,此时是创建一个非标准的基于UDP的ping程序;
  3. ping程序向客户端发送10个ping报文,对于每个报文,待其返回pong报文时要计算出相应的RTT;
  4. UDP是不可靠的协议,传输分组有可能丢失,当客户发送ping报文等待1s无应答则返回请求超时的报文。

        UDP是传输层上无连接的传输协议,是不可靠的;但相比TCP协议,它又节省了建立连接的开销。在本实验中可以将UDP客户端程序设置等待1s无应答,则返回等待超时。UDP协议有优缺点,当选择UDP协议传输数据时,利用其时延小的优点,传输丢包是可以接受的,比如:一些不断定时采样的数据,该周期传输丢包,下一周期采样再次传输数据,丢包对应用影响不大,像股票走势图。

服务器端代码

import random
from socket import *

serverSocket= socket(AF_INET,SOCK_DGRAM)  #使用IPv4,若用IPv6就AF_INET6
serverSocket.bind(('127.0.0.1',8888))     #绑定主机地址和端口
while True:
    rand = random.randint(0,10)           #利用随机数决定服务器是否应答
    message,address = serverSocket.recvfrom(1024)  #recvfrom是UDP的接收方法函数
    print("收到来自%s的报文:(%s)"%(address,message)) #打印来自客户端的ping报文
    print("随机数是:%d"%rand)
    message = message.upper()            #把收到的报文转为大写来发送回客户端
    if rand < 4:                         #应答概率为50%
        continue
    serverSocket.sendto(message,address) #发送大写的报文作为pong报文发送客户端

客户端代码:

import time
from socket import *
serverName = '127.0.0.1'
serverPort = 8888
clientSosket = socket(AF_INET,SOCK_DGRAM)
clientSosket.settimeout(1)      #设置超时时间为1s
for i in range(0,10):
    oldTime = time.time()
    sendTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(oldTime))
            #接收时间元组并返回可读字符串作为当地时间,sendtime类型是字符串
    message = ('package %d,client_local_time:%s' %(i+1,sendTime)).encode()
            #将package i和客户端的时间作为ping报文发送,还要将str类型转为bytes(传输要求)
    try:
        clientSosket.sendto(message,(serverName,serverPort))
        modifiedMessage,serverAddress = clientSosket.recvfrom(1024)
        rtt = time.time() - oldTime
        modifiedMessage = modifiedMessage.decode("utf-8")
        print('报文%d 收到来自 %s 的应答:%s ,往返时延RTT= %fs'%(i+1,serverName,modifiedMessage,rtt))
    except Exception as e:
        print('报文 %d :的请求超时'%(i+1))

运行代码并结合抓包如图:

从抓包结果亦可见请求报文四、七、九,服务器端是进行答复的。抓包计算的时间差小于代码计算的RTT,是因为抓包的时间是建立UDP协议的时间,没有包括代码运行的时间。

看抓包的报文

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值