socket通信

socket通信

socket套接字

  • 什么是socket
    • socket是一个模块,又称为套接字,用来封装 互联网协议(应用层一下的所有层)
  • 为什么要有socket
    • socket可以实现 互联网协议应用层以下层的工作
    • 提高开发效率

server服务端

import socket
server = socket.socket()
server.bind(('127.0.0.1', 9527)) # 把地址绑定到套接字
server.listen(5)    # 监听连接,半连接池,控制访问量
print('server is running...')
conn, addr = server.accept() # 接收客户端连接
data = conn.recv(1024)  # 接收客户端信息
print(data)     # 打印客户端信息
conn.send(b'hello I am server...')  # 向客户端发送信息
conn.close()    # 关闭客户端套接字
server.close()  # 关闭服务器套接字(可选)

client客户端

import socket
client = socket.socket() # 创建客户端套接字
client.connect(('127.0.0.1', 9527))# 尝试连接服务器
print('client is running...')
client.send(b'hello I am client...')
data = client.recv(1024)    # 对话(发送/接收)
print(data)
client.close()  # 关闭客户端套接字

黏包现象

  • 黏包现象的出现有两个问题

    • 无法确认对方发送过来数据的大小

    server端

    # 问题一
    import socket
    import subprocess   # 终端
    
    server = socket.socket()
    server.bind(('127.0.0.1', 9527))
    
    server.listen(5)
    
    while True:
        conn, addr = server.accept()
        print(addr)
        while True:
            try:
                # recv从内存中获取数据
                cmd = conn.recv(10)
                if len(cmd) == 0:continue
                cmd = cmd.decode('utf8')
                if cmd == 'q':break
    
                # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
                obj = subprocess.Popen(
                    # cmd接收解码后的字符串
                    cmd, shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)
                # 结果交给result
                result = obj.stdout.read() + obj.stderr.read()
                print(len(result))
                print(result.decode('gbk'))
                conn.send(result)
            except Exception as e:
                print(e)
                break
        conn.close()
    

    colient端

    # 问题一
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1', 9527))
    while True:
        cmd = input('客户端>>>:')
        client.send(cmd.encode('utf-8'))
        data = client.recv(1024)
        print(len(data))
        print(data.decode('gbk'))
        print(data.decode('gbk'))
        print('*' * 100)
        data = client.recv(1024)
        print(data.decode('gbk'))
        print('*' * 100)
        data = client.recv(1024)
        print(data.decode('gbk'))
        print('*' * 100)
    
    • 在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送

    server端

    # 问题二
    import socket
    server = socket.socket()
    
    server.bind(('127.0.0.1', 9527))
    server.listen(5)
    conn, addr = server.accept()
    data = conn.recv(10)
    print(data)
    data = conn.recv(10)
    print(data)
    data = conn.recv(10)
    print(data)
    

    client端

    # 问题二
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1', 9527))
    client.send(b'hello')
    client.send(b'hello')
    client.send(b'hello')
    client.send(b'hello')
    
  • 解决办法

    问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决黏包的方法就是围绕如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据

    • 就是要确认对方数据的大小

    我们可以借助一个模块struct,它可以把要发送的数据长度转换成固定长度的字节,这样客户端每次接收消息之前只要先接收这个固定长度字节的内容看看接下来接收的信息大小,那么最终接收的数据只要达到这个值就停止,就可以刚好不多不少的接收完整的数据

    import struct
    obj = struct.pack('i',123456)
    print(len(obj))  # 4
    obj = struct.pack('i',898898789)
    print(len(obj))  # 4
    # 无论数字多大,打包后长度恒为4
    
  • 无论哪一端先发送数据

    • 客户端
      • 先制作报头,并发送(struct)
      • 发送真是数据
    import socket
    import struct
    
    client = socket.socket()
    client.connect(('127.0.0.1', 9527))
    while True:
        cmd = input('客户端>>>:')
        cmd_bytes = cmd.encode('utf-8')
        # 做一个报头
        header = struct.pack('i', len(cmd_bytes))
        print(len(header))
        client.send(header)
        # 待服务端确认长度后,发送真实数据长度
        client.send(cmd_bytes)
        # 接收服务端返回的报头
        headers = client.recv(4)
        # 解包,接收服务端返回的真实数据
        data_len = struct.unpack('i', headers)[0]
        result = client.recv(data_len)
        print(len(result))
        print(result.decode('gbk'))
    
    • 服务端
      • 接收报头,并解包获取真实数据长度
      • 根据真实数据长度,接收真实数据
    import socket
    import subprocess
    import struct
    
    server = socket.socket()
    server.bind(('127.0.0.1', 9527))
    server.listen(5)
    while True:
        conn, addr = server.accept()
        print(addr)
        while True:
            try:
                # 获取客户端传过来的报头
                header = conn.recv(4)
                # 解包获取真实数据长度
                data_len = struct.unpack('i', header)[0]
                # 准备接收真实数据
                cmd = conn.recv(data_len)
                if len(cmd) == 0:continue
                cmd = cmd.decode('utf-8')
                if cmd == 'q':
                    break
                # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
                obj = subprocess.Popen(
                    # cmd 接收解码后的字符串
                    cmd, shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)
                result = obj.stdout.read() + obj.stderr.read()
                print(len(result))
                # print(result.decode('gbk')
                # 将结果返回给客户端
                # 做一个报头,返回给客户端
                header = struct.pack('i', len(result))
                print(len(header))
                conn.send(header)
                conn.send(result)
    
            except Exception: break
        conn.close()
    
原文地址:https://www.cnblogs.com/YGZICO/p/11991944.html