如何解决socket编程中tcp连接的粘包问题


在socket编程中,因为tcp连接发送的内容之间没有间隔,读取时会一次性读出积累的所有内容,即使这些内容并非仅仅是由一次发送而来。这被称之为粘包

# client.py
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ADDRESS = ('localhost', 8000)
    s.connect(ADDRESS)
    s.sendall('hello!'.encode())
    s.sendall('hello!'.encode())
    s.sendall('hello!'.encode())
# sever.py
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ADDRESS = ('localhost', 8000)
    s.bind(ADDRESS)
    s.listen()
    connect, address = s.accept()
    with connect:
        print(connect.recv(1024).decode())
        print(connect.recv(1024).decode())
        print(connect.recv(1024).decode())
hellp!hello!hello!
(空行)
(空行)

解决办法

1. 在每次发送之间制造一些时间间隙

# client.py
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ADDRESS = ('localhost', 8000)
    s.connect(ADDRESS)
    s.sendall('hello!'.encode())
    time.sleep(1)
    s.sendall('hello!'.encode())
    time.sleep(1)
    s.sendall('hello!'.encode())

2. 把多次发送融合成一次发送

# client.py
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ADDRESS = ('localhost', 8000)
    s.connect(ADDRESS)
    s.sendall('*'.join(["hello", "hello", "hello"]).encode())
# sever.py
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ADDRESS = ('localhost', 8000)
    s.bind(ADDRESS)
    s.listen()
    connect, address = s.accept()
    with connect:
        data = connect.recv(1024).decode()
        result = data.split('*')
        print(result)

3. 收到回复再发送

# client.py
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ADDRESS = ('localhost', 8000)
    s.connect(ADDRESS)
    s.sendall('hello!'.encode())
    print(s.recv(1024).decode())
    s.sendall('hello!'.encode())
    print(s.recv(1024).decode())
    s.sendall('hello!'.encode())
    print(s.recv(1024).decode())
# sever.py
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ADDRESS = ('localhost', 8000)
    s.bind(ADDRESS)
    s.listen()
    connect, address = s.accept()
    with connect:
        print(connect.recv(1024).decode())
        connect.sendall('I have received message'.encode())
        print(connect.recv(1024).decode())
        connect.sendall('I have received message'.encode())
        print(connect.recv(1024).decode())
        connect.sendall('I have received message'.encode())