zoukankan      html  css  js  c++  java
  • python---基础知识回顾(六)网络编程2(处理粘包)

    前戏:

    之前在python---基础知识回顾(六)网络编程异步模块中提到过粘包现象,而且在使用twisted中提到过一种处理办法,按行接收lineReceived,当收到 换行符时,才去缓冲区中获取到数据。

    from twisted.internet import reactor
    from twisted.internet.protocol import Protocol,Factory
    from twisted.protocols.basic import LineReceiver
    
    class EchoServer(LineReceiver):
        def connectionMade(self):   #建立连接的时候
            print("Get connect from",self.transport.client)
            self.factory.numPorts = self.factory.numPorts + 1
            if self.factory.numPorts > 3:
                self.transport.write("Too many connections , try later".encode("utf-8"))
                self.transport.loseConnection()
                self.factory.numPorts = self.factory.numPorts - 1
            else:
                self.transport.write("Successful connections".encode("utf-8"))
    
    
    
        def connectionLost(self, reason):   #连接断开的时候
            print(self.transport.client,"disconnect")
            self.factory.numPorts = self.factory.numPorts - 1
    
        def lineReceived(self,line):    #当收到一行数据的时候
            data = "reve a line :%s"%line.decode("utf-8")
            print(data)
            self.transport.write(data.encode("utf-8"))
    
    
    factory = Factory()
    factory.protocol = EchoServer
    
    port = 8080
    reactor.listenTCP(port,factory)
    reactor.run()   #进入循环
    服务器端lineReceived
    import socket
    
    ip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口
    
    sk = socket.socket()
    sk.connect(ip_port) #与服务器连接
    
    data = sk.recv(1024)
    print(data.decode("utf-8"))
    
    while True:
        sk.sendall("fafwagawgwa".encode("utf-8"))
        data = input(">>>:")
        if not data:
            continue
        if data == "hh":
            data += "
    "
        try:
            sk.sendall(data.encode("utf-8"))  # 向服务器端发送数据
            data = sk.recv(1024)
            print(data.decode("utf-8"))
        except ConnectionAbortedError:
            break
    
    sk.close()
    客户端
        sk.sendall("fafwagawgwa".encode("utf-8"))
        data = input(">>>:")
        if not data:
            continue
        if data == "hh":
            data += "
    " 
        sk.sendall(data.encode("utf-8"))  # 向服务器端发送数据

    在这里我们先发送fafwagawgwa数据到服务器端的缓冲区中(未被获取),然后在客户端输入hh后,会发送换行符到服务器端,此时缓冲区中的数据“fafwagawgwahh ”,当获取到换行符后,服务器会将数据获取打印

    reve a line :fafwagawgwahh

    粘包现象演示:

    粘包问题产生的原因:

    所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

    产生情况:

    1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
    import socket
    
    ip_port = ("0.0.0.0",8080)
    
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind(ip_port)
    sk.listen(5)
    
    while True:
        conn,addr = sk.accept()
        print("Connect from %s(%s)"%(addr[0],addr[1]))
        count = 0
        while True:
            count += 1
            bt_data = conn.recv(1024)
            if not bt_data: #对方断开了连接
                conn.close()
                break
            data = bt_data.decode("utf-8")
            print(count,data)
    sk.close()
    服务端
    import socket
    
    ip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口
    
    sk = socket.socket()
    sk.connect(ip_port) #与服务器连接
    
    message_list = ['1','2','3','4','5','6','7','8','9','10']
    
    num = 0
    
    while True:
        try:
            data = message_list[num]
        except IndexError:
            break
        send_data = data.encode("utf-8")
        sk.sendall(send_data)
        num += 1
    sk.close()
    客户端
    索引   数据
    Connect from 127.0.0.1(3395)
    1 123
    2 4
    3 5
    4 6
    5 7
    6 8
    7 9
    8 10
    输出结果

    从输出结果,可以看出在服务器端接收的数据产生了粘包现象

    2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 
    import socket
    
    ip_port = ("0.0.0.0",8080)
    
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind(ip_port)
    sk.listen(5)
    
    while True:
        conn,addr = sk.accept()
        print("Connect from %s(%s)"%(addr[0],addr[1]))
        count = 0
        while True:
            count += 1
            bt_data = conn.recv(5)
            if not bt_data: #对方断开了连接
                conn.close()
                break
            data = bt_data.decode("utf-8")
            print(count,data)
    sk.close()
    服务器端
    import socket,time
    
    ip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口
    
    sk = socket.socket()
    sk.connect(ip_port) #与服务器连接
    
    message_list = ['1dsafwawf','2faafaw']
    
    num = 0
    
    while True:
        try:
            data = message_list[num]
        except IndexError:
            break
        send_data = data.encode("utf-8")
        sk.sendall(send_data)
        num += 1
        time.sleep(5)
    sk.close()
    客户端
    Connect from 127.0.0.1(3531)
    1 1dsaf
    2 wawf
    3 2faaf
    4 aw
    输出结果

    简单版本:

    import socket
    
    ip_port = ("0.0.0.0",8080)
    
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind(ip_port)
    sk.listen(5)
    
    while True:
        conn,addr = sk.accept()
        print("Connect from %s(%s)"%(addr[0],addr[1]))
        buffer = ""
        while True:
            bt_data = conn.recv(5)
            if not bt_data: #对方断开了连接
                conn.close()
                break
            data = bt_data.decode("utf-8")
            buffer += data
            if '
    ' in buffer:
                index = buffer.find('
    ')
                data = buffer[:index]
                buffer = buffer[index+2:]
                print(data)
    sk.close()
    服务器端
    import socket,time
    
    ip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口
    
    sk = socket.socket()
    sk.connect(ip_port) #与服务器连接
    
    message_list = ['1dsafwawf
    ','2faafaw
    ']
    
    num = 0
    
    while True:
        try:
            data = message_list[num]
        except IndexError:
            break
        send_data = data.encode("utf-8")
        sk.sendall(send_data)
        num += 1
        time.sleep(5)
    sk.close()
    客户端
    Connect from 127.0.0.1(4167)
    1dsafwawf
    2faafaw
    输出结果

     改进版本(先发送一个报头,告诉服务端要接收的数据大小):

    import socket
    import struct
    
    ip_port = ("0.0.0.0",8080)
    
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind(ip_port)
    sk.listen(5)
    
    while True:
        conn,addr = sk.accept()
        print("Connect from %s(%s)"%(addr[0],addr[1]))
        while True:
            head_data = conn.recv(4)  #先接收报头,含有文件大小
            if not head_data: #对方断开了连接
                conn.close()
                break
            #设置接收的大小以及接收数据
            recv_size = struct.unpack('i',head_data)[0] #unpack返回元组
            recv_data = bytes()
    
            while recv_size:
                recv_data += conn.recv(recv_size)
                recv_size -= len(recv_data)
    
            data = recv_data.decode("utf-8")
            print(data)
    sk.close()
    服务端
    import socket,time
    import struct
    
    ip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口
    
    sk = socket.socket()
    sk.connect(ip_port) #与服务器连接
    
    message_list = ['1dsafwaffdawwawf','2faafwfwafwafgrehrssafawfaw']
    
    num = 0
    
    while True:
        try:
            data = message_list[num]
        except IndexError:
            break
        send_data = data.encode("utf-8")
        head_data = struct.pack('i',len(send_data)) #i是int类型4字节
        sk.send(head_data)
        sk.sendall(send_data)
        num += 1
        time.sleep(5)
    sk.close()
    客户端
    Connect from 127.0.0.1(5982)
    1dsafwaffdawwawf
    2faafwfwafwafgrehrssafawfaw
    输出结果

    最终版本:

    (对于一些文件的发送,我们需要先发送一个报头,其中是文件信息的字节长度,
    然后我们将文件的详细信息,以及文件的md5值发送过去,对方根据详细信息获取数据,对数据的检测更加完善)

     补充:使用hashlib进行文本(文件)比较

    python---基础知识回顾(九)图形用户界面-------Tkinter

    其中有一段提及使用md5值去比较文件一致性。

    import socket
    import struct
    import os
    import hashlib
    import json
    
    ip_port = ("0.0.0.0",8080)
    
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind(ip_port)
    sk.listen(5)
    
    def getMd5(path):
        fp = open(path,"r")
        content = fp.read()
        data = hashlib.md5(content.encode("utf-8"))
        fp.close()
        return data.hexdigest(),content
    
    while True:
        conn,addr = sk.accept()
        print("Connect from %s(%s)"%(addr[0],addr[1]))
        while True:
            try:
                path = conn.recv(1024)
            except ConnectionResetError:
                conn.close()
                break
            if not path:
                conn.close()
                break
            path = path.decode("utf-8")
            header_data = {'file_size':None,'file_md5':None,'file_name':None}  #用来封装文件的信息
            header_info = None  #用来标志长度 这个先发送,根据这个长度去获取上面的数据(里面有详细信息)
            if not os.path.isfile(path):
                header_info = struct.pack('i',-1)   #-1代表失败
                conn.send(header_info)  # 注意pack后的数据已经是字节型,所以我们不需要去编码
                continue
            header_data['file_size'] = os.stat(path).st_size
            header_data['file_name'] = os.path.basename(path)
            header_data['file_md5'],content = getMd5(path)
    
            header_data = json.dumps(header_data).encode("utf-8")
            header_info = struct.pack('i',len(header_data))
            conn.send(header_info)  #发送header_info,其中是header_data的长度
    
            conn.send(header_data)  #这里的数据在上面已经编码了,不需要我们处理
    
            conn.sendall(content.encode("utf-8"))   #发送文件的内容
    
    sk.close()
    文件下载服务端
    import socket
    import struct
    import json
    import hashlib
    
    ip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口
    
    sk = socket.socket()
    sk.connect(ip_port) #与服务器连接
    
    def getMd5(content):
        data = hashlib.md5(content)
        return data.hexdigest()
    
    while True:
        path = input(">>>(请输入下载的文件名):").strip()
        if not path:
            continue
        if path == "quit":
            break
        sk.send(path.encode("utf-8"))   #发送文件路径
        status = sk.recv(4)   #接收准备状态
        status = struct.unpack('i',status)[0]  #转换数据类型,记得unpack返回的是一个元组
        if status == -1:
            print("请输入正确的文件路径")
            continue
    
        header_data = sk.recv(status)
        header_data = header_data.decode("utf-8")
        header_data = json.loads(header_data)
    
        content = sk.recv(header_data['file_size'])
        file_md5 = getMd5(content)
    
        if file_md5 != header_data['file_md5']:
            print("文件下载出错,请重试")
            del content
            continue
    
        data = content.decode("utf-8") #获取到文件的所有数据
        with open(header_data['file_name'],"w") as fp:
            fp.write(data)
    
        print("文件下载完毕!")
    
    sk.close()
    文件下载客户端
  • 相关阅读:
    HDU 1009 FatMouse' Trade
    HDU 2602 (简单的01背包) Bone Collector
    LA 3902 Network
    HDU 4513 吉哥系列故事——完美队形II
    LA 4794 Sharing Chocolate
    POJ (Manacher) Palindrome
    HDU 3294 (Manacher) Girls' research
    HDU 3068 (Manacher) 最长回文
    Tyvj 1085 派对
    Tyvj 1030 乳草的入侵
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9043769.html
Copyright © 2011-2022 走看看