zoukankan      html  css  js  c++  java
  • d28 scoket套接字 struct模块

    
    

    scoket套接字

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口

     服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

     scoket模块

    import socket
    # 生成对象
    server = socket.socket()
    # 绑定ip和端口
    server.bind(('127.0.0.1',8080))
    #绑定参数为一个元组
    #127.0.0.1这叫回送地址  表示电脑本身   端口 为普通整数 一般为8000以上
    # 监听  半连接池
    server.listen(5)
    # 接收链接请求
    # 第一个是表示客户端的socket    第二个客户端的地址信息
    conn,addr = server.accept()
    #接收字节
    data = conn.recv(1024)
    print(data)
    # 发送数据
    conn.send(b'hello  world')
    # 关闭
    conn.close()
    # 关机
    server.close()
    服务端
    import socket
    
    client = socket.socket()
    # 连接服务端
    client.connect(('127.0.0.1',8080))
    #发送数据
    client.send(b'ni hao')
    # 接收数据
    data = client.recv(1024)
    
    print(data)
    # 关闭
    client.close()
    客户端
    accept  是一个阻塞函数  会一直等到有客户端链接过来 在继续执行
    connect本质实在进行三次握手   也是一个数据传输的过程  如果服务器没有立马响应 也会阻塞

     send

    发送数据 本质上是把数据交给操作系统来进行发送 一旦数据交给了操作系统 后续的发送

    应用程序就无法控制了 ,send一般不会卡 当然 如果数据量很大就会阻塞

    scoket套接字是从操作系统缓存区读取数据   
    如果当前还没有任何数据, 就会进入阻塞会一直等到有数据到来 再继续执行
    

      

    send与recv对应,不要出现两边都是相同的情况
    

      

    异常
    
    # 如果已经开启了服务器  再次运行将会抛出 端口占用异常  把之前开启的服务器关掉即可
    1.可能是由于你已经启动了服务器程序,却又再次启动了服务器程序,同一个端口不能被多个进程使用导致!
    
    2.三次握手或四次挥手时,发生了异常导致对方程序已经结束而服务器任然处于time_wait状态导致!
    
    3.在高并发的场景下,由于链接的客户端太多,也会产生大量处于time_wait状态的链接
    

     升级版

    import socket
    # 产生对象
    server = socket.socket()
    # 绑定ip和端口
    server.bind(('127.0.0.1',8080))
    # 监听
    server.listen(5)
    while True:
        # 接收
        conn,addr = server.accept()
        # print(conn)
        # print(addr)
        # 接收
        while True:
            try:
                data = conn.recv(1024)
                print(data)
                # 防止客户端发生异常或断掉
                if len(data) == 0:
                    break
                conn.send(data.upper())
            except ConnectionResetError as a:
                print(a)
                break
        conn.close()
    服务端
    import socket
    # 产生对象
    client = socket.socket()
    # 绑定
    client.connect(('127.0.0.1',8080))
    while True:
        # 输入的信息为字符串类型,传输数据是由二进制数据传送的
        msg = input('>>>..').encode('utf-8')
        # 防止输入的信息为空
        if len(msg) == 0:
            continue
        client.send(msg)
        data = client.recv(1024)
        print(data)
    客户端

    TCP粘包问题

    TCP流式协议, 数据之间没有分界, 就像水  一杯水和一杯牛奶倒在一起了!
    
    UDP 用户数据报协议    
    
    粘包 仅发生在TCP协议中   
    
    1. 发送端 发送的数据量小 并且间隔短 会粘
    
    2. 接收端 一次性读取了两次数据的内容    会粘 
    3. 接收端 没有接收完整  剩余的内容 和下次发送的粘在一起
    
    无论是那种情况,其根本原因在于  接收端不知道数据到底有多少 
    

    代码

    import socket
    
    server = socket.socket()
    
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    conn ,addr = server.accept()
    data = conn.recv(1024)
    print(data)
    data = conn.recv(10)
    print(data)
    data = conn.recv(7)
    print(data)
    data = conn.recv(20)
    print(data)
            
    服务器
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    client.send(b'strawberry')
    client.send(b'berries')
    client.send(b'interconnection')
    客户端
    # TCP特点
    # 会将数据量比较小的并且时间间隔比较短的数据
    # 不知到对方要传的数据有多大
    # 将数剧打包成固定长度 发送给对方

    例子:

    import socket
    
    import subprocess
    
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    # 敲入命令行拟cmd实行命令
    while True:
        conn,addr = server.accept()
        while True:
            try:
                cmd = conn.recv(1024)
                if not len(cmd):
                    break
                cmd = cmd.decode('utf-8')
                obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                res = obj.stdout.read() + obj.stderr.read()
                print(len(res))
                conn.send(res)
            except ConnectionResetError as a:
                print(a)
                break
        conn.close()
    服务端
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    while True:
        cmd = input('>>>').encode('utf-8')
        if not len(cmd):
            continue
        client.send(cmd)
        data = client.recv(1024)
        print(data.decode('gbk'))
    客户端
    解决粘包问题
    	服务端
    		1.先制作一个发送给客户端的字典
    		2.制作字典的报头
    		3.发送字典的报头
    		4.发送字典
    		5.再发真实数据
    	
    	客户端
    		1.先接受字典的报头
    		2.解析拿到字典的数据长度
    		3.接受字典
    		4.从字典中获取真实数据的长度
    		5.接受真实数据
    		

    如:

    import socket
    import struct
    import json
    import subprocess
    
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    while True:
        conn,addr = server.accept()
        while True:
            try:
                cmd = conn.recv(1024)
                if not len(cmd):
                    break
                cmd = cmd.decode('utf-8')
                obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                res = obj.stdout.read() + obj.stderr.read()
                # 一个文件的报头
                d = {'name':'baobo','file_size':len(res),'info':'asdfghjwertyuwertzxcvbnmdfghjkejhk'}
                json_d = json.dumps(d)
        #         1.先制作一个字典的报头
                header = struct.pack('i',len(json_d))
        #         2.发送报头长度
                conn.send(header)
        #         3.发送字典
                conn.send(json_d.encode('utf-8'))
        #         4.在发送真实数据
                conn.send(res)
            except ConnectionResetError as a:
                print(a)
                break
        conn.close()
    服务端
    import socket
    import struct
    import json
    
    
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    
    while True:
        cmd = input('>>>').encode('utf-8')
        if not len(cmd):
            continue
        client.send(cmd)
    #     1.先接受字典报头
        header_dict = client.recv(4)
        # 2.解析报头 获取字典的长度
        dict_size = struct.unpack('i',header_dict)[0]
    #     3.接收字典数据
        dict_bytes = client.recv(dict_size)
        dict_json = json.loads(dict_bytes.decode('utf-8'))
    #     4.从字典中获取信息
        print(dict_json)
        recv_size = 0
        real_data = b''
        while recv_size < dict_json.get('file_size'):
            data = client.recv(1024)
            real_data += data
            recv_size += len(data)
        print(real_data.decode('gbk'))
    客户端

    struct模块

     struct pack//unpack
    python中的struct主要是用来处理C结构数据的,读入时先转换为Python的字符串类型,然后再转换为Python的结构化类型,比如元组(tuple)啥的~。一般输入的渠道来源于文件或者网络的二进制流。
    struct.pack()和struct.unpack()
    在转化过程中,主要用到了一个格式化字符串(format strings),用来规定转化的方法和格式。
    struct.pack(fmt,v1,v2,.....)
    将v1,v2等参数的值进行一层包装,包装的方法由fmt指定。被包装的参数必须严格符合fmt。最后返回一个包装后的字符串。
    struct.unpack(fmt,string)
    解包。比如pack打包,然后就可以用unpack解包了。返回一个由解包数据(string)得到的一个元组(tuple), 即使仅有一个数据也会被解包成元组。
  • 相关阅读:
    你自己不优秀,就算认识再多优秀人又有何用
    史玉柱和他老同学的一段故事
    哪有雪中送碳,都是锦上添花
    围城之困
    心已死,梦前行
    一位销售高手逼单经历!
    Python--函数return多个值
    Python--内置函数
    Python--小程序
    Python--递归
  • 原文地址:https://www.cnblogs.com/komorebi/p/11318329.html
Copyright © 2011-2022 走看看