可靠数据传输原理(GBN 或 SR)

实验目的

运用各种编程语言实现基于 Go-Back-N 或 SR 的可靠数据传输软件。

环境配置:macos14.4    python3.11.7

设计思路:

发送端:

初始化和设置套接字连接:

首先,程序通过socket模块创建一个套接字,并绑定到主机和指定的端口上。这一步骤使得程序可以接收来自其他计算机的连接。

通过调用listen()方法监听传入的连接请求,并使用accept()方法接受连接,建立与客户端的连接。

交换用户名称:

在连接建立之后,双方首先交换他们的名字。这是通过发送和接收编码后的字符串完成的。

消息传输:

用户输入的消息首先被编码为二进制形式,然后通过套接字发送。

程序引入了一个简化版的滑动窗口协议来控制消息的发送。这种协议通常用于确保数据传输的可靠性,通过动态调整窗口大小来控制传输速率和顺序,以适应网络条件。

二进制编码:

消息在发送之前被转换为二进制编码。这不仅展示了数据转换的概念,也为消息传输提供了一个基础的加工过程。

滑动窗口协议的简化实现:

在发送二进制消息时,程序采用了滑动窗口机制。用户可以指定窗口的大小,即一次可以发送多少位数据。数据被分段发送,每次发送一小部分,根据接收方的确认(ACK)来决定是继续发送下一部分还是重发当前部分。

这个过程通过循环实现,模拟了在实际网络通信中使用滑动窗口协议来处理确认丢失(例如,“ACK Lost”)的情况,从而确保所有数据最终都能正确传输。

退出机制:

用户可以通过输入特定的指令(例如"[e]")来退出聊天室。在这之后,程序会发送一条离开聊天室的消息给对方,然后关闭连接。

接收端:

初始化和连接到服务器:

代码开始时,首先通过socket库创建一个套接字,然后请求用户输入服务器的地址和要使用的端口号(在这个示例中,端口号被硬编码为1234),以及用户的名字。

使用用户提供的信息,程序尝试连接到服务器。连接成功后,它会发送用户的名字到服务器,并接收服务器端用户的名字。

消息接收循环:

客户端进入一个无限循环,等待从服务器接收消息。首先,它接收表示即将到来的消息长度和窗口大小的信息。消息长度在这里似乎未被直接使用,而窗口大小用于确定在需要接收ACK之前可以接收多少个消息片段。

通过循环接收每个消息片段,客户端模拟了ACK确认可能会丢失的情况。这是通过随机决定是否发送一个表示ACK丢失的消息来实现的。如果模拟表示ACK已经收到(即没有丢失),它就会将接收到的消息片段拼接到最终消息中。

模拟ACK丢失:

对于每个接收到的消息片段,客户端随机决定是模拟ACK确认的丢失还是成功接收。这通过随机生成0或1来实现,其中0表示ACK丢失,1表示ACK成功接收。

如果模拟了ACK的丢失,客户端会通知服务器ACK丢失,服务器根据设计可能会重发相同的消息片段。如果ACK成功接收,客户端则向服务器发送确认,表示可以发送下一个消息片段。

问题和潜在改进:

代码在打印收到的消息长度(m)时存在逻辑上的小误解,实际上应该是打印完整的、拼接后的消息(a)。

此外,对于消息长度的接收和处理似乎并没有在代码中得到充分利用,可能是一个遗漏的细节。

结束通信:

代码没有显示如何结束通信循环或处理用户输入[e]来退出聊天的情况,这可能是留给读者作为练习的部分。

此时,双方已经完成链接的建立,在发送方输入信息,并且设置好窗口的长度,进行信息的传递:

发送方:在完成信息设定之后就会开始进行数据的传输,因为考虑到这次的实验是本机传递,不可能丢包,所以在程序自动设定了50%概率丢包判定。

原代码:

发送端

# 导入必要的模块

import time, socket, sys

# 将十进制转换为二进制

def decimalToBinary(n):  

    return n.replace("0b", "")

# 将字符串转换为二进制编码

def binarycode(s):

    a_byte_array = bytearray(s, "utf8")

    byte_list = []

    # 将每个字符转换为二进制表示并添加到列表中

    for byte in a_byte_array:

        binary_representation = bin(byte)

        byte_list.append(decimalToBinary(binary_representation))

    # 合并二进制字符串

    a=""

    for i in byte_list:

        a=a+i

    return a

# 打印欢迎信息和初始化信息

print("\n欢迎来到聊天室\n")

print("初始化....\n")

time.sleep(1)

# 创建套接字并绑定主机和端口

s = socket.socket()

host = socket.gethostname()

ip = socket.gethostbyname(host)

port = 1234

s.bind((host, port))

print(host, "(", ip, ")\n")

name = input(str("请输入你的名字: "))

# 开始监听传入连接

s.listen(1)

print("\n等待传入连接...\n")

conn, addr = s.accept()

print("来自", addr[0], "的连接 (端口:", addr[1], ")\n")

# 接收对方的名字并发送自己的名字

s_name = conn.recv(1024)

s_name = s_name.decode()

print(s_name, "已连接到聊天室\n输入 [e] 退出聊天室\n")

conn.send(name.encode())

while True:

    # 输入消息

    message = input(str("我 : "))

    conn.send(message.encode())

    

    # 如果消息是退出指令,发送退出消息并退出循环

    if message == "[e]":

        message = "离开聊天室!"

        conn.send(message.encode())

        print("\n")

        break

    

    # 将消息转换为二进制编码

    message=binarycode(message)

    f=str(len(message))

    conn.send(f.encode())

   

    # 设置窗口大小

    i=0

    j=0

    j=int(input("输入窗口大小 -> "))

    

    # 初始化窗口边界

    b=""

    j=j-1

    f=int(f)

    k=j

    

    # 循环发送消息直到发送完毕

    while i!=f:

        while(i!=(f-j)):

            conn.send(message[i].encode())

            b=conn.recv(1024)

            b=b.decode()

            print(b)

            if(b!="ACK Lost"):

                time.sleep(1)

                print("接收到确认信息!滑动窗口范围为 "+(str(i+1))+" 到 "+str(k+1)+",现在发送下一个数据包")

                i=i+1

                k=k+1

                time.sleep(1)

            else:

                time.sleep(1)

                print("数据位的确认信息丢失!滑动窗口仍在范围 "+(str(i+1))+" 到 "+str(k+1)+",现在重新发送相同的数据包")

                time.sleep(1)

        while(i!=f):

            conn.send(message[i].encode())

            b=conn.recv(1024)

            b=b.decode()

            print(b)

            if(b!="ACK Lost"):

                time.sleep(1)

                print("接收到确认信息!滑动窗口范围为 "+(str(i+1))+" 到 "+str(k)+",现在发送下一个数据包")

                i=i+1

                time.sleep(1)

            else:

                time.sleep(1)

                print("数据位的确认信息丢失!滑动窗口仍在范围 "+(str(i+1))+" 到 "+str(k)+",现在重新发送相同的数据包")

                time.sleep(1)

接收端

# 导入必要的模块

import time, socket, sys

import random

# 打印欢迎信息和初始化信息

print("\n欢迎来到聊天室\n")

print("初始化....\n")

time.sleep(1)

# 创建套接字

s = socket.socket()

shost = socket.gethostname()

ip = socket.gethostbyname(shost)

print(shost, "(", ip, ")\n")

# 输入服务器地址、名字和端口号

host = input(str("输入服务器地址: "))

name = input(str("\n输入你的名字: "))

port = 1234

print("\n正在尝试连接 ", host, "(", port, ")\n")

time.sleep(1)

# 连接到服务器

s.connect((host, port))

print("已连接...\n")

# 发送本地名字并接收对方名字

s.send(name.encode())

s_name = s.recv(1024)

s_name = s_name.decode()

print(s_name, "已加入聊天室\n输入 [e] 退出聊天室\n")

while True:

    # 接收消息长度

    m=s.recv(1024)

    m=m.decode()

    # 接收窗口大小

    k=s.recv(1024)

    k=k.decode()

    k=int(k)

    i=0

    a=""

    b=""

    # 模拟 ACK 丢失

    f=random.randint(0,1)

    message=""

    # 循环接收消息直到接收完毕

    while i!=k:

       # 模拟 ACK 丢失

       f=random.randint(0,1)

       if(f==0):

          b="ACK Lost"

          message = s.recv(1024)

          message = message.decode()

          s.send(b.encode())

       elif(f==1):

          b="ACK "+str(i)

          message = s.recv(1024)

          message = message.decode()

          s.send(b.encode())

          a=a+message

          i=i+1

    print("收到的消息为 :", m)

实验结果:

实验总结:

本次实验通过创建一个简单的聊天室应用,探讨了在Python中使用套接字(Sockets)进行基本网络通信的概念。实验分为服务器端和客户端两部分代码,展示了如何建立连接、交换信息,并模拟了网络通信中的一个关键挑战——确认消息(ACK)的丢失。

理解网络基础:通过动手实践,深入理解了网络通信的基础,尤其是数据如何在网络上发送和接收,以及如何通过编程实现基本的网络通信协议。

协议的实现和挑战:实验介绍了滑动窗口协议的基本概念和实现,展示了在数据传输过程中如何处理错误和数据丢失。

编程技能提升:加深了对Python编程和套接字编程的理解,特别是在网络编程方面。

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值