J'ai un fichier de serveur de socket asynchrone et un fichier client. Quand j'envoie quelque chose comme ça "download filename.ex" au client, le code de ce client répond à ma demande:

try:
    content = read(sp_data[-1]).decode('utf-8')
    print(content)
    msg = json.dumps({'file': sp_data[-1], 'command': data, 'content': content,
                                  'msg': f'[+] File {sp_data[-1]} has been successfully downloaded.'}).encode('utf-8')
except FileNotFoundError:
    msg = json.dumps({'msg': f'[-] File {sp_data[-1]} not found', 'command': data}).encode('utf-8')
    s.send(msg)

Lorsque le client envoie des données au serveur de sockets, la gestion du code de ce serveur a reçu le message:

def recv_message(client_socket):
    global messages
    data = json.loads(client_socket.recv(4096).decode('utf-8').strip()) ##Important here i got this error json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
    raddr = get_raddr(str(client_socket))
    raddr = f'{raddr[0]}:{raddr[1]}'
    message = f'From: {raddr}\nCommand: {data["command"]}\nOutput: \n\n{data["msg"]}'
    try:
        d = messages[raddr]
        d.append(message)
        messages[raddr] = d
    except KeyError:
        messages[raddr] = [message]
    except AttributeError:
        print(message, messages)
    if 'content' in data.keys(): ##Important
        print(data['content'])
        threading.Thread(target=create_file, args=(data['file'], data['content'],), daemon=False).start()

Erreur:

data = json.loads(client_socket.recv(4096).decode('utf-8').strip())
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)

Mais le code du serveur ci-dessus me donne cette erreur lorsqu'il reçoit un message du premier code (quand j'envoie quelque chose comme ça "download file.ex" au client, le client détecte ma commande comme commande spéciale, exécute le premier code, envoie le fichier json au serveur. Mais si j'envoie la commande "dir" au client, il détectera ma commande comme la commande shell, exécutera la commande via le sous-processus, enverra le résultat au serveur et je n'obtiendrai aucune erreur.)


Remarque: j'ai également réduit le code de socketserver. Par conséquent, quelque chose dans mon code peut fonctionner moins bien. L'objectif principal de cet article - faire fonctionner la fonction de téléchargement. Je comprends aussi que mon code est gros. J'ai laissé des commentaires « ## Important» dans mes fichiers. Vous ne pouvez regarder que le code localisé par ces commentaires.
Serveur :

import selectors
import socket
import threading
import json
import base64
import shlex

selector = selectors.DefaultSelector()

connections = {}


def accept_conn(server_socket):
    sock, addr = server_socket.accept()
    connections[len(connections) + 1] = [sock, f'{addr[0]}:{addr[-1]}']
    selector.register(fileobj=sock, events=selectors.EVENT_READ, data=recv_message)


s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('localhost', 4444))
s.listen()
selector.register(fileobj=s, events=selectors.EVENT_READ, data=accept_conn)

messages = {}

##Important 
def create_file(file, content): #content - base64 string
    print(content)
    with open(file, 'wb') as f:
        f.write(base64.b64decode(content.encode('utf-8')))


def recv_message(client_socket):
    global messages
    data = json.loads(client_socket.recv(4096).decode('utf-8').strip()) ##Important here i got this error json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
    raddr = get_raddr(str(client_socket))
    raddr = f'{raddr[0]}:{raddr[1]}'
    message = f'From: {raddr}\nCommand: {data["command"]}\nOutput: \n\n{data["msg"]}'
    try:
        d = messages[raddr]
        d.append(message)
        messages[raddr] = d
    except KeyError:
        messages[raddr] = [message]
    except AttributeError:
        print(message, messages)
    if 'content' in data.keys(): ##Important
        print(data['content'])
        threading.Thread(target=create_file, args=(data['file'], data['content'],), daemon=False).start()


def get_raddr(string):
    '''Get raddr parameter from client socket'''
    raddr = string.replace('>', '')
    return eval(raddr[raddr.find('raddr')::].replace('raddr=', ''))


def is_manage_string(sub, string):
    tokens = shlex.split(string)
    try:
        if len(tokens) == 2 and tokens[0] == sub and str(int(tokens[-1])):
            return True, int(tokens[-1])
    except Exception as e:
        print(e)
    return False


manage_process = False


def manage():
    global manage_process
    while True:
        manage_process = False
        command = input('>>> ').strip()
        if command == 'list':
            try:
                for i in range(1, len(connections) + 1):
                    print(f'{i}\t{connections[i][-1]}')
            except KeyError:
                pass
            if len(connections) == 0:
                print('[-] There are not any connections')
        elif 'manage' in command:
            index = is_manage_string('manage', command)
            if index:
                index = index[-1]
            else:
                print('[-] Invalid command\nUse manage "number_of_connection"\nEx: manage 1')
                continue
            if index >= 1 and index <= len(connections):
                sock, addr = connections[index]
                print(addr)
                print(f'{addr} is used')
                while True: ##Important here i launch loop which send data to socket
                    manage_process = True
                    command = input('>>> ').strip()
                    if command == 'messages':
                        try:
                            if messages[addr] == list():
                                print()
                                continue
                        except KeyError:
                            pass
                        try:
                            print('\n\n'.join(messages[addr]))
                        except KeyError:
                            print()
                    elif command == 'message':
                        try:
                            print(messages[addr][-1])
                        except:
                            print()
                    elif command == 'clear_messages':
                        try:
                            if messages[addr]:
                                messages[addr] = []
                        except KeyError:
                            print('[-] There are not any messages for cleaning up')
                    elif command == 'leave':
                        print(f'Leaving connection {addr}')
                        break
                    elif command: ##Important if command hasn't been detected as my special command(leave, messages), it will be executed like shell command
                        try:
                            sock.send(command.encode('utf-8'))
                            print(
                                'Your input has not been detected as special command and will execute like shell command or like client special command(ex: download; see client file)')
                        except ConnectionResetError:
                            print("Connection has been lost, therefore shell commands can't be used")
                    else:
                        continue

            else:
                print('[-] Invalid number of connection')

        elif command:
            print('[-] Invalid command\nType "help" to see avalible commands')

##Important
def event_loop():
    while True:
        data = selector.select()
        for key, _ in data:
            try:
                key.data(key.fileobj)
            except ConnectionResetError:
                selector.unregister(key.fileobj)


##Important
threading.Thread(target=manage, daemon=True).start()
event_loop()


Client :

import socket
import subprocess
import shlex
import threading
import json
import base64


s = socket.socket()
s.connect(('localhost', 4444))


##Important
def read(file):
    with open(file, 'rb') as f:
        return base64.b64encode(f.read())


def runner(data):
    sp_data = shlex.split(data)
    try:
        print(sp_data)
        if len(sp_data) == 2 and sp_data[0] == 'download': ###Important here we create json object which will be send to socketserver
            try:
                content = read(sp_data[-1]).decode('utf-8')
                print(content)
                msg = json.dumps({'file': sp_data[-1], 'command': data, 'content': content,
                                  'msg': f'[+] File {sp_data[-1]} has been successfully downloaded.'}).encode('utf-8')
            except FileNotFoundError:
                msg = json.dumps({'msg': f'[-] File {sp_data[-1]} not found', 'command': data}).encode('utf-8')
            s.send(msg)
            return ''
    except Exception as e:
        print(e)
    command = subprocess.run(data, shell=True, encoding='cp866', text=True, capture_output=True)
    command = command.stderr if command.stderr else command.stdout
    command = json.dumps({'msg': command, 'command': data})
    s.send(command.encode('utf-8'))


while True:##Important
    data = s.recv(4096).decode('utf-8').strip()
    threading.Thread(target=runner, args=(data,)).start()


1
kali_xyyali 27 oct. 2020 à 09:29

2 réponses

Meilleure réponse
import socket
import struct


class Socket(socket.socket):
    def __init__(self):
        self.sock = socket.socket()
        super().__init__(socket.AF_INET, socket.SOCK_STREAM)

    def send_msg(self, msg):
        # Prefix each message with a 4-byte length (network byte order)
        msg = struct.pack('>I', len(msg)) + msg
        self.sock.sendall(msg)

    def recv_msg(self):
        # Read message length and unpack it into an integer
        raw_msglen = self.recv_all(4)
        if not raw_msglen:
            return None
        msglen = struct.unpack('>I', raw_msglen)[0]
        # Read the message data
        return self.recv_all(msglen)

    def recv_all(self, n):
        data = bytearray()
        while len(data) < n:
            packet = self.sock.recv(n - len(data))
            if not packet:
                return None
            data.extend(packet)
        return data

J'ai remodelé votre code en classe Socket.

1
Nikto 29 oct. 2020 à 07:48

Solution - utilisez ces fonctionnalités:

def send_msg(sock, msg):
    # Prefix each message with a 4-byte length (network byte order)
    msg = struct.pack('>I', len(msg)) + msg
    sock.sendall(msg)

def recv_msg(sock):
    # Read message length and unpack it into an integer
    raw_msglen = recvall(sock, 4)
    if not raw_msglen:
        return None
    msglen = struct.unpack('>I', raw_msglen)[0]
    # Read the message data
    return recvall(sock, msglen)

def recvall(sock, n):
    # Helper function to recv n bytes or return None if EOF is hit
    data = bytearray()
    while len(data) < n:
        packet = sock.recv(n - len(data))
        if not packet:
            return None
        data.extend(packet)
    return data
1
kali_xyyali 27 oct. 2020 à 07:12