第八章 网络编程

网络编程

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()
原文地址:https://www.cnblogs.com/hanfe1/p/11752190.html