Этот сценарий реализует простую систему за груз ки файлов с сервера. Один ее экземпляр выполняется на компьютере, где находятся загружаемые файлы (на сервере), а другой — на компьютере, куда должны копироваться файлы (на клиенте). Аргументы командной строки определяют, в каком качестве используется сценарий, а также могут использоваться для определения имени компьютера сервера и номера порта, через который должна производиться передача. Экземпляр сервера может отвечать на любое количество запросов файлов клиентами на порту, который он слушает, так как каждый запрос обслуживается в отдельном потоке.
Пример 12.17. PP4E\Internet\Sockets\getfile.py
############################################################################ реализует логику работы клиента и сервера для передачи произвольного файла от сервера клиенту через сокет; использует простой протокол с управляющей информацией вместо отдельных сокетов для передачи управляющих воздействий и данных (как в ftp), обработка каждого клиентского запроса выполняется в отдельном потоке, где организован цикл поблочной передачи содержимого файла; более высокоуровневую схему организации транспортировки вы найдете в примерах ftplib;
############################################################################
import sys, os, time, _thread as thread
from socket import *
blksz = 1024
defaultHost = ‘localhost’
defaultPort = 50001
helptext = """
Usage…
server=> getfile.py -mode server [-port nnn] [-host hhh|localhost]
client=> getfile.py [-mode client] -file fff [-port nnn] [-host hhh|localhost]
def now():
return time.asctime()
def parsecommandline():
dict = {} # поместить в словарь для упрощения поиска
args = sys.argv[1:] # пропустить имя программы в начале аргументов
while len(args) >= 2: # пример: dict[‘-mode’] = ‘server’
dict[args[0]] = args[1]
args = args[2:]
return dict
def client(host, port, filename) sock = socket(AF_INET, SOCK_STREAM) sock.connect((host, port))
sock.send((filename + ‘\n’).encode()) # имя файла с каталогом: bytes dropdir = os.path.split(filename)[1] # имя файла в конце пути file = open(dropdir, ‘wb’) # создать локальный файл в cwd
while True:
data = sock.recv(blksz) # получать единовременно до 1 Кбайта
if not data: break # до закрытия сервером
file.write(data) # сохранить данные в локальном файле
sock.close() file.close() print(‘Client got’, filename, ‘at’, now())
def serverthread(clientsock):
sockfile = clientsock.makefile(‘r’) # обернуть сокет объектом файла
filename = sockfile.readline()[:-1] # получить имя файла
# без конца строки try:
file = open(filename, ‘rb’)
while True:
bytes = file.read(blksz) # читать/отправлять по 1 ‘у
if not bytes: break # до полной передачи файла
sent = clientsock.send(bytes) assert sent == len(bytes)
except:
print(‘Error downloading file on server:’, filename) clientsock.close()
def server(host, port):
serversock = socket(AF_INET, SOCK_STREAM) # слушать на сокете TCP/IP serversock.bind((host, port)) # обслуживать клиентов в потоках
serversock.listen(5) while True:
clientsock, clientaddr = serversock.accept()
print(‘Server connected by’, clientaddr, ‘at’, now())
thread.start_new_thread(serverthread, (clientsock,))
def main(args):
host = args.get(‘-host’, defaultHost) # аргументы или умолчания
port = int(args.get(‘-port’, defaultPort)) # строка в argv
if args.get(‘-mode’) == ‘server’: # None, если нет -mode: клиент
if host == ‘localhost’: host = » # иначе потерпит неудачу
server(host, port) # при удаленной работе
elif args.get(‘-file‘): # в режиме клиента нужен —file
client(host, port, args[‘-file’])
else:
print(helptext)
if __name__ == ‘__main__’:
args = parsecommandline() main(args)
В этом сценарии нет ничего особенного в сравнении с уже встречавшимися примерами. В зависимости от аргументов командной строки он вызывает одну из двух функций:
• Функция server направляет все поступающие клиентские запросы в потоки, отправляющие байты запрошенного файла.
• Функция client посылает серверу имя файла и сохраняет полученные от него байты в локальном файле с таким же именем.
Наибольшая новизна заключается в протоколе между клиентом и сервером: клиент начинает диалог с сервером путем отправки ему строки с именем файла, оканчивающейся символом конца строки и содержащей путь к файлу на сервере. На сервере порожденный поток извлекает имя запрошенного файла, читая данные из сокета клиента, открывает запрошенный файл и отправляет его клиенту по частям.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011