python-python基础8(断言,socket进阶)

一、断言

assert断言语句用来声明某个条件是真的,其作用是测试一个条件(condition)是否成立,如果不成立,则抛出异。

一般来说在做单元测试的时候用的比较多,在生产环境代码运行的情况下,不建议使用断言,会让程序abort掉。

什么时候使用断言:
  • 保护性的编程 正常情况下,并不是防范当前代码发生错误,而防范由于以后的代码变更发生错误。
  • 运行时序逻辑的检查 这种情况一般都是很严重的,防止脏数据或者异常数据进入业务系统,主动进行中断处理
  • 单元测试代码 开发或测试人员编写单元测试代码的时候,也会使用断言
data="kobe bryant"

assert type(data) is str
print("断言正确")

上面的例子就是判断data的类型是不是str,如果是,程序就会继续执行,如果不是,那么在assert的时候就会直接报错

 

二、socket进阶

服务端或客户端在接收数据时,有一个缓冲区,我们可以定义它的大小,如果接收到的数据超过这个大小,那么下一次接收的数据还是上一次没接收完的数据,我们把缓冲区大小改的很大是不科学的,因为我们不知道接收的数据会有多大。所以用循环接收的办法,直到接收完所有数据才打印,具体看以下例子。

server:

import socket,os,time

server=socket.socket()
server.bind(("localhost",9999))

server.listen()
print("service is start,listening...")

while True:
    conn,addr=server.accept()
    print("From {0} connect successfully.".format(addr))

    while True:
        data=conn.recv(1024)
        if not data:
            break               #客户端断开就是没有数据传过来,那就跳出循环等待下一次连接
        data_de=data.decode('utf-8')
        re_data=os.popen(data_de).read()
        print(len(re_data.encode()))     #打印结果数据的大小
        conn.send(str(len(re_data.encode())).encode())      #将结果数据的大小发送给客户端
        client_ack=conn.recv(1024)                     #防止粘包,收到一个确认数据后,再往下执行,这种方法比sleep好,没有延迟
        print("client_ack:", client_ack.decode())      
    #   time.sleep(1)
        conn.send(re_data.encode())     #发送具体的结果数据

server.close()

client:

import socket

client=socket.socket()
client.connect(("localhost",9999))
print("connect successfully")

while True:
    common=input(">>:")
    if len(common) == 0:continue
    client.send(common.encode())
    print("命令已发送")

    recv_size_en=client.recv(1024)     #接收结果大小
    client.send("发送确认,已收到数据大小".encode())     #发送确认数据,防止粘包
    recv_size=int(recv_size_en.decode())
    print("命令结果大小:",recv_size)
    data_size=0
    recv_data=b''   #空字节
    while data_size<recv_size:
        data=client.recv(1024)    #接收结果
        data_size+=len(data)
        print("接收到{0}字节的数据".format(data_size))
        recv_data+=data
    else:
        print("总共接收到{0}字节的数据".format(data_size))
        print(recv_data.decode())

client.close()

 用socket传输文件,简单的ftp,有md5验证:

server:

import socket,os

server=socket.socket()
server.bind(("localhost",8585))
server.listen()
print("服务启动")

while True:
    conn,addr=server.accept()
    print("From {0} connect successfully.".format(addr))

    while True:
        cmd_to=conn.recv(1024)
        if not cmd_to:
            print("客户端已断开")
            break
        cmd,file_name=cmd_to.decode().split()
        if os.path.isfile(file_name):
            file_md5=os.popen("md5sum {0}".format(file_name)).read().strip()
            file_size=os.stat(file_name).st_size
            conn.send(str(file_size).encode())
            with open(file_name,"rb") as f:
                conn.recv(1024)
                for line in f:
                    conn.send(line)
                data_ack=conn.recv(1024).decode()
                if data_ack=="recv done":
                    print("发送完成")
                else:
                    print("发送失败")
                conn.send(file_md5.encode())
                md5_ack=conn.recv(1024).decode()
                if md5_ack.split()[0]==file_md5.split()[0]:
                    print("文件校验正确")
                else:
                    print("文件校验错误")
        else:
            print("文件不存在")
            conn.send("not exist".encode())

server.close()

client:

import socket,os

client=socket.socket()
client.connect(('localhost',8585))
print("connect successfully")

while True:
    common=input(">>:")
    if len(common) == 0:continue
    if common.startswith("get"):    #startswith:如果第一个字符串是get则返回真
        client.send(common.encode())
        print("命令已发送")
        recv_size_en=client.recv(1024)     #接收结果大小
        if recv_size_en.decode() == "not exist":
            print("文件不存在")
        else:
            client.send("发送确认,已收到数据大小".encode())
            recv_size=int(recv_size_en.decode())
            print("文件大小:",recv_size)
            filename=common.split()[1]
            with open(filename+".new","wb") as f:
                data_size=0
                while data_size<recv_size:
                    data=client.recv(1024)    #接收结果
                    f.write(data)
                    data_size+=len(data)
                    print("接收到{0}字节的数据".format(data_size))
                else:
                    print("总共接收到{0}字节的数据".format(data_size))
                    print("文件接收完成")
            client.send("recv done".encode())
            file_md5=client.recv(1024).decode()
            print("原文件md5:{0}".format(file_md5))
            newfile_name=filename+".new"
            newfile_md5=os.popen("md5sum {0}".format(newfile_name)).read().strip()
            print("新文件md5:{0}".format(newfile_md5))
            client.send(newfile_md5.encode())
            if newfile_md5.split()[0]==file_md5.split()[0]:
                print("文件校验正确")
            else:
                print("文件校验错误")
    else:
        print("输入的命令无效")

client.close()

 

sockerserver模块

 socketserver模块是基于socket而来的模块,它是在socket的基础上进行了一层封装,并且实现并发等功能。

具体用法:

import socketserver                              #1、引入模块
class MyServer(socketserver.BaseRequestHandler): #2、自己写一个类,类名自己随便定义,然后继承socketserver这个模块里面的BaseRequestHandler这个类
    def handle(self):              #3、写一个handle方法,必须叫这个名字,不写运行父类的pass,服务端的所有操作都在这个方法里实现
        self.request.recv(1024)                  #4、收消息
        msg = '亲,学会了吗'
        self.request.send(bytes(msg,encoding='utf-8')) #5、发消息
        self.request.close()                     #6、关闭连接
     # 拿到了我们对每个客户端的管道,那么我们自己在这个方法里面的就写我们接收消息发送消息的逻辑就可以了
        pass
if __name__ == '__mian__':
    #thread 线程
    server = socketserver.ThreadingTCPServer(('127.0.0.1',8090),MyServer)#7、使用socketserver的ThreadingTCPServer这个类,将IP和端口的元祖传进去,还需要将上面咱们自己定义的类传进去,得到一个对象,相当于我们通过它进行了bind、listen
    server.serve_forever()                       #5、使用我们上面这个类的对象来执行serve_forever()方法,他的作用就是说,我的服务一直开启着,并且serve_forever()帮我们进行了accept

#注意:
#有socketserver 那么有socketclient的吗?
#当然不会有,我要作为客户去访问京东的时候,京东帮我也客户端了吗,客户端是不是在我们自己的电脑啊,并且socketserver对客户端没有太高的要求,只需要自己写一些socket就行了

简单的应用:

import socketserver
class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.send(self.data.upper())
        
if __name__ == "__main__":
    HOST, PORT = "127.0.0.1", 9999
    # 设置allow_reuse_address允许服务器重用地址
    socketserver.TCPServer.allow_reuse_address = True
    # 创建一个server, 将服务地址绑定到127.0.0.1:9999
    #server = socketserver.TCPServer((HOST, PORT),Myserver)
    server = socketserver.ThreadingTCPServer((HOST, PORT),Myserver)
    # 让server永远运行下去,除非强制停止程序
    server.serve_forever()

SocketServer主要被抽象为两个主要的类:

BaseServer类,用于处理连接相关的网络操作 BaseRequestHandler类,用于实际处理数据相关的操作,

SocketServer还提供了两个MixIn类:

ThreadingMinxIn和ForkingMixinl 用于扩展server,实现多线程和多进程。(用多线程或多进程可以实现多用户同时操作)

 

FTP示例:

server端:

import socketserver,json,os

class FTPserver(socketserver.BaseRequestHandler):
    def handle(self):
        print("{0} 已连接".format(self.client_address[0]))
        while True:
            self.data = self.request.recv(1024).strip()
            data_j=json.loads(self.data.decode())
            client_action=data_j["action"]
            filename=data_j["filename"]
            filesize=data_j["filesize"]
            if hasattr(self,client_action):
                func=getattr(self,client_action)
                func(filename,filesize)

    def pwd(self,filename,filesize):
        dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


    def get(self,filename,filesize):
        if os.path.isfile(filename):
            filesize=os.stat(filename).st_size
            file_msg={"action":"get","filename":filename,"filesize":filesize}
            file_msg_j=json.dumps(file_msg)
            self.request.send(file_msg_j.encode())
            client_code=self.request.recv(1024)
            with open(filename,"rb") as f:
                for line in f:
                    self.request.send(line)
            data_ack=self.request.recv(1024).decode()
            if data_ack == "200":
                print("发送完成")
            else:
                print("发送失败")
            server_file_md5=os.popen("md5sum %s"%filename).read().split()[0]
            self.request.send(server_file_md5.encode())
        else:
            print("%s文件不存在" %filename)
            self.request.send("400".encode())


    def put(self,filename,filesize):
        '''接收客户端上传的文件'''
        if os.path.isfile(filename):
            filename=filename+".new"
        self.request.send("200 ok".encode())
        with open(filename,"wb") as f:
            data_size=0
            while data_size<filesize:
                file_data=self.request.recv(1024)
                f.write(file_data)
                data_size+=len(file_data)
            else:
                print("文件上传完成")
        server_file_md5=os.popen("md5sum %s"%filename).read().split()[0]
        self.request.send(server_file_md5.encode())


if __name__=="__main__":
    HOST,PORT="localhost",6969
    server=socketserver.ThreadingTCPServer((HOST,PORT),FTPserver)
    server.serve_forever()

客户端:

import socket,os,json

class ftp_client(object):
    def __init__(self):
        self.client = socket.socket()
    def help(self):
        print('''
        ls,cd,get,put,pwd,mkdir
        ''')
    def connect(self,ip,port):
        self.client.connect((ip, port))
        print("connect successfully")
    def authenticate(self):
        pass
    def interactive(self):
        while True:
            cmd=input(">>:")
            if len(cmd)==0:continue
            if len(cmd.split())>1:
                action=cmd.split()[0]
                filename=cmd.split()[1]
                if hasattr(self,action):
                    func=getattr(self,action)
                    func(filename)
                else:
                    self.help()
            else:
                if cmd=="pwd":
                    self.pwd()

    def pwd(self):
        pwd_dict={"action":"pwd","filename":0,"filesize":0}
        pwd_dict_j=json.dumps(pwd_dict)
        self.client.send(pwd_dict_j.encode())
        server_code=self.client.recv(1024)
        if server_code.decode()=="200":
            print()



    def put(self,filename):
        if len(filename)>0:
            if os.path.isfile(filename):
                filesize=os.stat(filename).st_size
                file_msg={"action":"put","filename":filename,"filesize":filesize}
                file_msg_j=json.dumps(file_msg)
                self.client.send(file_msg_j.encode())
                #为了防止粘包,等待服务器发回一个确认码,代表状态,比如服务端接收到这个文件信息后,返回一个码的意思是正常或者是文件太大等等
                server_code=self.client.recv(1024).decode()
                with open(filename,"rb") as f:
                    for line in f:
                        self.client.send(line)
                print("文件上传完成")
                server_file_md5=self.client.recv(1024).decode()
                client_file_md5=os.popen("md5sum %s"%filename).read().split()[0]
                if server_file_md5==client_file_md5:
                    print("文件校验正确")
                else:
                    print("文件校验错误")
            else:
                print("%s文件不存在,或这不是一个文件"%filename)
        else:
            print("请输入文件名")

    def get(self,filename):
        if len(filename)>0:
            file_msg={"action":"get","filename":filename,"filesize":0}
            file_msg_j=json.dumps(file_msg)
            self.client.send(file_msg_j.encode())
            server_file_msg_j=self.client.recv(1024).decode()
            if server_file_msg_j=="400":
                print("%s文件不存在"%filename)
                self.interactive()
            else:
                server_file_msg=json.loads(server_file_msg_j)
                self.client.send("200".encode())
                filesize=server_file_msg["filesize"]
                if os.path.isfile(filename):
                    filename=filename+".new"
                with open(filename,"wb") as f:
                    datasize=0
                    while datasize<filesize:
                        data=self.client.recv(1024)
                        f.write(data)
                        datasize+=len(data)
                    else:
                        print("%s文件下载完成"%filename)
                self.client.send("200".encode())
                client_file_md5=os.popen("md5sum %s"%filename).read().split()[0]
                server_file_md5=self.client.recv(1024).decode().split()[0]
                if server_file_md5==client_file_md5:
                    print("文件校验正确")
                else:
                    print("文件校验错误")
        else:
            print("请输入文件名")


ftp=ftp_client()
ftp.connect("localhost",6969)
ftp.interactive()

 

posted @ 2020-03-01 19:28  jehuzzh  阅读(255)  评论(0)    收藏  举报