• day-10 网络编程


    1.软件开发架构

    我们了解的涉及到两个程序之间通讯的应用大致可以分为两种:

    第一种是应用类:qq、微信、网盘、优酷这一类是属于需要安装的桌面应用

    第二种是web类:比如百度、知乎、博客园等使用浏览器访问就可以直接使用的应用

    1.1 C/S架构

    C/S即:Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。

    这里的客户端一般泛指客户端应用程序EXE,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。

    1.2 B/S架构

    B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。

    Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。

    2.套接字的使用

    基于TCP协议的socket

    tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端

    server端

    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
    sk.listen() #监听链接
    conn,addr = sk.accept() #接受客户端链接
    ret = conn.recv(1024) #接收客户端信息
    print(ret) #打印客户端信息
    conn.send(b'hi') #向客户端发送信息
    conn.close() #关闭客户端套接字
    sk.close() #关闭服务器套接字(可选)

    client端

    import socket
    sk = socket.socket() # 创建客户套接字
    sk.connect(('127.0.0.1',8898)) # 尝试连接服务器
    sk.send(b'hello!')
    ret = sk.recv(1024) # 对话(发送/接收)
    print(ret)
    sk.close() # 关闭客户套接字

    3.在重启服务端时可能会遇到

    file ‘/users/applge/desktop/develop/pycharmprojcts/

    sk.bind(('127.0.0.1',8898))’

    OSError:[Erron 48]] Address already in use

    解决方法:

    #加入一条socket配置,重用ip和端口
    import socket
    from socket import SOL_SOCKET,SO_REUSEADDR
    sk = socket.socket()
    sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
    sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
    sk.listen()          #监听链接
    conn,addr = sk.accept() #接受客户端链接
    ret = conn.recv(1024)   #接收客户端信息
    print(ret)              #打印客户端信息
    conn.send(b'hi')        #向客户端发送信息
    conn.close()       #关闭客户端套接字
    sk.close()        #关闭服务器套接字(可选)

    4.基于UDP协议的socket

     udp是无链接的,启动服务之后可以直接接受消息不需要提前建立链接

    server端:

    import socket
    udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
    udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
    msg,addr = udp_sk.recvfrom(1024)
    print(msg)
    udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
    udp_sk.close()                         # 关闭服务器套接字

    client端

    import socket
    ip_port=('127.0.0.1',9000)
    udp_sk=socket.socket(type=socket.SOCK_DGRAM)
    udp_sk.sendto(b'hello',ip_port)
    back_msg,addr=udp_sk.recvfrom(1024)
    print(back_msg.decode('utf-8'),addr)

    黏包的解决方案

    struct解决

    该模块可以把一个类型,如数字,转成固定长度的bytes

    >>> struct.pack('i',1111111111111)
    
    struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

    解决方案示例
    import json,struct
    #假设通过客户端上传1T:1073741824000的文件a.txt
    
    #为避免粘包,必须自定制报头
    header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值
    
    #为了该报头能传送,需要序列化并且转为bytes
    head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输
    
    #为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
    head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度
    
    #客户端开始发送
    conn.send(head_len_bytes) #先发报头的长度,4个bytes
    conn.send(head_bytes) #再发报头的字节格式
    conn.sendall(文件内容) #然后发真实内容的字节格式
    
    #服务端开始接收
    head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
    x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度
    
    head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
    header=json.loads(json.dumps(header)) #提取报头
    
    #最后根据报头的内容提取真实的数据,比如
    real_data_len=s.recv(header['file_size'])
    s.recv(real_data_len)
    
    

    我们还可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)

    发送时 接收时

    先发报头长度

    先收报头长度,用struct取出来
    再编码报头内容然后发送 根据取出的长度收取报头内容,然后解码,反序列化
    最后发真实内容 从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容



  • 相关阅读:
    【概念】构造函数和析构函数
    【概念】使用Fixed创建固定大小的缓冲区
    Sqoop
    Flume组件
    Hive节点及原理
    Yarn
    Hive数据倾斜
    单例
    工厂设计模式
    JVM对象创建
  • 原文地址:https://www.cnblogs.com/junyingwang/p/9376586.html
Copyright © 2020-2023  润新知