Python: клиент-сервер

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Python: клиент-сервер

Непрочитанное сообщение Olej » 22 фев 2019, 18:54

Понадобилось написать сильно специализированный клиент-сервер, для создания сервиса на облаке для распознавания лиц людей (вопрос: зачем? - заказчик попросил и платит ;-) ).
Поскольку это распознавание лиц, то это должен быть Python ... + разнообразные средства-утилиты Linux, такие как NetCat, inetd/xinetd и т.п.
Там есть насколько разнообразных составляющих проблем в составе общей проблемы...

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: клиент-сервер

Непрочитанное сообщение Olej » 22 фев 2019, 19:03

Olej писал(а):Понадобилось написать сильно специализированный клиент-сервер,
Базовый клиент-сервер:
- клиент отправляет поток, полученный из SYSIN ...
- сервер возвращает длину полученного потока (позже это будет изображение);

- echo_srv.py :

Код: Выделить всё

#!/usr/bin/python3
# -*- coding: utf-8 -*- 
import argparse
import socketserver
from tcpip import *

parser = argparse.ArgumentParser()  
parser.add_argument( '-p', '--port', type=int, default = PORTt, help = 'TCP/IP port' )
parser.add_argument( '-v', '--verbose', action = 'count', help = 'increase output verbosity' )
args = vars( parser.parse_args() )

debug_level = 0                                          # verbose level
if args[ 'verbose' ] != None:
    debug_level = int( args[ 'verbose' ] )
if debug_level:
    print( 'version {}'.format( version ) )    
if debug_level > 1:
    print( 'options: {}'.format( args ) )

class MyTCPHandler( socketserver.BaseRequestHandler ):
    def handle( self ):
        self.data = b''
        while True:
            s = self.request.recv( bufsize )
            self.data += s
            if( len( s ) < bufsize ): break
        if debug_level: 
            print("{} wrote {} bytes".format( self.client_address[ 0 ], len( self.data ) ) )
        self.request.sendall( bytes( str( len( self.data ) ) + '\n', 'utf-8' ) )

# Create the server, binding to INADDR_ANY on port
with socketserver.TCPServer( ( '', PORTt ), MyTCPHandler ) as server:
    # Activate the server; this will keep running until you interrupt the program with Ctrl-C
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()
        print()
- echo_cli.py :

Код: Выделить всё

#!/usr/bin/python3
# -*- coding: utf-8 -*- 
import argparse
import socket
import sys
from tcpip import *

parser = argparse.ArgumentParser()  
parser.add_argument( '-p', '--port', type=int, default = PORTt, help = 'TCP/IP port' )
parser.add_argument( '-a', '--address', default = HOST, help = 'server IP address' )
parser.add_argument( '-v', '--verbose', action = 'count', help = 'increase output verbosity' )
args = vars( parser.parse_args() )

debug_level = 0                                # verbose level
if args[ 'verbose' ] != None:
    debug_level = int( args[ 'verbose' ] )
if debug_level:
    print( 'version {}'.format( version ) )
if debug_level > 1:
    print( 'options: {}'.format( args ) )

data = b'' 
while( True ):        
    s = sys.stdin.read( bufsize )              # read from SYSIN
    s = bytes( s, 'utf-8' )
    if b'' == s: break                         # EOF
    data += s 
if debug_level > 1: print( 'input data length {}'.formar( len( data ) ) )

# Create a socket (SOCK_STREAM means a TCP socket)
with socket.socket( socket.AF_INET, socket.SOCK_STREAM ) as sock:
    # Connect to server and reply with data
    sock.connect( ( args[ 'address' ], args[ 'port' ] ) )
    sock.sendall( data )
    # Receive data from the server and shut down
    received = b''
    while True:
        s = sock.recv( bufsize )
        received += s
        if( len( s ) != bufsize ): break
    sock.close()

if debug_level:
    print( "Sent:     {}...".format( data[ : 50 ] ) )
    print( "Received: {}...".format( received[ : 50 ] ) )
print( "Reply:    {}".format( str( received, 'utf-8' ).strip() ) )
Вложения
echo_srv.py
(1.36 КБ) 96 скачиваний
echo_cli.py
(1.58 КБ) 90 скачиваний
tcpip.py
(82 байт) 91 скачивание

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: клиент-сервер

Непрочитанное сообщение Olej » 22 фев 2019, 19:06

Olej писал(а): Базовый клиент-сервер:
Выглядит это так:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./echo_srv.py -v
version 0.22
127.0.0.1 wrote 44 bytes
127.0.0.1 wrote 44 bytes
127.0.0.1 wrote 44 bytes
127.0.0.1 wrote 10 bytes
^C

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./echo_cli.py -a localhost < 44.txt -v
version 0.22
Sent:     b'1111111111111111111111111111111111111111111\n'...
Received: b'44\n'...
Reply:    44
olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./echo_cli.py -a 127.0.0.1 < 44.txt 
Reply:    44
olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./echo_cli.py -a localhost < 44.txt -v
version 0.22
Sent:     b'1111111111111111111111111111111111111111111\n'...
Received: b'44\n'...
Reply:    44
olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./echo_cli.py -a localhost -v
version 0.22
1234
5678
Sent:     b'1234\n5678\n'...
Received: b'10\n'...
Reply:    10
Вложения
44.txt
(44 байт) 94 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: клиент-сервер

Непрочитанное сообщение Olej » 22 фев 2019, 19:43

Olej писал(а):Там есть насколько разнообразных составляющих проблем в составе общей проблемы...
Ещё задача из того же комплекса:
- забрасывать на сервере файлы-изображения в заданный каталог...
- с сохранением имён исходных файлов, которыми наполняется каталог.
Не хотелось для этого делать специальное приложение - обойдёмся утилитами Linux... (nc, tar, gzip)

- передаём файлы по сети скриптом known_cli_add:

Код: Выделить всё

#!/bin/bash

if [ $# -eq 0 ]
then
   echo "using: $1 <file1 file2 ...>" 
   exit 1
fi

PORTk=54321

for f
do
   tar -czf- "$f" | nc 87.249.221.249 $PORTk
   echo "$f file sent"
done

exit 0
- на приёмном, серверном хосте работает принимающий скрипт:

Код: Выделить всё

#!/bin/bash

if [ $# -eq 0 ]
then
   echo "using: $1 <known faces directory>" 
   exit 1
fi

cd $1
if [ $? != 0 ]
then 
   exit 2
fi 

PORTk=54321

while [ 0 ]
do
   nc -l -p $PORTk | tar -xzf- 2>/dev/null
   if [ $? != 0 ]
   then 
      rm -f * 2>/dev/null
   fi 
done
Запускаем на сервере 87.249.221.249:

Код: Выделить всё

android@android-vm:~/FaceDL/cloud$ ./known_srv_add known2
...

Код: Выделить всё

android@android-vm:~/FaceDL/cloud$ tree known2
known2

0 directories, 0 files
Пересылаем файлы с клиента:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./known_cli_add Egor1.2.jpg Olena3.4.jpg 
Egor1.2.jpg file sent
Olena3.4.jpg file sent
Они появляются на сервере в нужном каталоге:

Код: Выделить всё

android@android-vm:~/FaceDL/cloud$ tree known2
known2
├── Egor1.2.jpg
└── Olena3.4.jpg

0 directories, 2 files

android@android-vm:~/FaceDL/cloud$ ls -l known2
total 44
-rw-rw-r-- 1 android android 23454 Жел 12 18:06 Egor1.2.jpg
-rw-rw-r-- 1 android android 20381 Жел 12 18:20 Olena3.4.jpg
Таким образом можем добавлять сколько угодно в каталог эталонов...
Для очистки каталога (тем же серверным скриптом) имеем другого клиента known_cli_clean:

Код: Выделить всё

#!/bin/bash

if [ $# -ne 0 ]
then
   echo "using: $0" 
   exit 1
fi

PORTk=54321

echo "" | nc 87.249.221.249 $PORTk
if [ $? == 0 ]
then
   echo "all filed deleted"
fi
exit 0
Выполняю очистку:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./known_cli_clean
all filed deleted

На сервере:

Код: Выделить всё

android@android-vm:~/FaceDL/cloud$ tree known2
known2

0 directories, 0 files
Вложения
known_srv_add.sh
(315 байт) 89 скачиваний
known_cli_add.sh
(251 байт) 97 скачиваний
known_cli_clean.sh
(250 байт) 93 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: клиент-сервер

Непрочитанное сообщение Olej » 01 мар 2019, 17:24

Olej писал(а): Базовый клиент-сервер:
- клиент отправляет поток, полученный из SYSIN ...
- сервер возвращает длину полученного потока (позже это будет изображение);
Тут я немного намудрил в коде - требует переделки:
- при чтении в сервере из TCP сокета с длиной 1024 ( s = self.request.recv( bufsize ) ) принятый объём данных в реальной сети (не localhost) может быть любым - от 1 до 1024 байт, поэтому признаком конца передачи может быть только чтение 0 байт:

Код: Выделить всё

        while True:
            s = self.request.recv( bufsize )
            if( 0 == len( s ) ): break
            self.data += s
- для того отличить конец передачи, чтобы сервер смог получить свой 0, клиент должен после передачи полузакрыть сокет:

Код: Выделить всё

with socket.socket( socket.AF_INET, socket.SOCK_STREAM ) as sock:
    # Connect to server and reply with data
    sock.connect( ( args[ 'address' ], args[ 'port' ] ) )
    sock.sendall( data )
...
- и, наконец, чтобы серверу можно было передать данные не только с терминала, но и бинарные данные большого объёма, по каналу перенаправления, входное чтение клиента переделаем так:

Код: Выделить всё

data = b'' 
while True:        
    s = sys.stdin.buffer.read( bufsize )              # read from SYSIN
    if 0 == len( s ): break                           # EOF
    data += s 
...

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: клиент-сервер

Непрочитанное сообщение Olej » 01 мар 2019, 17:38

Olej писал(а): Тут я немного намудрил в коде - требует переделки:
Передача:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ls -l Egor1.2.ppm 
-rw-r--r-- 1 olej olej 921660 фев 22 22:49 Egor1.2.ppm

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/cloud$ ./echo_cli.py -vv < Egor1.2.ppm 
version 0.28
options: {'address': '87.249.221.249', 'port': 30000, 'verbose': 2}
input data length 921660
Sent:     b'P6\n# Created by GIMP version 2.10.8 PNM plug-in\n64'...
Received: b'921660\n'...
Reply:    921660
И на приёме смотрим последовательность длин принимаемых участков данных:

Код: Выделить всё

android@android-vm:~/FaceDL/cloud$ ./echo_srv.py -vvv
version 0.28
options: {'verbose': 3, 'port': 30000}
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
1024
1024
248
1024
1024
848
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
1024
1024
1024
248
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
1024
848
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
1024
848
1024
1024
1024
1024
248
1024
424
1024
1024
848
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
1024
848
1024
1024
848
1024
1024
848
1024
1024
848
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
1024
1024
1024
1024
672
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
992
1024
1024
1024
1024
1024
672
1024
424
1024
424
1024
1024
848
1024
1024
1024
1024
1024
672
1024
1024
848
1024
1024
1024
1024
248
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
1024
1024
248
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
1024
848
1024
1024
1024
1024
248
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
1024
1024
1024
672
1024
424
1024
424
1024
1024
848
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
1024
1024
1024
248
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
1024
848
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
1024
424
732
0
193.28.177.125 wrote 921660 bytes
...
При этом передающий клиент находится под Тель-Авивом, а принимающий сервер - в Челябинске.

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: клиент-сервер

Непрочитанное сообщение Olej » 01 мар 2019, 17:50

Olej писал(а): И на приёме смотрим последовательность длин принимаемых участков данных:
Обращаем внимание на "гуляние" длин принимаемых фрагментов данных (ни о каких "пакетах" в TCP говорить бессмысленно!). У TCP сокета есть множество настроечных данных: алгоритм Нэйгла, отсроченные подтверждения, ширина окна приёма и др., при вариациях которых принимаемые длины будут гулять как-угодно и по-другому.

P.S. В TCP потоке нет никаких делений на пакеты, сообщения. Поэтому разделять сообщения в TCP потоке можно (существуют практики):
- для текстовой передачи данных - 2 последовательных '\n' подряд (пустая строка); так работают HTTP (GET и POST), SIP и мн. другие протоколы прикладного уровня;
- передавать длину последующего бинарного сообщения перед самим сообщением;
- полузакрывать (по записи) сокет после передачи сообщения;
- посылать уведомление о завершении сообщения по приоритетному каналу TCP (ургентные сообщения);
Это то, что я знаю, встречал, вспоминаю по памяти...
Вложения
echo_cli.py
(1.65 КБ) 94 скачивания
echo_srv.py
(1.39 КБ) 93 скачивания

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 4 гостя