zoukankan      html  css  js  c++  java
  • 网络编程

    01基于tcp协议的socket通信

    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 买电话
    # socket.SOCK_STREAM 流式协议 就是TCP协议
    phone.bind(('127.0.0.1', 8080))  # 买电话卡
    
    phone.listen(5)  # 开机。
    # 5 不是链接数,链接可以产生N个,同一时刻只能监听5个请求。
    # print(111)
    conn, addr = phone.accept() # 等待接电话  # 阻塞状态
    # print(222)
    print(conn, addr)  # conn 代表的是socket通信的对象,一个管道
    
    client_data = conn.recv(1024)  # 交流过程
    print(client_data)
    conn.send(client_data.upper())
    
    conn.close()
    phone.close()
    服务端
    import socket
    
    phone = socket.socket()  # 买电话
    
    phone.connect(('127.0.0.1', 8080))  # 拨号
    
    msg = input('>>>').strip()
    phone.send(msg.encode('utf-8'))
    server_data = phone.recv(1024)  # 限制的是最大接收字节数。
    print(server_data)
    phone.close()
    客户端

    但是我们发现这个只能执行一次,就断开了,于是有了下面的通信循环。

    02通信循环

    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)  #防止报OSError
    phone.bind(('127.0.0.1', 8080))
    
    phone.listen(5)
    conn, addr = phone.accept()
    
    while 1:
        try:
            client_data = conn.recv(1024)
            print(client_data)
            conn.send(client_data + b'SB')   #SB要转换成bytes类型
        except Exception:   #排除其他所有的错误
            break
        
    conn.close()
    phone.close()
    服务端
    import socket
    
    phone = socket.socket()  # 买电话
    
    phone.connect(('127.0.0.1', 8080))  # 拨号
    while 1:
        msg = input('>>>').strip()
        if msg.upper() == 'Q': break    #选择正常退出
        phone.send(msg.encode('utf-8'))
        server_data = phone.recv(1024)  # 限制的是最大接收字节数。
        print(server_data.decode('utf-8'))
    phone.close()
    客户端

    但是又发现当客户端输入q退出时,服务端就自动端口了,于是有了下面的链接循环。

    03链接循环

    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
    phone.bind(('127.0.0.1', 8080))
    
    phone.listen(5)
    while 1:
        conn, addr = phone.accept()
        print(addr)
        while 1:
            try:
                client_data = conn.recv(1024)
                print(client_data)
                print(222)
                conn.send(client_data + b'SB')
                print(333)
            except Exception:
                break
    
        conn.close()
    phone.close()
    服务端
    import socket
    
    phone = socket.socket()  # 买电话
    
    phone.connect(('127.0.0.1', 8080))  # 拨号
    while 1:
        msg = input('>>>').strip()
        if msg.upper() == 'Q': break
        phone.send(msg.encode('utf-8'))
        server_data = phone.recv(1024)  # 限制的是最大接收字节数。
        print(server_data.decode('utf-8'))
    phone.close()
    客户端1
    import socket
    
    phone = socket.socket()  # 买电话
    
    phone.connect(('127.0.0.1', 8080))  # 拨号
    while 1:
        msg = input('>>>').strip()
        if msg.upper() == 'Q': break
        phone.send(msg.encode('utf-8'))
        server_data = phone.recv(1024)  # 限制的是最大接收字节数。
        print(server_data.decode('utf-8'))
    phone.close()
    客户端2

    ...以此类推,客户端3.4.5.6...

    当客户端1输入q退出时,客户端2开始聊,客户端2输入q退出时,3.4.5....依此聊。

    04远程执行命令

     需要引入模块:subprocess

    import subprocess
    
    obj = subprocess.Popen('dir',
                           shell=True,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           )
    
    ret = obj.stdout.read().decode('gbk')
    ret1 = obj.stderr.read().decode('gbk')
    print(ret)
    print(ret1)

     

    import socket
    import subprocess
    #
    # obj = subprocess.Popen('dir',
    #                        shell=True,
    #                        stdout=subprocess.PIPE,
    #                        stderr=subprocess.PIPE,
    #                        )
    #
    # ret = obj.stdout.read().decode('gbk')
    # ret1 = obj.stderr.read().decode('gbk')
    # print(ret)
    # print(ret1)
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
    phone.bind(('127.0.0.1', 8080))
    
    phone.listen(5)
    conn, addr = phone.accept()
    while 1:
        try:
            cmd = conn.recv(1024)
            obj = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   )
    
            ret = obj.stdout.read()
            ret1 = obj.stderr.read()
            
            conn.send(ret + ret1)
        except Exception:
            break
    
    conn.close()
    phone.close()
    import socket
    
    phone = socket.socket()
    
    phone.connect(('127.0.0.1', 8080))
    while 1:
        msg = input('>>>').strip()
        if msg.upper() == 'Q': break
        elif not msg: continue
        phone.send(msg.encode('utf-8'))
        server_data = phone.recv(1024)
        print(server_data.decode('gbk'))
    phone.close()

    但是我们发现输入ipconfig之后,最终展示的结果是不全的,这就是粘包现象,下面看来看下粘包现象

     05粘包现象

    粘包现象分为两种:

     # 1,流式协议。数据全部都像水一样,连在一起。 一次性发送 1519个字节(全部在客户端操作系统的缓存区)但是客户端一次只取1024字节,所以缓存区还剩495字节,下次再取。
    # 粘包现象一。

    import socket
    import subprocess
    
    #
    # obj = subprocess.Popen('dir',
    #                        shell=True,
    #                        stdout=subprocess.PIPE,
    #                        stderr=subprocess.PIPE,
    #                        )
    #
    # ret = obj.stdout.read().decode('gbk')
    # ret1 = obj.stderr.read().decode('gbk')
    # print(ret)
    # print(ret1)
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    phone.bind(('127.0.0.1', 8082))
    
    phone.listen(5)
    conn, addr = phone.accept()
    while 1:
        try:
            cmd = conn.recv(1024)
            obj = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   )
    
            ret = obj.stdout.read()
            ret1 = obj.stderr.read()
            total_size = len(ret+ret1)  #len测试的是bytes类型的数据 示例见tes.py
            print(total_size)
    
            conn.send(ret + ret1)
        except Exception:
            break
    
    conn.close()
    phone.close()
    服务端
    import socket
    
    phone = socket.socket()
    
    phone.connect(('127.0.0.1', 8082))
    while 1:
        msg = input('>>>').strip()
        if msg.upper() == 'Q': break
        elif not msg: continue  # 解决的是输入为空程序会夯住的问题。
        phone.send(msg.encode('utf-8'))
        server_data = phone.recv(1024)
        print(server_data.decode('gbk'))
    phone.close()
    客户端

    服务端增加了一个字节:

     

     # 2,针对于(客户端/服务端)发送的连续的小的数据,对方会一次性接收。

     客户端给服务端发送两条数据,一条hello,一条world

    #2 针对(客户端/服务端)发送的数据,他会一次性接收。
    
    import socket
    so1 = socket.socket()
    so1.bind(('127.0.0.1',8888))
    so1.listen(5)
    
    conn,addr = so1.accept()
    client_data1 = conn.recv(1024)
    print('data1',client_data1.decode('utf-8'))
    client_data2 = conn.recv(1024)
    print('data2',client_data2.decode('utf-8'))
    conn.send(client_data1)
    conn.close()
    so1.close()
    服务端
    #例子2:
    import socket
    
    phone = socket.socket()
    
    phone.connect(('127.0.0.1', 8888))
    phone.send(b'hello')
    phone.send(b'world')
    
    print(phone.recv(1024).decode('utf-8'))
    phone.close()
    客户端

    发现,数据小,又连续,但是因为粘包现象都在data1上了。

    解决方式一:让连续的数据变成不连续,不能这么做。

     但是不能这么用。

    解决方式二:限定服务端获取的字节数等于发送的字节数,不行,写死了。

    我们发现这两种都不行。我们要采用数据大小的具体数值+循环接收的方式

    循环接受怎么办,来看一下:

    # 循环接收
    import socket
    
    so1 = socket.socket()
    so1.bind(('127.0.0.1', 8888))
    so1.listen(5)
    
    conn, addr = so1.accept()
    
    client_data1 = conn.recv(5)
    client_data2 = conn.recv(5)
    client_data3 = conn.recv(5)
    client_data4 = conn.recv(5)
    print(client_data1,client_data2,client_data3,client_data4)
    conn.send(client_data1)
    conn.close()
    so1.close()
    服务端
    import socket
    
    
    phone = socket.socket()
    
    phone.connect(('127.0.0.1', 8888))
    phone.send(b'hellohellohellohellohello')
    # phone.send(b'world')
    
    print(phone.recv(1024).decode('utf-8'))
    phone.close()
    客户端

    还有一个问题, len测试的数据大小返回的是int,你一个int怎么发送?int怎么转成bytes类型。 int 你会说先转成str,str再转成bytes。你的脸得标准,你有一个固定长度。

     

    如何将一个固定的数字转成bytes类型呢,下面的来介绍。

    06 解决粘包

    # total_size: len(ret+ret1)  不固定的数字类型。 1519 518 10000
    #咱们要发送一个: 固定的头部(数据的总大小) + 数据

    引入一个模块:struct
    struct  将一个数据,int,转化成固定的bytes
    来看下用法

    import struct
    # 将一个数字转化成等长度的bytes类型。
    ret = struct.pack('i', 183346)
    print(ret, type(ret), len(ret))
    
    # 通过unpack反解回来
    ret1 = struct.unpack('i',ret)[0]
    print(ret1, type(ret1), len(ret1))
    
    
    # 但是通过struct 处理不能处理太大
    
    ret = struct.pack('l', 4323241232132324)
    print(ret, type(ret), len(ret))  # 报错

    struct相关

    import struct
    #
    # 将不固定长度的数字 ---> 固定的bytes
    ret = struct.pack('i',151988)
    
    # ret = struct.pack('q',1519324324424324324324324)  # 数据量大了就不可以
    print(ret, type(ret), len(ret), sep='
    ')
    
    # 反解
    ret1 = struct.unpack('i', ret)[0]
    print(ret1)
    
    
    # header: 可以包含多种对象:文件名,文件的MD5 文件的大小,等等
    # 所以将文件头部构造成字典比较合适。
    # import json
    # head_dict = {
    #     'filename': '二狗.avi',
    #     'md5': '3243243254erew5rwef',
    #     'total_size': 1343243243243243243244324324322324,
    # }
    #
    # dict_json = json.dumps(head_dict)  #字典类型的头部转成了bytes类型
    # # print(dict_json)
    #
    #
    # header_total_size = len(dict_json)  #总长度
    # print(header_total_size)
    #
    # ret = struct.pack('i', header_total_size)
    # print(ret,len(ret))
    # print(dict_dict)
    
    
    # 将字典转化成json格式,---> bytes-->发送。

    low版:

    import socket
    import subprocess
    import struct
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
    phone.bind(('127.0.0.1', 8085))
    
    phone.listen(5)
    conn, addr = phone.accept()
    while 1:
        try:
            cmd = conn.recv(1024)
            obj = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   )
            
            # 1,获取执行命令返回的结果
            ret = obj.stdout.read()
            ret1 = obj.stderr.read()
            
            # 2,计算head(总数据)大小
            total_size = len(ret+ret1)
            print(total_size)
            
            # 3,将head转换成固定长度的bytes
            head = struct.pack('i', total_size)
            
            # 4, 发送head
            conn.send(head)
            
            # 5,发送数据部分。
            conn.send(ret)
            conn.send(ret1)
            
            
        except Exception:
            break
    
    conn.close()
    phone.close()
    
    # total_size: len(ret+ret1)  不固定的数字类型。 1519 518 10000
    #咱们要发送一个:    固定的头部(数据的总大小) + 数据
    # struct  将一个数据,int,转化成固定的bytes
    服务端
    import socket
    import struct
    phone = socket.socket()
    
    phone.connect(('127.0.0.1', 8085))
    while 1:
        msg = input('>>>').strip()
        if msg.upper() == 'Q': break
        elif not msg: continue
        phone.send(msg.encode('utf-8'))
        
        # 1,接收报头。
        header_bytes = phone.recv(4)
        
        # 2,反解报头。
        total_size = struct.unpack('i', header_bytes)[0] # int
        
        # 3,接收数据部分。
    
        data_size = 0
        res = b''
        while data_size < total_size:
            data = phone.recv(1024)  #每次接收1024个数据
            res = res + data   #b'5000个内容'  每次加1024给res,最后加到5000
            # print(res)  #"b'
    Windows IP xc5xe4xd6xc3
    
    
    xcexdexcfxdfxbexd6xd3xf2xcdxf8xcaxcaxc5xe4xc6xf7 xb1xbexb5xd8xc1xacxbdxd3* 1:
    
     "
            # print(len(res))
            data_size = data_size + len(data)  #数据大小+最后的数据大小
    
        print(res.decode('gbk'))
    phone.close()
    客户端

    upper版:

    import socket
    import subprocess
    import struct
    import json
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
    phone.bind(('127.0.0.1', 8080))
    
    phone.listen(5)
    conn, addr = phone.accept()
    while 1:
        try:
            cmd = conn.recv(1024)
            obj = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   )
            
            # 1,获取执行命令返回的结果,计算结果的总大小
            ret = obj.stdout.read()
            ret1 = obj.stderr.read()
            total_size = len(ret + ret1)
            
            # 2,构建header字典
            head_dict = {
                'filename': '二狗.avi',
                'md5': '3243243254erew5rwef',
                'total_size': total_size,
            }
    
            
            # 3,将字典转换json格式
            dict_json = json.dumps(head_dict)
    
            # 4, 将字典转化成bytes
            dict_bytes = dict_json.encode('utf-8')
            
            
            # 5, 计算字典bytes类型的大小
            dict_len = len(dict_bytes)
            
            # 6将bytes类型的 字典的长度转化成固定长度的bytes
            head_dict_len = struct.pack('i', dict_len)
            
            # 7,发送字典的总大小
            conn.send(head_dict_len)
            
            # 8,发送 bytes类型的字典
            conn.send(dict_bytes)
        
            # 9,发送数据部分。
            conn.send(ret)
            conn.send(ret1)
            
            
        except Exception:
            break
    
    conn.close()
    phone.close()
    
    # total_size: len(ret+ret1)  不固定的数字类型。 1519 518 10000
    #咱们要发送一个:    固定的头部(数据的总大小) + 数据
    # struct  将一个数据,int,转化成固定的bytes
    服务端
    import socket
    import struct
    import json
    phone = socket.socket()
    
    phone.connect(('127.0.0.1', 8080))
    while 1:
        msg = input('>>>').strip()
        if msg.upper() == 'Q': break
        elif not msg: continue
        phone.send(msg.encode('utf-8'))
        
        # 1,接收字典的大小。
        dict_size = struct.unpack('i', phone.recv(4))[0]
        
        # 2,获取报头字典json格式。
        header_dict_json = phone.recv(dict_size).decode('utf-8')
        
        # 3, 通过json 反解成 字典
        header_dict = json.loads(header_dict_json)
        
        # 4,接收数据部分。
        
        data_size = 0
        res = b''
        while data_size < header_dict['total_size']:
            data = phone.recv(1024)
            res = res + data
            data_size = data_size + len(data)
    
        print(res.decode('gbk'))
    phone.close()
    客户端

     





  • 相关阅读:
    android中设置快捷键方法setShortcut参数的说明
    LayoutInflater
    Java读取文本文件中文乱码问题 .转载
    eclipse+ADT 进行android应用签名详解
    实验课表
    程序员技术练级攻略
    超过4000长度的字符串如何添加到oracle数据库中
    Hive sql创建表以及插入分区表
    Hive Sql 时间格式化处理
    oracle如何查询分区表所占空间大小
  • 原文地址:https://www.cnblogs.com/wangkaiok/p/10115546.html
Copyright © 2011-2022 走看看