tcp recv 如何判断数据传输结束

我没有找到较为明确的说明

参考博客:https://www.cnblogs.com/changzhendong/p/11419482.html

有人说在首部写入接下来的数据长度,这个也可以,但是也有限制,一般来说是用一个unsigned int,4个字节来表示报文的长度,但是如果4个字节放不下呢,也就是报文大小大于2^32-1个字节,就是说报文大于4GB时就会出问题,当然一般不会有这种问题,但是终究是可能会出现问题的。

有人说用一个特殊的报文来做结束,但是tcp传输中常常会把报文拼在一起(粘包问题),会导致你每次都要去匹配你的那个特殊报文,消耗估计也是不小的。

服务器启动,监听端口9999...
b'client1 data***************************************************************************************'
b'***************************************************************************************'
False

利用头部写入数据包大小的方法:

config:

from yacs.config import CfgNode as CN

config = CN()

config.server = CN()
config.server.port = 9999
config.server.ip = "127.0.0.1"

config.client = CN()
config.client.port = 8888
config.client.ip = "127.0.0.1"

server:

from socket import *
from config import config
import multiprocessing
import time
import struct


def server_listen():
    HOST = config.server.ip
    PORT = config.server.port
    BUFFSIZE = 1024
    ADDRESS = (HOST, PORT)

    # 创建监听socket
    tcpServerSocket = socket(AF_INET, SOCK_STREAM)

    # 绑定IP地址和固定端口
    tcpServerSocket.bind(ADDRESS)
    print("服务器启动,监听端口{}...".format(ADDRESS[1]))

    tcpServerSocket.listen(0)

    process_list = []
    try:
        while True:
            # print('服务器正在运行,等待客户端连接...')
            # client_socket是专为这个客户端服务的socket,client_address是包含客户端IP和端口的元组
            client_socket, client_address = tcpServerSocket.accept()
            # print('客户端{}已连接!'.format(client_address))
            p = multiprocessing.Process(target=server_accept, args=(client_socket, client_address))  # 参数与threading.Thread 一致,如果要传参数(args=(xxx))
            p.daemon = True  # 这个属性默认为False, 相当于主程序会等待子进程结束;设置True之后,子进程是一个后台进程
            p.start()
            process_list.append(p)
            process_list_number = len(process_list)
            i = 0
            while i < process_list_number:
                if process_list[i].is_alive() == False:
                    process_list.__delitem__(i)
                    i = 0
                    process_list_number = len(process_list)
                else:
                    i += 1
            # time.sleep(1)
    finally:
        # 关闭监听socket,不再响应其它客户端连接
        for i in range(len(process_list)):
            process_list[i].join()
        tcpServerSocket.close()

def server_accept(client_socket, client_address):
    BUFFSIZE = 2048
    try:
        while True:
            # 接收客户端发来的数据,阻塞,直到有数据到来
            # 事实上,除非当前客户端关闭后,才会跳转到外层的while循环,即一次只能服务一个客户
            # 如果客户端关闭了连接,data是空字符串
            recv_data = client_socket.recv(4)
            if len(recv_data) == 0:
                break
            print(len(recv_data))
            message_size = struct.unpack('i', recv_data)[0]
            data = b''

            while message_size > 0:
                t_buffsize = BUFFSIZE
                if message_size < BUFFSIZE:
                    t_buffsize = message_size
                t_data = client_socket.recv(t_buffsize)
                data = data + t_data
                message_size -= len(t_data)

            if len(data) > 0:
                print('接收到消息 {}({} bytes) 来自 {}'.format(data.decode('utf-8'), len(data), client_address))
                # 返回响应数据,将客户端发送来的数据原样返回
                data = "ok"
                data_encode = data.encode('utf-8')
                # 发送数据
                client_socket.sendall(struct.pack('i', len(data_encode)))
                client_socket.sendall(data_encode)
            else:
                print('客户端 {} 已断开!'.format(client_address))
                break
    finally:
        # 关闭为这个客户端服务的socket
        client_socket.close()



if __name__ == '__main__':
    server_listen()

client:

import time
from socket import *
from config import config
import multiprocessing
import json
import struct

def client(client_no):
    HOST = config.server.ip
    PORT = config.server.port
    BUFFSIZE = 1024
    ADDRESS = (HOST, PORT)

    tcpClientSocket = socket(AF_INET, SOCK_STREAM)
    try:
        tcpClientSocket.connect(ADDRESS)
        data = "client{0}".format(client_no)
        data_encode = data.encode('utf-8')
        # 发送数据
        tcpClientSocket.sendall(struct.pack('i', len(data_encode)))
        tcpClientSocket.sendall(data_encode)
        # socket 不会发送长度为0的报文,因为许多代码在判断一个socket是否被客户端close掉了的时候会用报文的长度为0来判断。
        # 接收数据
        recv_data = tcpClientSocket.recv(4)
        if len(recv_data) > 0:
            message_size = struct.unpack('i', recv_data)[0]
            data = b''

            while message_size > 0:
                t_buffsize = BUFFSIZE
                if message_size < BUFFSIZE:
                    t_buffsize = message_size
                t_data = tcpClientSocket.recv(t_buffsize)
                data = data + t_data
                message_size -= len(t_data)

            if len(data) > 0:
                print("服务器端响应:", data.decode('utf-8'))
    except:
        print("client connect failed!")
    tcpClientSocket.close()

if __name__ == '__main__':

    # process_list = []
    #
    # process_number = 10
    # for i in range(process_number):
    #     p = multiprocessing.Process(target=client, args=(i,))
    #     p.start()
    #     process_list.append(p)
    #
    # for i in range(process_number):
    #     process_list[i].join()

    # while 1:
    #     client(1)
    #     time.sleep(1)

    client(1)
文章目录