python写的udp通讯,自定义了收发包和包的解析,包含服务端和客户端

【1】服务端

import socket
import hashlib
import subprocess
import time


class UDPServer:
    def __init__(self, host='192.168.1.35', port=9527):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.server_address = (host, port)
        self.server_socket.bind(self.server_address)

        self.client_address=None #收包的时候储存客户端的地址
        self.csv_len_bytes=None  #整体csv文件的大小
        self.number_of_chunks=None   #分块大小
        self.error_number=None    #错误重传次数
        self.full_row=""
        self.received_packets={}
        self.max_sequence_number=None

        self.missing_keys=None
        self.b_find_lack=True
        self.re_send_count=0
        print(f"Server is listening on {self.server_address}...")
        #subprocess()

    def recv_and_store(self):

        while True:  #1400
            # 接收数据
            print(f"start recving bag!")
            data, self.client_address = self.server_socket.recvfrom(1472)
            # 解析数据包
            self.csv_len_bytes = int.from_bytes(data[:4], byteorder='little')
            self.number_of_chunks=int.from_bytes(data[4:8], byteorder='little')
            self.error_number=int.from_bytes(data[8:12], byteorder='little')
            sequence_number = int.from_bytes(data[12:16], byteorder='little')  # 提取前4字节作为序列号 [总长] [分包数量] [错误次数] [包序号] [和校验] [具体内容]
            checksum_received = data[16:32]  # 提取接下来12字节作为校验和
            payload = data[32:]  # 提取剩余部分作为数据
            #TODO: 计数
            # 验证校验和
            checksum_calculated = hashlib.md5(payload).digest()

            # test
            #if sequence_number == 2:
            #    print(f"Checksum failed for sequence number {sequence_number}")
            #    continue
                # 跳过了一组数字

            if checksum_received != checksum_calculated:
                print(f"Checksum failed for sequence number {sequence_number}")
                continue
            else:
                print(f"hash checksum successful!")
                self.received_packets[sequence_number]=payload
                print(f"sequence_number:{sequence_number} received_packets[{sequence_number}]:{self.received_packets[sequence_number]}")

            # 更新最大序列号
            if self.max_sequence_number is None or sequence_number > self.max_sequence_number:
                self.max_sequence_number = sequence_number

            if(sequence_number==self.number_of_chunks-1):
                self.re_send_count/=14
                break

    def find_lack(self):
        all_possible_keys=set(range(self.max_sequence_number+1))
        existing_keys=set(self.received_packets.keys())
        self.missing_keys=all_possible_keys-existing_keys

        print("missing keys:",sorted(self.missing_keys))
        if(len(self.missing_keys)==0):
            return True
        else:
            return False




    def assemble_full_row(self):
        if len(self.received_packets) > 0 and self.max_sequence_number == max(self.received_packets.keys()):
            # 重新组装数据
            self.full_row = b''.join([self.received_packets[k] for k in sorted(self.received_packets.keys())])

        print(f"Received from {self.client_address},Server recv size: {len(self.full_row)}")
        print(f"Received from {self.client_address}: {self.full_row}")

        if (len(self.full_row) == self.csv_len_bytes):
            return self.full_row

    def send_to(self,s_cmd):
        print(f"Sent cmd")
        s_cmd_bytes = s_cmd.to_bytes(4, byteorder='little')   #[cmd] [len of lack] [lack...]

        #len_of_lack=len(self.missing_keys)

        #sorted_missing_keys=sorted(self.missing_keys)   #缺失的排序
        #byte_array=bytearray()
        #for i in sorted_missing_keys:
        #    byte_array.extend(i.to_bytes(4, byteorder='little'))#填入byte_array中
        packet=s_cmd_bytes #+ len_of_lack + byte_array
        self.server_socket.sendto(packet, self.client_address)


if __name__ == "__main__":
    full_row = ""
    server=UDPServer()
    server.recv_and_store()
    print(f"resend_count={server.re_send_count}")
    server.b_find_lack=server.find_lack()

    if (server.re_send_count<=3 & server.b_find_lack == False):
            # 重传
            server.send_to(0)
    else:
        print(f"all csv received correct! start merging")
        server.full_row=server.assemble_full_row()
        server.send_to(1)



    #s_cmd=("fffffffjixjioasjdioasjiodjsaioda").encode('utf-8')
    #time.sleep(0.05)
    #server.send_to(s_cmd,server.client_address)

重传功能没有做完,因为udp要实现重传感觉要一直开启监听和发送,这样就要开线程了。

现在服务端可以实现的功能是:

1.收包

2.对当前子包进行校验

3.校验通过的话就把子包放入received_packets[]中

4.检查received_packets,如果缺了key,就把缺了的序号进行排序,然后发送给客户端(但是没有重传,所以这个功能不太能用)

5.如果received_packets中key不缺少,就把所有子包内容组装到full_row中。如果full_row的长度和子包结构体中定义的总包长度相同,那么代表收到了所有内容,且内容无误。

6.发个0回去给服务端,表示正确。

【2】客户端

import socket
import csv
import hashlib


class UDPClient:
    def __init__(self, host='192.168.1.35', port=9527):
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.server_address = (host, port)
        self.csv_file_path='test729.csv'
        self.max_payload_size=1400
        self.find_pencent_and_space_bool=False
        self.chunks=None
        self.csv_len_bytes=None
        self.number_of_chunks_bytes=None
        self.error_number=(0).to_bytes(4, byteorder='little')

    def open_csv_get_chunks(self):
        with open(self.csv_file_path, mode='r', encoding='utf-8') as file:
            csv_reader = csv.reader(file)

            for row in csv_reader:          #对于一组数据
                # 将CSV行转换为字节串
                csv_row_bytes = ','.join(row).encode('utf-8')
                print(f"Check % and blank space")
                self.find_pencent_and_space_bool=self.check_blank(csv_row_bytes)
                if(self.find_pencent_and_space_bool==True):
                    print(f"Input data wrong!!! Include % or blank space")
                    return self.find_pencent_and_space_bool
                else:
                    print(f"Input right! Start chunk and send")
                #获取csv文件的len和len_bytes总长度数量
                self.csv_len_bytes = len(csv_row_bytes).to_bytes(4, byteorder='little')  # 使用xiao端字节序

                # 分片数据
                self.chunks = [csv_row_bytes[i:i + self.max_payload_size] for i in range(0, len(csv_row_bytes), self.max_payload_size)]

                #分片数量
                self.number_of_chunks_bytes=len(self.chunks).to_bytes(4, byteorder='little')  # 使用大端字节序


                return self.find_pencent_and_space_bool


    def first_send(self,server_address=('192.168.1.35', 9527)):
        #
            # 发送每个分片
            sequence_number = 0
            for chunk in self.chunks:
                # 将序列号转换为4字节的大端序字节串
                sequence_number_bytes = sequence_number.to_bytes(4, byteorder='little')

                # 计算校验和
                checksum = hashlib.md5(chunk).digest()

                # 构造带有序列号和校验和的分片
                packet = self.csv_len_bytes + self.number_of_chunks_bytes +self.error_number + sequence_number_bytes + checksum + chunk  #[长度] [分片数量] [重发次数] [序列号] [和校验] [内容]

                # 发送数据包
                self.client_socket.sendto(packet, server_address)
                print(f"Sent: {packet}")

                # 更新序列号
                sequence_number += 1




    def client_listen(self):
        while True:
            data, _ = self.client_socket.recvfrom(self.max_payload_size)
            #解析包
            csv_len_bytes = int.from_bytes(data[:4], byteorder='little')
            #TODO:解包
            if (csv_len_bytes == 0):
                print(f"Recv: scmd={csv_len_bytes}")
                print(f"Something wrong! Need to re-send!")
            else:
                print(f"Recv: scmd={csv_len_bytes}")

    def check_blank(self,Bytes):
        chars_to_check=bytes("% ",'utf-8')

        for char in chars_to_check:
            if char in Bytes:
                return True
        return False





if __name__ == "__main__":
    client=UDPClient()
    b_csv_not_ok=client.open_csv_get_chunks()
    if(b_csv_not_ok==False):
        print(f"csv to chunks ok! start sending chunks")
        client.first_send(('192.168.1.35', 9527))
        print(f"first_send over! Start listen and optional send again")
        client.client_listen()
    else:
        print(f"Input wrong, do not run client_listen")

客户端的功能:

1.解析csv文件,如果中间有% 或者空格,就报错

2.csv文件的所有内容准备好,然后拆成子包进行发送。包括了

#[长度] [分片数量] [重发次数] [序列号] [和校验] [内容]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值