c# – Реализация клиент-серверного приложения
Имеется следующая архитектура: клиентская часть(dll-ка на C#) отсылает определенное количество картинок на сервер(либо Windows Service, либо Web Service), где они обрабатываются, а потом отсылается ответ в виде XML файла результатов обработки.
Клиент – это просто автоматизированное приложение, без интерфейса и ввода вывода.
Сервер. На нем крутится движок, использующий многопоточность (с помощью ThreadPool) для обработки картинок. Соответственно, когда обращается новый клиент, сервер создает новый поток, в котором происходит обработка, по окончанию он отсылает ответ пользователю(xml-файл). Нагрузка на сервер планируется не очень большая 3-20 одновременных подключений.
Пока что я не могу понять какая архитектура взаимодействия лучше всего подойдет для моего случая. Есть несколько путей реализации, либо писать асинхронный сервер на сокетах, либо использовать WCF, или просто написать ASP.NET приложение и залить его на IIS(к этому варианту я склоняюсь больше всего).
Какой протокол передачи лучше всего использовать? Хватит ли HTTP для передачи большого количества картинок(тогда можно двигаться в направлении Web Service), или стоит задуматься о TCP/IP(здесь уже WCF)?
Кому интересно, несколько статей по созданию клиент-серверного приложения:
Example with ASYNC/AWAIT
Example with THREADS
Example with SOCKETS
- c#
- asp.net
- сокет
- wcf
- клиент-сервер
2
HTTP, WCF и голый TCP справляются с заливкой картинок примерно одинаково.
Особенно если учесть, что HTTP работает поверх TCP, а WCF – или поверх HTTP, или с собственным протоколом поверх TCP, в зависимости от настроек биндинга.
Никакой ощутимой разницы между реализациями с точки зрения производительности не будет.
То же самое с типом хостинга – между Self-Hosted и IIS нет практически никакой разницы (не при вашей нагрузке).
Вам стоит использовать то, что вам удобнее в написании и поддержке.
3
Зарегистрируйтесь или войдите
Регистрация через Facebook
Регистрация через почту
Отправить без регистрации
Почта
Необходима, но никому не показывается
Отправить без регистрации
Почта
Необходима, но никому не показывается
Нажимая на кнопку «Отправить ответ», вы соглашаетесь с нашими пользовательским соглашением, политикой конфиденциальности и политикой о куки
сокет – Клиент-серверное приложение на 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)
9 Программирование сокетов
9 Программирование сокетовФайл: Стив/Курсы/2014/s2/its332/sockets.tex, r3521
Мы знаем, что многие интернет-приложения используют для связи модель клиент/сервер:
сервер прослушивает соединения; и клиент инициирует соединения с сервером.
Как это
реализованы клиентские и серверные программы? В этой главе вы изучите основы программирования
конструкции, называемые сокетами, для создания клиентской и серверной программы. Вы можете использовать эти
программные конструкции для реализации собственного клиент-серверного приложения. Эта глава
объясняет сокеты на примере языка программирования C. Пример исходного кода C:
приведены в Приложении D. Сокеты также используются в других языках программирования. Приложение Д
дает пример исходного кода Python. Весь исходный код доступен для скачивания через
http://ict.siit.tu.ac.th/~sgordon/netlab/source/.
9.1 Программирование с помощью сокетов
Сокеты — это программные конструкции, используемые для связи между процессами. Есть различные типы систем, для которых могут использоваться сокеты, основной из которых нас интересует: Интернет-сокеты (другим часто используемым сокетом являются сокеты Unix).
Сокеты для Интернет-программирования были созданы в ранних версиях Unix (написанных на C
код).
Из-за популярности Unix для сетевых вычислений в то время эти Unix/C
сокеты на основе становятся довольно распространенными. Теперь та же концепция была расширена
на другие языки и другие операционные системы. Поэтому, хотя мы используем код C и
Система на базе Unix (Ubuntu Linux), принципы применимы практически к любому компьютеру.
система.
Существует два основных типа сокетов Интернета, соответствующих двум основным транспортным сетям Интернета. протоколы:
- Потоковые сокеты используют для связи протокол управления передачей (TCP). TCP
ориентирован на поток, отправляя поток байтов получателю. Это также надежный
транспортный протокол, а значит гарантирует, что все данные поступят к получателю,
и приходит по порядку. TCP начинает устанавливать соединение (мы видели
3-стороннее рукопожатие в других лабораториях), а затем отправка данных между отправителем и получателем.
TCP используется для большинства приложений, ориентированных на данные, таких как просмотр веб-страниц, передача файлов и
Эл.
адрес. Сокеты дейтаграмм - используют для связи протокол пользовательских дейтаграмм (UDP). UDP ненадежный протокол. Нет установки соединения или повторных передач. отправитель просто отправляет пакет (датаграмму) получателю и надеется, что он прибудет. UDP используется для большинства приложений, ориентированных на работу в реальном времени, таких как передача голоса по IP и видео. разговоры.
В этой лабораторной работе мы имеем дело только с сокетами Stream (TCP).
Основная процедура показана на рисунке 9.1. Сервер должен сначала создать сокет, а затем связать или привязать IP-адрес и номер порта к этому сокету. Затем сервер прослушивает связи.
Рисунок 9.1. Связь через сокет
Клиент создает сокет, а затем подключается к серверу.
Системный вызов connect()
от клиента запускает сегмент TCP SYN от клиента к серверу.
Сервер принимает соединение от клиента. Системный вызов accept() на самом деле является блокирующая функция — когда программа вызывает accept(), сервер не возвращается из работать до тех пор, пока не получит сегмент TCP SYN от клиента и не завершит трехсторонний рукопожатие.
После возврата клиента из системного вызова connect() и возврата сервера из accept(), соединение установлено. Теперь двое могут отправить данные.
Отправка и получение данных осуществляется с помощью функций write() и read(). читать () блокирующая функция — она вернется только тогда, когда сокет получит данные. Вы (приложение программист) должны правильно координировать операции чтения и записи между клиентом и сервером. Если клиент вызывает функцию read(), но данные с сервера не отправляются, тогда клиент будет ждать навсегда!
9.1.1 Серверы, обрабатывающие несколько соединений
Обычно сервер реализуется таким образом, что он может обрабатывать несколько подключений одновременно.
время. Самый распространенный способ сделать это — заставить главный серверный процесс прослушивать соединения,
и когда соединение установлено, создать дочерний процесс для обработки этого соединения (пока
родительский процесс возвращается к прослушиванию соединений). В нашем примере мы используем fork()
системный вызов.
Системный вызов fork() создает новый процесс, который является дочерним процессом текущего процесс. И родительский, и дочерний процессы выполняют следующую команду после вызова вилка(). fork() возвращает идентификатор процесса, который может быть:
- Отрицательный результат, означающий, что создание дочернего процесса не удалось
- 0 возвращается дочернему процессу
- Родительскому процессу возвращается положительное значение — это идентификатор процесса ребенок.
Следовательно, мы можем использовать идентификатор процесса, возвращенный функцией fork(), чтобы определить, что делать.
next — родительский процесс (pid > 0) завершит текущий цикл и вернется к ожиданию
связи. Дочерний процесс (pid = 0) будет производить обмен данными с
клиент.
9.1.2 Дополнительные пояснения
Вы должны прочитать исходный код server.c, а затем исходный код client.c. Комментарии содержат дополнительные пояснения того, как осуществляется связь через сокеты. выполнила.
Пример кода для client.c и server.c взят из http://www.cs.rpi.edu/courses/sysprog/sockets/sock.html. Вы можете прочитать через подробности на этой веб-странице.
Большинство системных вызовов сокетов подробно описаны на отдельных справочных страницах. Ты следует использовать справочные страницы для получения более подробной информации о каждой функции. Обратите внимание, что вы можете необходимо указать раздел справочных страниц для использования (это раздел 2, Системные вызовы раздел):
$ man -S2 accept
ACCEPT(2) Руководство программиста Linux ACCEPT(2)
ИМЯ
accept – принять соединение на сокете
3 .
..
Примечание 3. Справочные страницы Unix Справочные страницы в Unix сгруппированы в разделы. Может быть команда/файл/функция в Unix с тем же именем, но в разных разделах. Выполнять man man, чтобы получить список разделов. Например, accept — это системный вызов (раздел 2). а также команду системного администратора (раздел 8). Исполнитель принимает волю дать вам справочную страницу для команды системного администрирования. Чтобы увидеть руководство странице для системного вызова необходимо явно указать раздел: man -S2 accept. если ты не знаете раздел, который вы ищете, используйте опцию -k для поиска, например, man -к принять.
9.2 Задачи
Для выполнения следующих задач необходимо записать тесты с помощью Wireshark, чтобы понять отношения между приложениями и сетевыми коммуникациями. Для новых программ, которые вы создаете, вы должны продемонстрировать программу и исходный код инструктор.
Задача 9.
1. Загрузите, скомпилируйте и протестируйте предоставленные программы клиент-серверных сокетов.
Задача 9.2. Измените программы клиент/сервер, чтобы разрешить обмен несколькими сообщениями. К сделать это, создать «фальшивый» механизм входа в систему. Сервер должен запросить у клиента имя пользователя отправив сообщение с именем пользователя, а затем клиент отправит имя пользователя на сервер. Затем сервер запросит пароль, отправив сообщение с паролем, а затем клиент отправит пароль на сервер. Наконец, сервер проверит, является ли имя пользователя и пароль совпадают с уже известной парой имя пользователя/пароль и отправляют ответ обратно клиенту. Затем и клиент, и сервер могут завершить работу (конечно, сервер все равно должен обрабатывать больше подключений). Результат взаимодействия должен выглядеть следующим образом:
Клиент Сервер
1.
Имя пользователя:
2. <пользователь вводит имя пользователя, например. "Х">
3. Клиент логин с имя пользователя “X”
4. Пароль:
5. <пользователь вводит пароль, например. "Ю">
6. Клиент ввел пароль “Y”
7. Имя пользователя и пароль указаны правильно.
8. Вы вошли в систему.
Задача 9.3. Используя прилагаемые программы клиент-серверных сокетов, реализуйте третий прокси-сервер. сервер.
POSIX Socket в C
При программировании Socket у нас должно быть два узла, т. е. клиент и сервер. Конечная точка для передачи известна как сокет POSIX или просто сокет. Чтобы отправитель и получатель, A и B, могли взаимодействовать друг с другом, обе эти стороны должны сначала создать связь между своими конкретными конечными точками. Другой сокет (конечная точка) ищет первый сокет для создания ссылки, тогда как первый сокет отвечает на определенный порт по IP-адресу. В то время как клиент обращается к серверу, сервер генерирует сокет прослушивателя.
В этом руководстве мы объясним использование функции Socket из библиотеки POSIX языка C при использовании операционной системы Ubuntu 20.04 Linux.
Сторона сервера:
Написание клиентского и серверного кода необходимо для программирования сокетов. К одному узлу или серверу может быть подключено несколько клиентов. Необходимые заголовочные файлы для использования при программировании сокетов включены в первые восемь строк. Установив значение 8080, мы определяем глобальную переменную PORT. Если порт 8080 недоступен, мы можем поменять его на любой другой доступный порт.
Мы объявляем несколько переменных целочисленного типа в основном методе, которые мы будем использовать позже. Эти переменные включают переменные для файлового дескриптора сервера (server_file_desc), нового сокета (new_socket) и значения или сообщения, считанного с клиента (value_Read). Адреса для семейства интернет-адресов хранятся в структуре типа sockaddr, которая объявлена в следующей строке.
Позже мы определили option=1, address Length=sizeof(socket_Address) и buffer[1024], которые изначально были установлены в 0 и использовались для сообщений, получаемых от клиентов размером 1024 байта. Далее следует переменная message, в которой хранится сообщение, отправленное сервером подключенным к нему клиентам. В следующей строке мы создаем сокет и передаем ему AF_INET (семейство адресов, известное как AF INET, используется для указания типов адресов, с которыми может соединяться ваш сокет (в данном случае это IP-адреса (протокол Интернета) v4/v6).
TCP — это протокол передачи данных, основанный на соединении. Как только соединение установлено между двумя сторонами (клиент-сервер), связь начинается до тех пор, пока соединение не будет прервано или прервано одной из двух сторон или какой-либо ошибкой сетевого соединения. Вот снимок кода на стороне сервера, записанный в файле server.c.
Теперь мы принудительно подключаем сокет к порту 8080. После этого мы инициализируем семейство сокетов, адрес и порт.
После этого привязываем адрес сокета к порту 8080. Теперь сервер готов слушать любое соединение от клиента. Поэтому мы печатаем сообщение перед методом прослушивания. Когда любой клиент подключается к серверу, используя этот порт, сервер примет клиентское соединение.
Если клиент и сервер подключены, сервер прочитает сообщение от клиента, используя буфер, и отобразит его на экране терминала. Аналогично тому, как клиенты получают сообщения от серверов, отправляют методы для передачи сообщения и информацию о сокетах клиентам. Затем сервер отображает сообщение об успешном выполнении, прежде чем прервать связь с соответствующим сокетом. Сервер также закрыл прослушивающий порт сокета на сервере.
Сторона клиента:
На изображении ниже показан фрагмент кода файла Client.c:
Клиент подключается к серверу через сокет с помощью метода connect(), и мы также передали адрес сервера. Клиент и сервер могут взаимодействовать после того, как соединение будет эффективно установлено.
Отображать сообщение об ошибке на экране терминала, если сервер не работает или клиент не может подключиться по какой-либо другой причине. После успешного подключения клиент будет использовать сокет для отправки сообщения на сервер, чтения сообщения с сервера с использованием буфера в значение переменной Read и отображения ответа сервера на экране. Затем клиент отключает связь со связанным сокетом.
Мы скомпилировали файлы Server.c и Client.c с помощью компилятора GCC и сохранили результаты в папках Server.c и Client.c. Команды для компиляции файлов сервера и клиента перечислены ниже.
Нам нужны были два отдельных терминала, один для сервера и один для клиента, чтобы видеть связь между ними. Нажмите Ctrl+SHIFT+N, чтобы запустить новый терминал в Ubuntu. Одна вещь, которую вы должны иметь в виду, это то, что сервер должен быть активен и ожидать соединения от клиента. Вы получите сообщение об ошибке «Сбой подключения к серверу», если сервер находится в автономном режиме и не подключен по какой-либо причине, в то время как клиент хочет подключиться к серверу.
Снимок ниже показывает эту ошибку:
Когда мы запускаем выходной файл сервера, сервер будет ждать подключения клиента, прежде чем они смогут взаимодействовать. Просто введите ./Server в только что открытом терминале, чтобы запустить сервер. Вот скриншот, показывающий, что сервер запущен и готов принимать подключения от клиентов:
Пришло время запустить клиент прямо сейчас. Чтобы инициировать связь между клиентом и сервером, введите «/Client» на новом терминале. Затем сервер отвечает и отправляет сообщение, которое клиент читает, сохраняет в переменной сообщения и отображает с помощью оператора printf. Сообщение клиента отображалось на экране терминала ниже.
Когда клиент подключается к серверу, сервер принимает сообщение для проверки соединения. Таким образом, сообщение клиента отображается на экране ниже. После этого сервер уведомит клиента и отобразит сообщение об успешном завершении. После этой успешной связи сервер завершает или прерывает соединение с клиентом.
