zoukankan      html  css  js  c++  java
  • python

     socket 是什么?

      socket (又叫套接字)

      socket 是应用层与TCP/IP 协议簇通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议簇封装在socket接口后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。

      所以,我们无需深入理解tcp/ip协议,socket已经为我们封装好了,我么你只需要遵循socket的规定去编程,写出程序自然就遵循tcp/ip标准。

       

    socket分类:

      基于文件类型的

      AF_UNIX

      unix下一切皆文件,基于文件的socket调用的就是底层的文件系统来取数据,两个套接字进程运行在泳衣机器,乐意通过访问同一个文件系统间接完成通信。

      基于网络的

      AF_INET

      还有AF_INET6,还有一些其他的地址家族。不过,他们要么是用于某平台,要么就是已经被废弃。AF_INET是使用最广泛的,python支持很多中地址家族,但是由于我们之关系年网络编程,所有大部分时间我们只用使用了AF_INET。

    socket工作流程:

      

    拓展知识:

      1.tcp三次握手和四次挥手

      2.SYN洪水攻击

    基础示例:

    server:(TCP)

    import socket               # 导入 socket 模块
    
    s = socket.socket()         # 创建 socket 对象
    host = "127.0.0.1" # 获取本地主机名
    port = 12345                # 设置端口
    s.bind((host, port))        # 绑定端口
    
    s.listen(5)                 # 等待客户端连接
    while True:
        c, addr = s.accept()     # 建立客户端连接。
        print ('连接地址:', addr)
        info ="hello!"
        c.send(info.encode())
        c2 = c.recv(1024)
        print(c2)
        c.close()                # 关闭连接
    s.close()

    client:

    #---------------
    s = socket.socket()         # 创建 socket 对象
    host = "127.0.0.1"  # 获取本地主机名
    port = 12345                # 设置端口号
    
    s.connect((host, port))
    s2 = s.recv(1024).decode()
    info = "nihao!"
    s.send(info.encode())
    print(s2,type(s2))
    s.close()

    进阶:

    server端:(TCP方式)

    import socket
    re = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    #地址重用的解决办法
    re.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    
    re.bind(("127.0.0.1",12345))
    re.listen()
    
    while True:
        print("开始监听----")
        a, b = re.accept()
        print(b, "已接入链接....")
        try:
            while True:
                info = a.recv(1024)
                a.send(info)
            a.close()
        except ConnectionResetError as J:
            print("远程主机已断开......")
    
    re.close()

    client端:

    import socket
    con = socket.socket()
    con.connect(("127.0.0.1",12345))
    while True :
        info = input(">>>")
        con.send(info.encode())
        msc = con.recv(1024)
        print(msc.decode())
    con.close()

    server端:(utp方式)

    import socket
    ss = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    ip_port = ("127.0.0.1",8080)
    
    ss.bind(ip_port)
    
    while True:
        #元组:(data,IP_port)
        data,addr = ss.recvfrom(1024)
        print(data)
        ss.sendto(data.upper(),addr)

    client端:

    import socket
    
    ip_port = ("127.0.0.1",8080)
    cli = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    while True:
        data = input(">>>:")
        cli.sendto(data.encode("UTF-8"),ip_port)
        data2,addr = cli.recvfrom(1024)
        print(data2)

    TCP、UDP使用场景

    TCP协议需要三次握手通信成功后进行建立连接.

      场景:互联网和企业网上的客户端应用、数据传输的性能必须让位于数据传输的完整性、可控制性和可靠性时,TCP协议是当然的选择。

    UDP协议是直接发送,不会判断是否接收和发送成功.

      场景: 当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择。QQ使用的是UDP协议聊天,因为用户不需要等待就会接收。


    粘包现象:

    实例:(socket-TCP + subprocess)

    server端:

    import socket
    import subprocess
    
    ip_port = ("127.0.0.1",8080)
    buffer_size = 1024
    
    ss = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ss.bind(ip_port)
    ss.listen(5)
    
    while True:
        print("开始监听链接...")
        conn,addr = ss.accept()
        print(addr,"已链接....")
        while True:
            try:
                data = conn.recv(buffer_size)
                if data == "":continue
                data_de = data.decode('utf-8')
                data_msg = subprocess.Popen(data_de,shell=True,
                                 stdin=subprocess.PIPE,
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE)
                if data_msg.stderr.read().__len__():
                    data_send = "输入错误,请重新输入..".encode('gbk')
                else:
                    data_send = data_msg.stdout.read()
    
                conn.send(data_send)
    
            except ConnectionResetError :
                print("远程主机已断开....")
                break
        conn.close()
    ss.close()

    client端:

    import socket
    
    ip_add = ("127.0.0.1",9999)
    buff_size = 1024
    se = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    se.connect(ip_add)
    
    while True:
        data = input(">>>:")
        if data == "":continue
        se.send(data.encode("utf-8"))
        info = se.recv(1024)
        print(info.decode("gbk"))
    se.close()

    出现问题:

    1.执行ipconfig  命令后 返回的数据并不完整.

    2.执行第二条命令时,还是第一条命令的显示.

    解决粘包方案:

    server:

    import socket
    import subprocess
    import json
    import struct
    
    #常量定义
    ip = "127.0.0.1"
    port = 9999
    ip_port = (ip, port)
    buffer_size = 1024
    
    #socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(ip_port)
    server_socket.listen(5)
    
    while True:
        print("开始监听.....")
        conn, addr = server_socket.accept()
        print(addr, "已接入....")
        while True:
            try:
                #获取指令
                zl_b = conn.recv(buffer_size)
                print(zl_b)
                zl_s = zl_b.decode()
                
                #指令执行返回数据
                data = subprocess.Popen(zl_s, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                data_Y_b = data.stdout.read()
                data_E_b = data.stderr.read()
                
                #封装数据总长度
                data_len = len(data_Y_b) + len(data_E_b)
                data_dict = {"data_dict": data_len}
                json_data_s = json.dumps(data_dict)
                
                #封装head部长度
                json_data_b = json_data_s.encode()
                head_len_i = len(json_data_b)
                
                #封装前4个字节长度
                head_len_b = struct.pack("i", head_len_i)
    
                #发送4个字节,发送head,发送数据
                conn.send(head_len_b)
                print(len(head_len_b))
                conn.send(json_data_b)
                print(len(json_data_b))
                conn.send(data_Y_b)
                print(len(data_Y_b))
                conn.send(data_E_b)
                print(len(data_E_b))
    
            except Exception:
                break
        conn.close()
    server_socket.close()

    client:

    import socket
    import json
    import struct
    
    ip = "127.0.0.1"
    port = 9999
    ip_port = (ip,port)
    buffer_size = 1024
    
    client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    client_socket.connect(ip_port)
    
    while True:
        #发送指令至服务端.
        zhiling_s = input(">>>:").strip()
        if not zhiling_s:continue
        zhiling_b = zhiling_s.encode()
        client_socket.send(zhiling_b)
    
    #层层封装,收取对应数据对应长度,解封装.
    
        #获取前四个字节长度
        head_len_b = client_socket.recv(4)
        head_len_i = struct.unpack('i',head_len_b)[0]
        print(head_len_i)
    
        #获取head部长度
        json_data_b = client_socket.recv(head_len_i)
        print(len(json_data_b))
    
        #获取数据总长度
        json_data_s = json_data_b.decode()
        data_dict = json.loads(json_data_s)
        data_len = data_dict["data_dict"]
        print(data_len)
    
        #循环收取总数据
        data_len_recv = 0
        data = b""
        while data_len_recv < data_len:
            data += client_socket.recv(buffer_size)
            data_len_recv += len(data)
        print(data.decode("gbk"))
    
    client_socket.close()

    问题遗留:

    client 端收取数据
        data = struct.unpack("i",cli.recv(4))[0]
        data2 = iter(partial(cli.recv,data),b"").__next__().decode("gbk")

    能否用两条命令就解决收取数据以及粘包问题?以上两条可以收取,依旧有粘包,待解决...

  • 相关阅读:
    Python: 编程遇到的一些问题以及网上解决办法?
    Python: Win7下使用 pip install lxml 无法安装lxml?
    Python:Pycharm下无法导入安装好的第三方模块?
    Python:如何删除文件中的空白行?
    Pycharm 快捷键
    Python读取二进制文件
    python实现grep
    Python学习笔记
    VBA批量查找和复制文件
    %~dp0是什么意思
  • 原文地址:https://www.cnblogs.com/Anec/p/9631656.html
Copyright © 2011-2022 走看看