Потоки. Передача файлов. Клиент-серверный чат

Потоки

Потоки (thread) в Python позволяет одновременно запускать разные подпрограммы, таким образом становится возможно выполнять несколько подпрограмм в одно и то же время. thread - это отдельный поток выполнения.

На самом деле подпрограммы даже при использовании потоков не выполняются одновременно:

  1. Работа с потоками - это не работа с разными ядрами процесса, мы фактически работаем внутри того же процесса, что и без потоков, только дробим его на подзадачи, так что о более эффективном распределении ресурсов речи не идёт.

  2. В Python для обеспечения стабильности и снижения конфликтов работает специальный шлюз - Global Interpreter Lock (GIL). Он и контролирует потоки.

GIL переключает потоки по умолчанию раз в 5 миллисекунд, так что потоки работают не одновременно, а последовательно, постоянно сменяя друга.

Самая близкая идея работе потоков в Питоне - сеанс одновременной игры в шахматы, когда один шахматист играет с несколькими. Сеанс хоть и называется “одновременным”, но фактически шахматист обходит всех по очереди и делает свой ход в каждой партии.

Для работы с потоками используются модули _thread и threading (более новый).

Рассмотрим простой код для работы с потоками.:

import threading
import time

def thread_function(number):
    print(f"Thread {number}: starting\n")
    time.sleep(2)
    print(f"Thread {number}: finishing\n")

threads = list()

for index in range(3):
    print(f"Create and start thread {index}\n")
    x = threading.Thread(target=thread_function, args=(index,))
    threads.append(x)
    x.start()

for i,thread in enumerate (threads):
    print (f'Join thread {i} \n')
    thread.join()

Передача файлов

Передачу файлов с помощью сокетов можно реализовать по следующей схеме:
  1. Настраиваем сокет, устанавливаем соединение.

  2. На отправляющей стороне:

    • открываем файл на чтение в режиме байтового чтения ('rb').

    • построчно передаём файл принимающей стороне.

  3. На принимающей стороне:

    • открываем файл на запись в режиме байтовой записи ('wb').

    • построчно пишем в файл всё, что получаем от передающей стороны.

  4. Закрываем файлы и соединение.

Передача файлов байтовыми строками позволяет пересылать любые файлы, независимо от их типа, расширения и т.п.

Пример кода для отправки файла с клиента серверу.

Сервер:

import socket
import sys

# создаём сокет и связываем его с IP-адресом и портом

sock = socket.socket()
ip = "localhost"
port = 9999
sock.bind((ip, port))

# сервер ожидает передачи информации
sock.listen(10)

while True:
    # начинаем принимать соединения
    conn, addr = sock.accept()

    # выводим информацию о подключении
    print('connected:', addr)

    # получаем название файла
    name_f = (conn.recv(1024)).decode ('UTF-8')

    # открываем файл в режиме байтовой записи в отдельной папке 'sent'
    f = open('sent/' + name_f,'wb')

    while True:

        # получаем байтовые строки
        l = conn.recv(1024)

        # пишем байтовые строки в файл на сервере
        f.write(l)

        if not l:
            break

    f.close()
    conn.close()

    print('File received')

sock.close()

Клиент:

import socket
import sys

ip = "localhost"
port = 9999

# создаём сокет для подключения
sock = socket.socket()
sock.connect((ip,port))

# запрашиваем имя файла и отправляем серверу
f_name = input ('File to send: ')
sock.send((bytes(f_name, encoding = 'UTF-8')))

# открываем файл в режиме байтового чтения
f = open (f_name, "rb")

# читаем строку
 l = f.read(1024)

while (l):
    # отправляем строку на сервер
    sock.send(l)
    l = f.read(1024)

f.close()
sock.close()

Задания

  1. Используя код из урока, реализуйте возможность отправлять файл с сервера клиенту.

  2. Реализуйте возможность передачи файлов как от клиента серверу, так и от сервера клиенту:

  • должна быть возможность выбрать режим передачи (1 - от клиента серверу, 2 - от сервера клиенту);

  • оформите код с помощью функций;

  • добавьте графический интерфейс с помощью модуля easygui.

  1. Используя потоки напишите клиент-серверный чат, который будет принимать и отправлять сообщения. Для этого потребуется оформить код в функции.