Dayday up ---- python Day8

动态导入模块

# 方法1:
mod = __import__("lib.aa")

print(mod)
instance = getattr(mod.aa,"C")
print(instance)
obj = instance("小花")
print(obj.name)

# 或者:

mod = __import__("lib.aa")
obj = mod.aa.C("小花")
print(obj.name)


# 方法2,官方建议使用

import importlib
aa = importlib.import_module("lib.aa")
print(aa)
print(aa.C("小花").name)

lib.aa

class C(object):
    def __init__(self,name):
        self.name = name

 

断言 assert

import importlib
aa = importlib.import_module("lib.aa")

obj = aa.C("小花")
# print(obj.name)

# 断言 assert 和if语句很像
assert type(obj.name) is str    # 如果断言错了就不往下走
print(obj.name)


socket

  把tcp/ip, udp 封装,暴露的是send, recv

socket families 地址簇 

socket.AF_INET ipv4

socket.AF_INET6 ipv6

socket.AF_UNIX  localhost

socket types 类型

socket.SOCK_STREAM  #for tcp

socket.SOCK_DGRAM   #for udp 

socket.SOCK_RAW     #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

socket.SOCK_RDM  #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。

socket.SOCK_SEQPACKET #废弃了

 

socket的server 和 client 的基本过程

服务器端:

1、server = socket.socket()   实例化

2、server.bind() 绑定地址和端口

3、server.listen() 监听

4、server.accept()接收消息

5、recv 收取

 

客户端:

1、client = socket.socket()   实例化

2、client.connect((serverip,port))

3、client.send(data)

4、client.recv(data)

 

# -*- coding:utf-8 -*-

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

server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)

    while True:
        print("等待新指令")
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break
        print(data)
        cmd_res = os.popen(data.decode()).read()
        print("before send:",cmd_res)
        if len(cmd_res) == 0:
            cmd_res = "cmd has not output"

        conn.send( str(len(cmd_res.encode())).encode("utf-8") )
        conn.send(cmd_res.encode("utf-8"))
        print("send done")

server.close()
socket_server
# -*- coding:utf-8 -*-
import socket

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

while True:
    cmd = input(">>:").strip()
    if len(cmd) == 0: continue
    client.send(cmd.encode("utf-8"))
    cmd_res_size = client.recv(1024)
    print("命令结果大小:",cmd_res_size)

    received_size = 0
    received_data = b''
    while received_size < int(cmd_res_size.decode()):
        data = client.recv(1024)
        received_size += len(data)
        received_data += data
    else:
        print("cmd res received done:",received_size )
        print(received_data.decode())

client.close()
socket_client

socket 粘包

造成粘包的原因,两次send会强制将缓冲区的数据 发送给客户端

解决:

1、可以  在一次send之后使用time.sleep(0.5)   但是不支持,慢,卡

2、client_ack = conn.recv(1024)  # 等待客户端确认

client_send = conn.send(“等待…”) #发送消息给服务端确认

 

以上实现了ssh功能,如果我想实现ftp上传下载文件功能怎么实现呢?

ftp server

1、 读取文件名

2、检测文件是否存在

3、打开文件

4、检测文件大小 发给客户端

5、发送文件大小给客户端

6、等待客户端确认

7、开始边读边发

8、发送md5给客户端校验

 

ftp client

1、判断命令,startswith(“get”)

2、发送命令

3、接收文件大小

4、给服务端发确认消息

5、打开文件

6、边收边写

并且加上 md5功能呢?

回忆hash的使用方法:

import hashlib
m = hashlib.md5()
m.update(b"test")
print(m.hexdigest())

# 结果 
098f6bcd4621d373cade4e832627b4f6

 ftp_server + md5

# -*- coding:utf-8 -*-
import socket,os,hashlib

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

while True:
    conn,addr = server.accept()
    print("new connect",addr)
    while True:
        print("等待新指令...")
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break
        cmd,filename = data.decode().split()
        print(filename)
        if os.path.isfile(filename):
            f = open(filename,"rb")
            m = hashlib.md5()
            file_size = os.stat(filename).st_size
            conn.send( str(file_size).encode())
            conn.recv(1024)
            for line in f:
                m.update(line)
                conn.send(line)
            print("file md5", m.hexdigest())
            f.close()
            conn.send(m.hexdigest().encode())
        print("send done")

server.close()

 ftp_client + md5

# -*- coding:utf-8 -*-
import socket,hashlib
client = socket.socket()
client.connect(('localhost',9999))

while True:
    cmd = input(">>>:").strip()
    if len(cmd) == 0: continue
    if cmd.startswith("get"):
        client.send(cmd.encode())
        server_response = client.recv(1024)
        print("server response:",server_response)
        client.send(b"ready to recv file")
        file_total_size = int(server_response.decode())
        received_size = 0
        filename = cmd.split()[1]
        f = open(filename + ".new","wb")
        m = hashlib.md5()
        while received_size < file_total_size:
            if file_total_size - received_size > 1024:
                size = 1024
            else:
                size = file_total_size - received_size
                print("last received:",size)
            data = client.recv(size)
            received_size += len(data)
            m.update(data)
            f.write(data)

        else:
            new_file_md5 = m.hexdigest()
            print("file recv done", received_size,file_total_size)
            f.close()
        server_file_md5 = client.recv(1024)
        print("server file md5:", server_file_md5)
        print("client file md5:",new_file_md5)

client.close()

 

SocketServer 

 socketserver 类型 

1、socketserver.TCPServer() tcp协议

2、socketserver.UDPServer() 

3、socketserver.UnixStreamServer

4、socketserver.UnixDatagramServer

以上类型都是统一继承了 baseserver类

+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+

 

要创建一个socetserver至少要分以下几步:

1、继承baserequesthandle,你必须自己创建一个请求处理类,并且这个类要继承baserequesthandle,并且重写 父亲类里的handle()方法

2、实例化 server classes 比如: tcpserver,传递server ip 和 上面创建的请求处理类给这个server classes,比如 tcpserver

3、server.handle_request() 只处理一个请求  server.serve_forever 处理多个请求 永远执行

4、server_close()

 

server跟客户端所有的交互都是在handle里面写的

 

简单的socketserver 实例 :

server:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):    # 每次请求都会实例化
    def handle(self):  # 重写handle
        while True:    # 多次循环
            try:
                self.data = self.request.recv(1024).strip()
                print("{} wrote:".format(self.client_address[0]))
                print(self.data)
                self.request.send(self.data.upper())
            except ConnectionResetError as e:
                print("err",e)
                break
if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    # Create the server, binding to localhost on port 9999
    server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)   # 实例化tcpserver,ThreadingTCPServer 多线程多并发 
    server.serve_forever()

 client:

client = socket.socket() #声明socket类型,同时生成socket连接对象
#client.connect(('192.168.16.200',9999))
client.connect(('localhost',9999))
while True:
    msg = input(">>:").strip()
    if len(msg) == 0:continue
    client.send(msg.encode("utf-8"))
    data = client.recv(10240)
    print("recv:",data.decode())

client.close()

 多进程 :

server = socketserver.ForkingTCPServer((HOST, PORT), MyTCPHandler)   #在windows上不好使

fileno() 返回文件描述符

server.handle_request() 处理单个请求

server_forever(poll_interval=0.5)   自动调用 service_actions()

 

allow_reuse_address 地址重用

 

 

socket上地址重用

server = socket.socket() #获得socket实例

server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

 

 

 

 

 

 

 

  

posted @ 2016-09-16 10:26  a_monologue  阅读(64)  评论(0)    收藏  举报