• Python之旅.第八章.网络编程


    一、上节课复习

    1 TCP(建立的是一个双向连接)三次握手建连接,四次挥手断连接

    三次握手:

       c----syn=1 seq=x--->s

       s----ack=1+x syn=1 seq=y--->c

       c----ack=1+y------->s

       四次挥手:

       s------fin=1---------->c

       c------>ack=1--------->s

       c------>fin=1--------->s

       s------>ack=1--------->c

       

    127.0.0.1: 本地回环地址

    无论收发(sendreceive)都是和自己的操作系统直接交互,由操作系统接着调硬件将信息传到对方的操作系统。

    也是基于以上原因,收发不是一一对应的。

     

    2socket

       socket是位于应用层与传输层之间的一个抽象层,专门把传输层以下的协议封装成接口提供给应用层使用。应用只需要调用socket的接口或者说按照socket的标准编写程序,写出的程序自然是遵循tcp/ip协议。

     

    二、粘包问题

    TCP又称流式协议,有两个特性:

    a 像流水一样源源不断

    b 优化传输机制,用NAGLE算法,将时间间隔短的数据量少的数据攒成一波

    以上两个特性导致了粘包问题

     

    解决粘包问题的思路是知道发多少,然后收多少

    注:如果用time.sleep解决,把这一端解决了,有可能在另一端发生粘包

     

    三、解决粘包问题(基本板)

    a struct模块

    1)把整型数字转成bytes类型

    2)转成的bytes是固定长度的

     

    import struct

    res=struct.pack('i',20332)   # ‘i’ --integer

    print(res,len(res)) 

    res2=struct.unpack('i',res)

    print(res2[0])

     

    b 服务端

    from socket import *

    import subprocess

    import struct

     

    server=socket(AF_INET,SOCK_STREAM)

    server.bind(('127.0.0.1',8080))

    server.listen(5)

     

    while True:

        conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)

        print(client_addr)

        while True:

            try:

                cmd=conn.recv(1024)

                obj=subprocess.Popen(cmd.decode('utf-8'),

                                     shell=True,

                                     stdout=subprocess.PIPE,

                                     stderr=subprocess.PIPE

                                     )

                stdout=obj.stdout.read()

                stderr=obj.stderr.read()

     

                # 1、制作固定长度的报头

                total_size=len(stdout) + len(stderr)

                header=struct.pack('i',total_size)

     

                # 2、发送报头

                conn.send(header)

     

                #3、发送真实的数据

                conn.send(stdout)

                conn.send(stderr)

            except ConnectionResetError:

                break

     

        conn.close()

    server.close()

     

    c 客服端

    from socket import *

    import struct

     

    client=socket(AF_INET,SOCK_STREAM)

    client.connect(('127.0.0.1',8080))

    # print(client)

     

    while True:

        cmd=input('>>>: ').strip()

        if not cmd:continue

        client.send(cmd.encode('utf-8'))

        #1、先收固定长度的报头

        header=client.recv(4)

        #2、解析报头

        total_size=struct.unpack('i',header)[0]

        print(total_size) #1025

        #3、根据报头内的信息,收取真实的数据

     

        recv_size=0

        res=b''

        while recv_size < total_size:

            recv_data=client.recv(1024)

            res+=recv_data

            recv_size+=len(recv_data)

     

        print(res.decode('gbk'))

    client.close()

     

    四、解决粘包问题(终极版)

    a struct模块

    import struct

    import json

     

    header_dic = {

        'total_size': 3122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222121,

        'md5': '123svsaef123sdfasdf',

        'filename': 'a.txt'

    }

     

    header_json=json.dumps(header_dic)

    # print(header_json,type(header_json))

    header_bytes=header_json.encode('utf-8')

    header_size=len(header_bytes)

     

    print(header_size)

    obj=struct.pack('i',header_size)

    print(obj,len(obj))

     

    b 服务端

    from socket import *

    import subprocess

    import struct

    import json

     

    server=socket(AF_INET,SOCK_STREAM)

    server.bind(('127.0.0.1',8080))

    server.listen(5)

     

    while True:

        conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)

        print(client_addr)

        while True:

            try:

                cmd=conn.recv(1024)

                obj=subprocess.Popen(cmd.decode('utf-8'),

                                     shell=True,

                                     stdout=subprocess.PIPE,

                                     stderr=subprocess.PIPE

                                     )

                stdout=obj.stdout.read()

                stderr=obj.stderr.read()

     

                # 1、制作报头

                header_dic={

                    'total_size':len(stdout) + len(stderr),

                    'md5':'123svsaef123sdfasdf',

                    'filename':'a.txt'

                }

                header_json = json.dumps(header_dic)

                header_bytes = header_json.encode('utf-8')

     

                # 2、先发送报头的长度

                header_size=len(header_bytes)

                conn.send(struct.pack('i',header_size))

     

                # 3、发送报头

                conn.send(header_bytes)

     

                # 4、发送真实的数据

                conn.send(stdout)

                conn.send(stderr)

            except ConnectionResetError:

                break

     

        conn.close()

    server.close()

     

    c 客户端

    from socket import *

    import struct

    import json

     

    client=socket(AF_INET,SOCK_STREAM)

    client.connect(('127.0.0.1',8080))

    # print(client)

     

    while True:

        cmd=input('>>>: ').strip()

        if not cmd:continue

        client.send(cmd.encode('utf-8'))

        #1、先收报头的长度

        header_size=struct.unpack('i',client.recv(4))[0]

     

        #2、接收报头

        header_bytes=client.recv(header_size)

     

        #3、解析报头

        header_json=header_bytes.decode('utf-8')

        header_dic=json.loads(header_json)

        print(header_dic)

     

        total_size=header_dic[ 'total_size']

        # print(total_size) #1025

        #4、根据报头内的信息,收取真实的数据

     

        recv_size=0

        res=b''

        while recv_size < total_size:

            recv_data=client.recv(1024)

            res+=recv_data

            recv_size+=len(recv_data)

     

        print(res.decode('gbk'))

    client.close()

     

    五、远程执行命令的程序

    a 客户端

    from socket import *

     

    client=socket(AF_INET,SOCK_STREAM)

    client.connect(('127.0.0.1',8080))

    # print(client)

     

    while True:

        cmd=input('>>>: ').strip()

        if not cmd:continue

        client.send(cmd.encode('utf-8'))

        # print('has send')

        res=client.recv(14744)

        # print('has recv')

        print(len(res))

        print(res.decode('gbk'))

     

    client.close()

     

    b 服务端

    from socket import *

    import subprocess

     

    server=socket(AF_INET,SOCK_STREAM)

    server.bind(('127.0.0.1',8080))

    server.listen(5)

     

    while True:

        conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)

        print(client_addr)

        while True:

            try:

                cmd=conn.recv(1024)

                obj=subprocess.Popen(cmd.decode('utf-8'),

                                     shell=True,

                                     stdout=subprocess.PIPE,

                                     stderr=subprocess.PIPE

                                     )

                stdout=obj.stdout.read()

                stderr=obj.stderr.read()

     

                # 先发送数据的长度

                total_size=len(stdout) + len(stderr)

                conn.send(total_size)

     

                # 发送真实的数据

                conn.send(stdout)

                conn.send(stderr)

            except ConnectionResetError:

                break

     

        conn.close()

    server.close()

     

    #交互式命令或是cd。。一般要在一台机器上,不推荐远程调控。如若强行要求,不要直接执行,而是要模拟执行,并将模拟结果发回。

     

  • 相关阅读:
    在django中使用orm来操作MySQL数据库的建表,增删改
    TCP中的粘包问题,以及用TCP和UDP实现多次聊天
    网络编程概念
    面向对向---封装
    xlrd模块读取Excel表中的数据
    curl和wget的区别和使用
    WebSocke
    HTTP状态码(响应码)
    IO模型
    Redis为什么使用单进程单线程方式
  • 原文地址:https://www.cnblogs.com/yangli0504/p/8930948.html
Copyright © 2020-2023  润新知