Клиент-серверное приложение на Python c использованием утилиты ffmpeg
Всем добрый день! столкнулся с проблемой: пишу клиент серверное приложение через sockets. Клиент и сервер могут чатиться текстовыми сообщениями. Теперь подключили утилиту ffmpeg, чтобы захватывать через запись с устройства клиента и передавать серверу аудиозапись в wav формате. Все работает ровно до того момента, как осуществляется передача файла. То есть при подключении клиента к серверу я спокойно могу чатиться, осуществлять запись и передавать. В момент того, как файл передался серверу, я больше не могу отправлять сообщения с клиента (с сервера все отправляется). Подозреваю, что проблема с потоками, при передачи файла не закрывается какой-то поток. Важно, чтобы после отправки файла, я также мог чатить клиент-сервер. Сервер на win, клиент на CentOS.
код клиента:
import socket, threading
import os
import struct
import argparse
from subprocess import Popen
from sys import argv
import os
import signal
import time
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = input('целевой IP-адрес входа')
while True:
name = input('Пожалуйста, введите личный ник, не более десяти символов, менее одного символа')
if 1 < len(name) < 10:
break
port = 9090
client.connect((host, port))
print('-' * 5 + 'подключился к серверу' + '-' * 5)
print('-' * 5 + 'Enter, чтобы закрыть соединение с сервером' + '-' * 5)
def send_file_func():
send_file(client, "records/output.wav")
print('File has sent')
def outdatas():
while True:
outdata = input('')
if outdata == 'enter':
break
client.send(f'{name}:{outdata}'.encode('UTF-8'))
print('%s:%s' % (name, outdata))
proc = None
def record():
global proc
global pid
output = " records/" + 'output.wav'
cmdstr = "ffmpeg -f alsa -c:a pcm_s32le -sample_rate 44100 -i default:CARD=US16x08 -ac 4" + output
print(cmdstr)
proc = Popen(cmdstr, shell=True)
pid = proc.pid
def stop_record():
global proc
global pid
output = " records/" + 'output.wav'
cmdstr = "ffmpeg -f alsa -c:a pcm_s32le -sample_rate 44100 -i default:CARD=US16x08 -ac 4" + output
Popen.kill(proc)
os.kill(pid,signal.SIGTERM)
def indatas():
while True:
indata = client.recv(1024)
print(indata.decode('utf-8'))
if ('Сервер:' and f'Начать запись, {name}') in indata.decode('utf-8'):
client.send(f'{name}:Я начал запись'.encode('utf-8'))
record()
elif ('Сервер:' and f'Стоп, {name}') in indata.decode('utf-8'):
stop_record()
client.send(f'{name}:Я прислал файл'.encode('utf-8'))
send_file_func()
def send_file(sck: socket.socket, filename):
filesize = os.path.getsize(filename)
sck.sendall(struct.pack("<Q", filesize))
with open(filename, "rb") as f:
while True:
read_bytes = f.read(1024)
if not read_bytes:
break
sck.sendall(read_bytes)
#lock = threading.RLock()
t1 = threading.Thread(target=indatas, name='input')
t2 = threading.Thread(target=outdatas, name='out')
t1.start()
t2.start()
t1.join()
#t2.join()
print('-' * 5 + 'сервер отключен' + '-' * 5)
client.close()
Код сервера:
# Импортировать пакет сокетов
import socket, threading
import struct
import os
# Создаем объект сокета
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Получить локальный ip
host = socket.gethostname()
s_ip = socket.gethostbyname(host)
print(s_ip)
# Данный порт
port = 9090
# Укажите IP и порт сервера
server.bind((host, port))
# Максимальное количество подключений
server.listen(5)
print('Enter Enter для выхода с сервера')
# Создайте список клиентов
clients = list()
# Хранить клиентов, которые создали потоки
end = list()
# Блокировка ожидания подключения клиента, возврата объекта подключения и адреса косвенного объекта
def accept():
while True:
client, addr = server.accept()
clients.append(client)
print("\ r" + '-' * 5 + f'сервер подключен через {addr}: текущее количество подключений: ----- {len (clients)}' + '-' * 5, end = '\n') #Взаимодействие с другими людьми
def recv_data(client):
while True:
# Принимаем информацию от клиента. Если приходит текст - печатаем его. Если файл, вызываем функцию receive_file
# получаем сообщение вместе с файлом
try:
indata = client.recv(1024)
if 'Я прислал файл' in indata.decode('utf-8'):
# Ищем индекс символа : , затем определяем имя клиента, записываем в переменную name
index = indata.decode('utf-8').find(':')
name = indata.decode('utf-8')[:index]
# Проверяем каталог на наличие папки, если нет - создаем, если есть, кладем файл в нужную папку
if os.path.exists(name):
if os.path.isdir(name):
print('Папка существует')
filepath = name+'/'+'audio-received.wav'
else:
print('Папка не существует')
os.mkdir(name)
filepath = name + '/' + 'audio-received.wav'
# выполняем сохранение в нужном каталоге
receive_file(client, filepath)
print(indata.decode('utf-8'))
else:
print(indata.decode('utf-8'))
except Exception as e:
# если мы закрываем одного из клиентов, срабатывает блок except для того, чтобы не возникало
# ошибок в процессе работы
clients.remove(client)
end.remove(client)
print("\ r" + '-' * 5 + f'Сервер отключен: текущее количество подключений: ----- {len (clients)}' + '-' * 5, end = '\n')
break
finally:
for clien in clients:
# Перенаправить информацию от клиента и отправить ее другим клиентам
if clien != client:
clien.send(indata)
else:
continue
#print('Файл получен')
def outdatas():
while True:
# Введите информацию, которая будет предоставлена клиенту
print('')
outdata = input('Введите сообщение своим пользователям\n')
print()
if outdata == 'enter':
break
print('Отправить всем:% s' % outdata)
# Отправлять информацию каждому клиенту
for client in clients:
client.send(f"Сервер: {outdata}".encode('utf-8)'))
def indatas():
while True:
# Выполните цикл подключенных клиентов и создайте соответствующий поток
for clien in clients:
# Если поток уже существует, пропустить
if clien in end:
continue
index = threading.Thread(target=recv_data, args=(clien,))
index.start()
end.append(clien)
def receive_file_size(sck: socket.socket):
# Эта функция обеспечивает получение байтов,
# указывающих на размер отправляемого файла,
# который кодируется клиентом с помощью
# struct.pack(), функции, которая генерирует
# последовательность байтов, представляющих размер файла.
fmt = "<Q"
expected_bytes = struct.calcsize(fmt)
received_bytes = 0
stream = bytes()
while received_bytes < expected_bytes:
chunk = sck.recv(expected_bytes - received_bytes)
stream += chunk
received_bytes += len(chunk)
filesize = struct.unpack(fmt, stream)[0]
return filesize
def receive_file(sck: socket.socket, filename):
# Сначала считываем из сокета количество
# байтов, которые будут получены из файла.
filesize = receive_file_size(sck)
# Открываем новый файл для сохранения
# полученных данных.
with open(filename, "wb") as f:
received_bytes = 0
# Получаем данные из файла блоками по
# 1024 байта до объема
# общего количество байт, сообщенных клиентом.
while received_bytes < filesize:
chunk = sck.recv(1024)
if chunk:
f.write(chunk)
received_bytes += len(chunk)
print('Файл получен')
# Создать многопоточность
# Создать получающую информацию, объект потока
t1 = threading.Thread(target=indatas, name='input')
t1.start()
# Создать отправляемое сообщение, объект потока
t2 = threading.Thread(target=outdatas, name='out')
t2.start()
# Ожидание подключения клиента, объект потока
t3 = threading.Thread(target=accept(), name='accept')
t3.start()
# Блокировать округ, пока подпоток не будет завершен, и основной поток не может закончиться
t1.join()
#t2.join()
# Выключите все серверы
for client in clients:
client.close()
print('-' * 5 + 'сервер отключен' + '-' * 5)