• 第八章 网络编程


    网络编程

    1.网络基础

    用途:未来的web框架的学习 未来的工作场景做铺垫

    • 两个运行中的程序如何传递信息?
      • 通过文件
    • 两台机器上的两个运行中的程序如何通信?
      • 通过网络
      • 网络应用开发架构
        • C/S
          • client 客户端
          • server 服务端
          • 例如:迅雷 qq 浏览器 飞秋 输入法 百度云 pycharm git VNC 红蜘蛛 各种游戏
        • B/S
          • browser 浏览器
          • server 服务端
          • 例如:淘宝 邮箱 各种游戏 百度 博客园 知乎 豆瓣 抽屉
        • 统一程序的入口
        • B/S和C/S架构的关系:B/S是特殊的C/S架构

    3.网卡:是一个实际存在计算机中的硬件

    4.mac地址:每块网卡上都有一个全球独一无二的mac地址

    5.交换机:链接多台机器并帮助通讯的物理设备,只认识mac地址。

    6.协议:两台物理设备之间对于要发送的内容,长度,顺序的一些约定

    7.ip地址:

    • ipv4协议 4位的点分十进制,32位2进制表示
      • 0.0.0.0 - 255.255.255.255
    • ipv6协议 6位的冒分十六进制 128位2进制表示
      • 0:0:0:0:0:0-FFFF:FFFF:FFFF:FFFF:FFFF:FFFF

    8.公网ip:能被所有人访问到ip地址

    9.内网ip:这些区间的ip地址公网不会使用,避免了公网ip和内网ip的重叠

    • 192.168.0.0 - 192.168.255.255
    • 172.16.0.0 - 172.31.255.255
    • 10.0.0.0 - 10.255.255.255

    10.arp协议:通过ip地址获取mac地址

    11.网关ip:一个局域网的网络出口,访问局域网之外的区域都需要经过路由器和网关

    12.网段:指的是一个地址段 ,如x.x.x.0或x.x.0.0或x.0.0.0

    13.子网掩码:判断两台机器是否在同一个网段、子网内。

    14.port:端口 ,0-65535

    • ip地址能够确认一台机器
    • ip+port能确认一台机器上的一个应用

    2.osi七层模型

    • 第七层:应用层
    • 第六层:表示层
    • 第五层:会话层
    • 第四层:传输层
    • 第三层:网络层
    • 第二层:数据链路层
    • 第一层:物理层

    3.osi 五层协议

    层数 名称 协议 物理设备
    第五层 应用层 python代码相关 http/https/ftp/smtp协议
    第四层 传输层 port端口 tcp、udp协议 四层路由器、四层交换机
    第三层 网络层 ip地址相关 ipv4、ipv6协议 三层路由器、三层交换机
    第二层 数据链路层 mac地址相关 arp协议,以太网协议 网卡、二层交换机
    第一层 物理层 电信号

    4.tcp、udp协议

    tcp协议:

    • 特点:
      • 可靠、慢、全双工通信
      • 建立链接时:三次握手
      • 断开链接时:四次挥手
      • 在建立起连接之后
        • 发送的每一条信息都有回执
        • 为了保证数据的完整性,还有重传机制
      • 长连接:会一直占用双方的端口
      • 能够传递的数据长度几乎没有限制

    应用场景:

    • 文件的上传下载
      • 发送邮件,网盘,缓存电影等

    1571214552118

    SYN=1,是tcp的标志位,代表这是一个请求信息

    ACK=1,代表确认信息

    seq=x 代表这一个数据包的序列号, 一半都加到ACK+1代表是这个包

    3次握手:connect客户端发起一个syn链接请求,如果得到了server端响应ack的同时还会再收到一个由server端发来的syc链接请求client端进行回复ack之后,就建立起了一个tcp协议的链接

    4次挥手:每一端发起的close操作都是一次fin的断开请求,得到'断开确认ack'之后,就可以结束一端的数据发送

    如果两端都发起close,那么就是两次请求和两次回复,一共是四次操作.

    服务端确认信息和close操作不能同时,是因为可能服务端还没处理完数据

    udp协议:

    1、特点:

    • 无连接,速度快
    • 可能会丢消息
    • 能够传递的长度有限,是根据数据传递的设备的设置有关

    应用场景:

    • 即时通信类
      • qq,微信,飞秋等

    tcp协议和udp协议的区别:

    tcp协议:是一个面向连接的,流式的,可靠的,慢的,全双工通信

    • 邮件 文件 http web

    udp协议:是一个面向数据报的,无连接的,不可靠的,快的,能完成一对一,一对多,多对多的高效通讯协议

    • 即时聊天工具 在线视频

    总结

    1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
    2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
    3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头

    5.scoket(套接字)

    什么是socket?

    Socket是应用层与TCP/ip协议族通信的中间软件抽象层,它是一组接口,帮助我们完成了所有信息的组织和拼接

    1571363131309

    基于tcp协议的socket

    • tcp是基于链接的,必须先启动服务端,绑定ip和端口,然后再启动客户端去链接服务端
    #服务端
    import socket
    
    socket_server=socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
    
    socket_server.bind(('127.0.0.1',8802)) #把地址绑定到套接字
    socket_server.listen(5)#监听链接
    conn,addr = socket_server.accept()#接受客户端链接
    print(conn,addr) #conn为套接字,addr为地址端口
    
    ret = conn.recv(1024)#接受客户端信息
    print(ret)
    conn.send(ret.upper())#向客户端发送信息
    
    conn.close()#关闭客户端套接字
    socket_server.close()#关闭服务端套接字
    
    #客户端
    import socket
    socket_client = socket.socket() #创建客户端套接字
    
    socket_client.connect(('127.0.0.1',8802))#尝试链接服务器
    
    socket_client.send('hello!'.encode('utf-8'))#发送消息 
    #send、recv本质是发给自己的操作系统,操作网卡在根据tcp、udp协议往后面传
    ret = socket_client.recv(1024) #接受消息
    print(ret.decode('utf-8')) 
    
    socket_client.close() #关闭客户端套接字
    
    #注意消息的传递都是bytes类型
    

    加上通讯循环+解决bug+多台客户端一个个访问

    #服务端
    import socket
    
    socket_server=socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
    socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #会遇到端口在用,加上这句搞定
    socket_server.bind(('127.0.0.1',8805)) #把地址绑定到套接字
    socket_server.listen(5)
    
    while True: #这里循环接受客户端连接,一个断链后,可以继续接受。串行,这里还不是并发
        conn,addr = socket_server.accept()
        print(conn,addr)
    
        while True:
            try:
                data = conn.recv(1024)
                if not data:break
                print(data)
                conn.send(data.upper())
            except ConnectionResetError:#在window机器上会报这个错误
                break
        conn.close()#关闭一个客户端
    socket_server.close()
    
    #客户端
    import socket
    socket_client = socket.socket()
    
    socket_client.connect(('127.0.0.1',8805))
    
    while True:
        try:
            msg = input('<<<').strip()
            if not msg:
                continue
            socket_client.send(msg.encode('utf-8'))
    
            data = socket_client.recv(1024)
            print(data.decode('utf-8'))
        except ConnectionResetError: 
            break
    socket_client.close()
    
    

    socket实现ssh服务

    #服务端
    
    import socket,subprocess
    
    
    ip_port = ('127.0.0.1',8081)
    ssh_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ssh_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    
    ssh_server.bind(ip_port)
    ssh_server.listen(5)
    
    while 1:
        conn,addr=ssh_server.accept()
        print('客户端',addr)
    
        while 1:
            cmd = conn.recv(1024)
            if not cmd:break
            print('recv cmd',cmd)
            res = subprocess.Popen(cmd.decode('GBK'),shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   stdin=subprocess.PIPE) #windows机器上是GBK
    
            stderr = res.stderr.read()
            stdout = res.stdout.read()
            print('res length',len((stdout)))
            conn.send(stderr)
            conn.send(stdout)
    
        conn.close()
    
    #客户端
    import socket
    
    ip_port = ('127.0.0.1', 8081)
    ssh_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    ssh_client.connect_ex(ip_port)
    
    
    while 1:
        msg = input('>>').strip()
        if not msg:continue
        if msg == 'quit':break
    
        ssh_client.send(msg.encode('utf-8'))
        res = ssh_client.recv(1024)
    
        print(res.decode('GBK'))
    
    

    基于udp协议的socket:

    #server
    import socket
    import subprocess
    
    ip_port = ('127.0.0.1', 9003)
    bufsize = 1024
    
    udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_server.bind(ip_port)  
    #不用建立链接
    
    while True:
        # 收消息
        cmd, addr = udp_server.recvfrom(bufsize)
        print('用户命令----->', cmd,addr)
    
        # 逻辑处理
        res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE)
        stderr = res.stderr.read()
        stdout = res.stdout.read()
    
        # 发消息
        udp_server.sendto(stdout + stderr, addr) #回消息也要指定端口
    
    udp_server.close()
    
    #client
    from socket import *
    
    import time
    
    ip_port = ('127.0.0.1', 9003)
    bufsize = 1024
    
    udp_client = socket(AF_INET, SOCK_DGRAM)
    
    while True:
        msg = input('>>: ').strip()
        if len(msg) == 0:
            continue
    
        udp_client.sendto(msg.encode('utf-8'), ip_port)  #发送消息时要告诉向哪个端口发消息
        data, addr = udp_client.recvfrom(bufsize)
        print(data.decode('utf-8'), end='')
    

    6.粘包现象:

    定义:同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种现象就是黏包

    粘包的成因:只有TCP有粘包现象,UDP永远不会粘包

    tcp协议的粘包现象:

    • 什么是粘包现象
      • 发生在发送端的粘包
        • 由于两个数据的发送时间间隔短+数据的长度小
        • 所以由tcp协议的优化机制将两条信息作为一条信息发送出去了
        • 为了减少tcp协议中的“确认收到”的网络延迟时间
      • 发生再接收端的粘包
        • 由于tcp协议中所传输的数据无边界,所以来不及接收的多条
        • 数据会在接收放的内核的缓存端黏在一起
      • 本质: 接收信息的边界不清晰

    总结:

    • 粘包现象只发生在tcp协议
    • 从表面上来看,粘包问题主要是因为发送方和接受方的缓存机制,tcp协议的面向流通信的特点
    • 实际上。主要还会因为接受方不知道消息之间的边界,不知道一次性提取多少字节的数据造成的

    解决粘包问题 :

    • 自定义协议1
      • 首先发送报头的长度,报头长度4个字节,内容是发送的报文的字节长度
      • 在发送报头
    • 自定义协议2
      • 我们专门用来做文件发送的协议
      • 先发送报头字典的字节长度,再发送字典(字典中包含文件的名字、大小),再发送文件的内容
    #服务端
    import socket,subprocess
    import json,struct
    
    ip_port = ('127.0.0.1',8082)
    ssh_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ssh_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    
    ssh_server.bind(ip_port)
    ssh_server.listen(5)
    
    while 1:
        conn,addr=ssh_server.accept()
        print('客户端',addr)
    
        while 1:
            try:
                #1、收命令
                cmd = conn.recv(8096)
                if not cmd:break
                #2、执行命令拿到结果
                res = subprocess.Popen(cmd.decode('GBK'),shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       stdin=subprocess.PIPE)
    
                stderr = res.stderr.read()
                stdout = res.stdout.read()
                #3、把命令的结果返回给客户端
                #第一步:制作固定长度的报头
                header_dic = {
                    'filename':'a.txt',
                    'md5':'xxx',
                    'total_size':len(stdout)+len(stderr)
                }
                header_json = json.dumps(header_dic)
                header_bytes = header_json.encode('GBK')
    
                #第二步:先发送报头的长度
                conn.send(struct.pack('i',len(header_bytes))) #这里的固定长度是4,为了防止粘包
    
                #第三步:在发送报头
                conn.send(header_bytes)
    
                #第四步:在发送真实的数据
                conn.send(stderr)
                conn.send(stdout)
            except ConnectionResetError:
                break
        conn.close()
    
    ssh_server.close()
    
    #客户端
    import socket,json,struct
    
    ip_port = ('127.0.0.1', 8082)
    ssh_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    ssh_client.connect_ex(ip_port)
    
    
    while 1:
        msg = input('>>').strip()
        if not msg:continue
        if msg == 'quit':break
    
        ssh_client.send(msg.encode('GBK'))
        #2、拿到命令,并打印
        #第一步:接受报头的长度
        obj=ssh_client.recv(4) #bytes类型的
        header_size =struct.unpack('i',obj)[0] #解包 报头的长度
    
        #第二步:接受报头
        header_bytes=ssh_client.recv(header_size) #收的报头的长度
        #第三步:从报头中解析出对应真实的数据
        header_json = header_bytes.decode('GBK')
        header_dic = json.loads(header_json)
        total_size = header_dic['total_size']
    
        #第四步:接受真实的数据
        recv_size = 0
        recv_data = b''
        while recv_size < total_size:
            res = ssh_client.recv(1024)
            recv_size+=len(res)
            recv_data+=res
    
        print(recv_data.decode('GBK'))
    
    
    ssh_client.close()
    

    7.实现文件传输

    简单版本:

    #server
    import os
    import socket,subprocess
    import json,struct
    
    ip_port = ('127.0.0.1',9999)
    ssh_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ssh_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    
    ssh_server.bind(ip_port)
    ssh_server.listen(5)
    
    share_dir =r'D:pycharm2018python_stack3面向对象&网络编程基础网络编程_文件传输servershare'
    while 1:
        conn,addr=ssh_server.accept()
        print('客户端',addr)
    
        while 1:
            try:
                #1、收命令
                res = conn.recv(8096)  #get 1.mp4
                if not res:break
    
                #2、解析命令,拿到相应的参数
                cmds=res.decode('GBK').split()
                filename = cmds[1]
    
                #3、以读的方式打开文件,读取文件内容返回给客户端
                #第一步:制作固定长度的报头
                header_dic = {
                    'filename':filename,
                    'md5':'xxx',
                    'total_size':os.path.getsize('%s\%s'%(share_dir,filename))
                }
                header_json = json.dumps(header_dic)
                header_bytes = header_json.encode('GBK')
    
                #第二步:先发送报头的长度
                conn.send(struct.pack('i',len(header_bytes))) #这里的固定长度是4,为了防止粘包
    
                #第三步:在发送报头
                conn.send(header_bytes)
    
                #第四步:在发送真实的数据
                with open('%s\%s'%(share_dir,filename),'rb')as f:
                    for line in f:
                        conn.send(line)
    
            except ConnectionResetError:
                break
        conn.close()
    
    ssh_server.close()
    
    
    #client
    import socket,json,struct
    
    ip_port = ('127.0.0.1', 9999)
    ssh_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    ssh_client.connect_ex(ip_port)
    
    download_dir = r'D:pycharm2018python_stack3面向对象&网络编程基础网络编程_文件传输clientdownload'
    while 1:
        msg = input('>>').strip() #get 1.mp4
        if not msg:continue
        if msg == 'quit':break
    
        ssh_client.send(msg.encode('GBK'))
        #2、拿到命令,并打印
        #第一步:接受报头的长度
        obj=ssh_client.recv(4) #bytes类型的
        header_size =struct.unpack('i',obj)[0] #解包 报头的长度
    	
        #第二步:接受报头
        header_bytes=ssh_client.recv(header_size) #收的报头的长度
        #第三步:从报头中解析出对应真实的数据
        header_json = header_bytes.decode('GBK')
        header_dic = json.loads(header_json)
    
        total_size = header_dic['total_size']
        filename = header_dic['filename']
    
        #第四步:接受真实的数据
        with open('%s\%s'%(download_dir,filename),'wb')as f:  #windows
    
            recv_size = 0
            while recv_size < total_size:
                res = ssh_client.recv(1024)
                f.write(res)
                recv_size+=len(res)
                print('总共%s,现在下载了%s' % (total_size, recv_size))
    
    
    ssh_client.close()
    
    

    1571475241004

    1571475257088

    函数版本:

    #服务端
    import os
    import socket,subprocess
    import json,struct
    share_dir =r'D:pycharm2018python_stack3面向对象&网络编程基础网络编程_文件传输函数版本servershare'
    
    def get(conn,filename):
        # 3、以读的方式打开文件,读取文件内容返回给客户端
        # 第一步:制作固定长度的报头
        header_dic = {
            'filename': filename,
            'md5': 'xxx',
            'total_size': os.path.getsize('%s\%s' % (share_dir, filename))
        }
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode('GBK')
    
        # 第二步:先发送报头的长度
        conn.send(struct.pack('i', len(header_bytes)))  # 这里的固定长度是4,为了防止粘包
    
        # 第三步:在发送报头
        conn.send(header_bytes)
    
        # 第四步:在发送真实的数据
        with open('%s\%s' % (share_dir, filename), 'rb')as f:
            for line in f:
                conn.send(line)
    
    def put(ssh_client):
    
        # 2、拿到命令,并打印
        # 第一步:接受报头的长度
        obj = ssh_client.recv(4)  # bytes类型的
        header_size = struct.unpack('i', obj)[0]  # 解包 报头的长度
    
        # 第二步:接受报头
        header_bytes = ssh_client.recv(header_size)  # 收的报头的长度
        # 第三步:从报头中解析出对应真实的数据
        header_json = header_bytes.decode('GBK')
        header_dic = json.loads(header_json)
    
        total_size = header_dic['total_size']
        filename = header_dic['filename']
    
        # 第四步:接受真实的数据
        with open('%s\%s' % (share_dir, filename), 'wb')as f:  # windows
    
            recv_size = 0
            while recv_size < total_size:
                res = ssh_client.recv(1024)
                f.write(res)
                recv_size += len(res)
                print('总共%s,现在下载了%s' % (total_size, recv_size))
    
    def run():
        ip_port = ('127.0.0.1',8889)
        ssh_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        ssh_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
        ssh_server.bind(ip_port)
        ssh_server.listen(5)
    
        while 1:
            conn,addr=ssh_server.accept()
            print('客户端',addr)
    
            while 1:
                try:
                    #1、收命令
                    res = conn.recv(8096)  #get 1.mp4
                    if not res:break
    
                    #2、解析命令,拿到相应的参数
                    cmd=res.decode('GBK')
                    cmds = cmd.split()
                    print(cmds)
    
                    filename = cmds[1]
                    if cmds[0] == 'get':
                        get(conn,filename)
                    elif cmds[0] == 'put':
                        put(conn)
    
                except ConnectionResetError:
                    break
            conn.close()
    
        ssh_server.close()
    
    
    if __name__ == '__main__':
        run()
    
    #客户端
    import socket,json,struct
    import os
    
    download_dir = r'D:pycharm2018python_stack3面向对象&网络编程基础网络编程_文件传输简单版本clientdownload'
    
    
    def get(ssh_client):
        # 2、拿到命令,并打印
        # 第一步:接受报头的长度
        obj = ssh_client.recv(4)  # bytes类型的
        header_size = struct.unpack('i', obj)[0]  # 解包 报头的长度
    
        # 第二步:接受报头
        header_bytes = ssh_client.recv(header_size)  # 收的报头的长度
        # 第三步:从报头中解析出对应真实的数据
        header_json = header_bytes.decode('GBK')
        header_dic = json.loads(header_json)
    
        total_size = header_dic['total_size']
        filename = header_dic['filename']
    
        # 第四步:接受真实的数据
        with open('%s\%s' % (download_dir, filename), 'wb')as f:  # windows
    
            recv_size = 0
            while recv_size < total_size:
                res = ssh_client.recv(1024)
                f.write(res)
                recv_size += len(res)
                print('总共%s,现在下载了%s' % (total_size, recv_size))
    
    def put(ssh_client,filename):
    
        # 3、以读的方式打开文件,读取文件内容返回给客户端
        # 第一步:制作固定长度的报头
        header_dic = {
            'filename': filename,
            'md5': 'xxx',
            'total_size': os.path.getsize('%s\%s' % (download_dir, filename))
        }
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode('GBK')
    
        # 第二步:先发送报头的长度
        ssh_client.send(struct.pack('i', len(header_bytes)))  # 这里的固定长度是4,为了防止粘包
    
        # 第三步:在发送报头
        ssh_client.send(header_bytes)
    
        # 第四步:在发送真实的数据
        with open('%s\%s' % (download_dir, filename), 'rb')as f:
            for line in f:
                ssh_client.send(line)
                print(line)
    
    def run():
        ip_port = ('127.0.0.1', 8889)
        ssh_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        ssh_client.connect_ex(ip_port)
    
        while 1:
            try:
                msg = input('>>').strip() #get 1.mp4
                if not msg:continue
                if msg == 'quit':break
                ssh_client.send(msg.encode('GBK'))
                inp = msg.split()
                filename = inp[1]
                if inp[0] == 'get': #下载操作,服务端读取,内容发给客户端
                    get(ssh_client)
    
                elif inp[0] == 'put':  #上传操作,从本地读取,发给服务端
                    put(ssh_client,filename)
            except ConnectionResetError:
                break
        ssh_client.close()
    
    if __name__ == '__main__':
        run()
    

    面向对象版本:

    #服务端
    import socket
    import struct
    import json,subprocess
    import os
    
    class MYTCPServer:
    
        address_famliy =socket.AF_INET #协议
        socket_type = socket.SOCK_STREAM #TCP
        coding = 'GBK'
        max_packet_size = 8096
        request_queue_size = 5
        allow_reuse_address = False
        server_dir = r'D:pycharm2018python_stack3面向对象&网络编程基础网络编程_文件传输面向对象版本load'
    
        def __init__(self,server_address):
            self.server_address = server_address
            self.socket = socket.socket(self.address_famliy,self.socket_type)
    
            self.server_bind()
            self.server_activate()
    
    
        def server_bind(self):
            if self.allow_reuse_address:
                self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind(self.server_address)
            self.server_address = self.socket.getsockname()
    
    
        def server_activate(self):
            self.socket.listen(self.request_queue_size)
    
    
        def server_close(self):
            self.socket.close()
    
        def get_request(self):
            return self.socket.accept()
    
        def close_request(self,request):
            request.close()
    
        def get(self):
            header_dic = {
                'filename':self.filename,
                'total_size':os.path.getsize(self.server_dir)
            }
            head_json = json.dumps(header_dic)
            header_bytes = head_json.encode(self.coding)
            #发送报头长度
            self.conn.send(struct.pack('i',len(header_bytes)))
            #再发报头
            self.conn.send(header_bytes)
            #发送真实数据
            with open('%s'%self.filename,'rb')as f:
                for line in f:
                    self.conn.send(line)
    
        def put(self):
            pass
    
        def run(self):
            while 1:
                self.conn,self.addr = self.get_request()
    
                while 1:
                    try:
                        # 1、收命令
                        res = self.conn.recv(self.max_packet_size)
                        if not res:break
                        print(res)
                        # 2、解析命令,拿到相应的参数
                        cmd = res.decode(self.coding)
                        cmds = cmd.split()
                        print(cmds)
                        self.cmd=cmds[0]
                        self.filename = cmds[1]
    
                        if hasattr(self,self.cmd):
                            func = getattr(self,self.cmd)
                            func()
                        else:print('没有这个功能')
                    except ConnectionResetError:
                        break
    #####
    obj =MYTCPServer(('127.0.0.1',8888))
    obj.run()
    

    ·

    #客户端
    import socket
    import struct
    import json,subprocess
    import os
    
    class MYTCPClient:
    
        address_famliy =socket.AF_INET #协议
        socket_type = socket.SOCK_STREAM #TCP
        coding = 'GBK'
        server_dir =r'D:pycharm2018python_stack3面向对象&网络编程基础网络编程_文件传输面向对象版本up'
    
        def __init__(self,server_address):
            self.server_address = server_address
            self.socket = socket.socket(self.address_famliy,self.socket_type)
    
            self.connect_ex()
    
        def connect_ex(self):
            self.socket.connect_ex(self.server_address)
    
        def server_close(self):
            self.socket.close()
    
    
        def close_request(self,request):
            request.close()
    
        def get(self):
            obj=self.socket.recv(4)
            header_size = struct.unpack('i',obj)[0]
    
            header_bytes = self.socket.recv(header_size)
            header_json = header_bytes.decode(self.coding)
            header_dic = json.loads(header_json)
            #根据字典筛选有用信息
            total_size = header_dic['total_size']
            filename = header_dic['filename']
            #写文件
            with open('%s'%self.filename,'rb')as f:
                recv_size = 0
                while recv_size< total_size:
                    res=self.socket.recv(1024)
                    f.write(recv_size)
                    recv_size+=len(res)
                    print('总共%s,现在下载了%s' % (total_size, recv_size))
    
        def put(self):
            pass
    
        def run(self):
    
            while 1:
                try:
                    # 1、收命令
                    msg = input('>>').strip()  # get 1.mp4
                    if not msg: continue
                    if msg == 'quit': break
                    self.socket.send(msg.encode(self.coding))
                    print(msg.encode(self.coding))
                    inp = msg.split()
                    self.filename = inp[1]
                    self.cmd = inp[0]
    
                    if hasattr(self,self.cmd):
                        func = getattr(self,self.cmd)
                        func()
                except ConnectionResetError:
                    break
    #####
    obj =MYTCPClient(('127.0.0.1',8888))
    obj.run()
    
  • 相关阅读:
    前端常见跨域解决方案(全)
    小程序动画wx.createAnimation
    判断对象是否有某个属性
    占位符
    vue起航——搭建脚手架
    微信小程序-携带参数转发分享页面
    JS获取地址栏参数
    微信小程序wx:if vs hidden
    小程序websocket(心跳连接)
    初识websocket
  • 原文地址:https://www.cnblogs.com/hanfe1/p/11752190.html
Copyright © 2020-2023  润新知