Отправка сообщения после завершения передачи файла - PullRequest
0 голосов
/ 03 октября 2018

Я новичок в программировании сокетов.Я пытаюсь отправить 4 файла с одного хоста на другой.Вот код:

отправитель:

from __future__ import print_function
import socket
from struct import pack

HOST = '10.0.0.2'
PORT = 12345
BUFSIZE = 4096

def send(sock, data):
    while data:
        sent = sock.send(data)
        data = data[sent:]

def send_file(fname):
    with open(fname, 'rb') as f:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            sock.connect((HOST, PORT))
        except socket.error as err:
            print(err, HOST, PORT)
            sock.close()
            return

        # Send the file name length & the filename itself in one packet          
        send(sock, pack('B', len(fname)) + fname.encode())
        while True:
            data = f.read(BUFSIZE)
            if not data:
                break
            send(sock, data)

    sock.close()

fnames = [
    '1.jpg',
    '2.jpg',
    '3.jpg',
    '4.jpg',
]

def main():
    for fname in fnames:
        send_file(fname)

if __name__ == '__main__':
    main()

Получатель:

from __future__ import print_function
import socket
from struct import unpack

HOST = '10.0.0.2'
PORT = 12345
BUFSIZE = 4096


class Receiver:
    ''' Buffer binary data from socket conn '''
    def __init__(self, conn):
        self.conn = conn
        self.buff = bytearray()

    def get(self, size):
        ''' Get size bytes from the buffer, reading
            from conn when necessary 
        '''
        while len(self.buff) < size:
            data = self.conn.recv(BUFSIZE)
            if not data:
                break
            self.buff.extend(data)
        # Extract the desired bytes
        result = self.buff[:size]
        # and remove them from the buffer
        del self.buff[:size]
        return bytes(result)

    def save(self, fname):
        ''' Save the remaining bytes to file fname '''
        with open(fname, 'wb') as f:
            if self.buff:
                f.write(bytes(self.buff))
            while True:
                data = self.conn.recv(BUFSIZE)
                if not data:
                    break
                f.write(data)


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        sock.bind((HOST, PORT))
    except socket.error as err:
        print('Bind failed', err)
        return

    sock.listen(1)
    print('Socket now listening at', HOST, PORT)
    try:
        while True:
            conn, addr = sock.accept()
            print('Connected with', *addr)
            # Create a buffer for this connection
            receiver = Receiver(conn)
            # Get the length of the file name
            name_size = unpack('B', receiver.get(1))[0] 
            # Get the file name itself
            name = receiver.get(name_size).decode()
            print('name', name)
            # Save the file
            receiver.save(name)
            conn.close()
            print('saved\n')

    # Hit Break / Ctrl-C to exit
    except KeyboardInterrupt:
        print('\nClosing')

    sock.close()

if __name__ == '__main__':
    main()

Передача файлов работает нормальнои с этим проблем нет.Теперь я хочу послать простую строку типа «конец» после отправки всех файлов, чтобы получатель понял, что передача завершена, и он выполнит некоторые другие задачи, основанные на этом сообщении завершения (однако он все еще может получать сообщения в то же времявремя).

Я попытался сделать это, добавив еще одну функцию с именем sendMessage() к коду отправителя и функцию с именем recvMessage() к получателю.Вот измененные коды:

Отправитель:

from __future__ import print_function
import socket
from struct import pack

HOST = '10.0.0.2'
PORT = 12345
BUFSIZE = 4096
BUFFER_SIZE = 1024
MESSAGE = "Finish!"

def send(sock, data):
    while data:
        sent = sock.send(data)
        data = data[sent:]

#Updated part for sending message
def sendMessage(message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((HOST, PORT))
    sock.send(message)
    data = sock.recv(BUFFER_SIZE)
    sock.close()
    print ("received data:", data)

def send_file(fname):
    with open(fname, 'rb') as f:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            sock.connect((HOST, PORT))
        except socket.error as err:
            print(err, HOST, PORT)
            sock.close()
            return

        # Send the file name length & the filename itself in one packet          
        send(sock, pack('B', len(fname)) + fname.encode())
        while True:
            data = f.read(BUFSIZE)
            if not data:
                break
            send(sock, data)

    sock.close()

fnames = [
    '1.jpg',
    '2.jpg',
    '3.jpg',
    '4.jpg',
]

def main():
    for fname in fnames:
        send_file(fname)
    sendMessage(MESSAGE)

if __name__ == '__main__':
    main()

Получатель:

from __future__ import print_function
import socket
from struct import unpack

HOST = '10.0.0.2'
PORT = 12345
BUFSIZE = 4096
BUFFER_SIZE = 20

class Receiver:
    ''' Buffer binary data from socket conn '''
    def __init__(self, conn):
        self.conn = conn
        self.buff = bytearray()

    def get(self, size):
        ''' Get size bytes from the buffer, reading
            from conn when necessary 
        '''
        while len(self.buff) < size:
            data = self.conn.recv(BUFSIZE)
            if not data:
                break
            self.buff.extend(data)
        # Extract the desired bytes
        result = self.buff[:size]
        # and remove them from the buffer
        del self.buff[:size]
        return bytes(result)

    def save(self, fname):
        ''' Save the remaining bytes to file fname '''
        with open(fname, 'wb') as f:
            if self.buff:
                f.write(bytes(self.buff))
            while True:
                data = self.conn.recv(BUFSIZE)
                if not data:
                    break
                f.write(data)

#Updated part for receiving message
def recvMessage(conn):
    while 1:
        data = conn.recv(BUFFER_SIZE)
        if not data: break
        print("received data:", data)
        conn.send(data)  # echo


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        sock.bind((HOST, PORT))
    except socket.error as err:
        print('Bind failed', err)
        return

    sock.listen(1)
    print('Socket now listening at', HOST, PORT)
    try:
        while True:
            conn, addr = sock.accept()
            print('Connected with', *addr)
            # Create a buffer for this connection
            receiver = Receiver(conn)
            # Get the length of the file name
            name_size = unpack('B', receiver.get(1))[0] 
            # Get the file name itself
            name = receiver.get(name_size).decode()
            print('name', name)
            # Save the file
            receiver.save(name)
            conn.close()
            print('saved\n')

    recvMessage(conn)
    # Hit Break / Ctrl-C to exit
    except KeyboardInterrupt:
        print('\nClosing')

    sock.close()

if __name__ == '__main__':
    main()

, но после выполнения этихкоды как отправителя, так и получателя замораживаются после полной передачи 4 файлов и ничего не происходит.Что не так и как я могу это сделать?

1 Ответ

0 голосов
/ 03 октября 2018

Я подозреваю, что вы становитесь жертвой буферизации здесь:

def sendMessage(message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((HOST, PORT))
    sock.send(message)
    data = sock.recv(BUFFER_SIZE)
    sock.close()
    print ("received data:", data)

Вы выполняете send, а затем сразу же пытаетесь recv.За исключением потоковых соединений, которые имеют тенденцию к буферизации, чтобы избежать чрезмерных издержек пакета, так что, скорее всего, вы еще ничего не отправляете, сервер ничего не видит, поэтому он не отвечает, и обе стороны блокируются в ожидании данных.

Самое простое решение здесь - отключить порт на стороне отправки для записи после отправки сообщения, что вытесняет последние данные и сообщает получателю, что вы закончили:

def sendMessage(message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((HOST, PORT))
    sock.sendall(message)          # sendall makes sure the *whole* message is sent
    sock.shutdown(socket.SHUT_WR)  # We're done writing
    data = sock.recv(BUFFER_SIZE)
    sock.close()
    print("received data:", data)

На стороне получателя у вас есть большая проблема: вы закрываете соединение, прежде чем пытаться получить вообще:

    while True:
        conn, addr = sock.accept()
        print('Connected with', *addr)
        # Create a buffer for this connection
        receiver = Receiver(conn)
        # Get the length of the file name
        name_size = unpack('B', receiver.get(1))[0] 
        # Get the file name itself
        name = receiver.get(name_size).decode()
        print('name', name)
        # Save the file
        receiver.save(name)
        conn.close()       # Closed here!!!
        print('saved\n')

        recvMessage(conn)  # Used again here!!!

Поэтому переместите close после вызова recvMessage и измените recvMessage на используйте setsockopt до , включите TCP_NODELAY, чтобы не происходила буферизация (в противном случае обратный эхо-сигнал может закончиться буферизацией на неопределенный срок, хотя отключение отправителя для записи означает, что вывы, вероятно, обнаружите, что отправитель сделан, и выйдите из цикла, затем закроете соединение, поэтому оно может нормально работать без TCP_NODELAY, если отправитель не ожидает получения данных иответь дальше):

def recvMessage(conn):
    # Disable Nagle algorithm so your echoes don't buffer
    conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    while 1:
        data = conn.recv(BUFFER_SIZE)
        if not data: break
        print("received data:", data)
        conn.sendall(data)  # echo using sendall, again, to ensure it's all really sent
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...