Создание клиент-сервер на Python для зародышей

Kate

Administrator
Команда форума
В данной статье не будет никакой занудной теории по типу "А чТо ТаКоЕ СЕРВЕР???". Мы будем вкратце описывать работу клиент-сервер, а также приведём примеры. Данная статья будет интересна тем, кто не до конца понимает как работает клиент-сервер, кто забивает в череп гвозди, чтобы повысить содержание железа и стать умнее, кто думает что клиент-сервер это что-то заоблачное для обычного начинающего кодера.

Итак, приступим к описанию принципов работы:
  • Сервер, на IP адресе {пусть это будет alpha} по порту {пусть это будет beta} ожидает подключения на указанный IP адрес {пусть это alpha2} с портом {пусть это beta2}. IP машины и IP приёма должны быть одинаковы. Порты имеют значения в диапазоне 0-65535.
  • Далее, клиент хочет зайти на ваш сервер. Изначально, наш сервер это небольшая консоль для доступа .... например к дневнику Иванова Ивана и 5Б. Клиент вводит IP {alpha2} и порт {beta2}, после чего по TCP или UPD происходит обмен данными.
  • Сервер видит что кто-то хочет посмотреть нюдсы Иванова Ивана и просто отсылает их обратно клиенту.
- Вот так мы максимально кратко расписали клиент-сервер. Распишем немного побольше...
- ЭЙ СТОЙ! А чо ещё за TCP и UPD?
- Секундочку.
Что такое UPD и TCP?
Вкратце это технологии передачи данных между двумя устройствами. Они оба разные как лолихантер и милфхантер. Приведём парочку примеров:
- Эй, Санёк, я тут камни нашёл. Можно я в тебя его кину?
- Хорошо, Шанёк, кидай
- Разрешение кидать получено!
*Кинул камни настолько мягко и последовательно, что Санёк успел словить все*
Это был пример работы TCP. Он превосходит UPD в целостности данных, строго последовательным отправлением данных и большей надёжности, но в отличии от него меньшей скоростью.
- Эй, Санёк, лови!
*Кинул камни так сильно, что Санёк сразу дал дёру, успев сначала словить большую часть камней*
- *****, не поймал, в лицо попал
Это был пример работы UPD. В отличии от своего "прилежного" брата он более быстрый в закидывании камня. Но вместо строгой последовательности отправки данных, кидает всё что видит.
Теперь черпанём немного практики.
Для начала сделаем вечно получающий информацию сервер.
Для передачи информации через сокеты в Python используем socket
import socket
Теперь же нам надо сделать слушалку.
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
IP = socket.gethostbyname(socket.gethostname())
PORT = 12333
listener.bind((IP, PORT))
listener.listen(0)

connection, address = listener.accept()
socket.socket() - создаём наш сокет. Тут мы просто создаём коробку определённого вида, цвета, размера (повторяю, это формально). Но без содержимого.
socket.setsockopt() - добавляем в нашу коробку пенопласт для информации. Настраиваем опции коробки: какой пенопласт, размеры и т.д.
IP = socket.gethostbyname(socket.gethostname()) - получаем наш IP. В функцию передаётся имя ПК, IP которого мы хотим получить, в нашем случае нас.
listener.bind((IP, PORT)) - устанавливаем данные для прослушивания данного порта. Берём почтовый ящик IP/PORT. Данные передаются кортежем, это не ошибка!
listener.listen() - разрешаем серверу принимать запросы.
listener.accept() - в случае найденного пользователя разрешаем ему подключиться
Теперь у нас есть почтовый ящик и заранее готовая коробка с посылкой. Теперь нам нужно проверять наш почтовый ящик. Наш почтовый ящик вмещает 1 КБ (1024 байт). Поэтому нам нужно каждый раз открывать ящик, забирать оттуда данные и продолжать до тех пор, пока ящик не опустеет.
connection.send("Привет, подкючайся!".encode('utf8'))

connection.send("Привет!".encode('utf8'))
while True:
data_output = ''
while True:
data = connection.recv(1024).decode("utf8")
data_output+=data
if not data:
break

print(data_output)
connection.send() - отправить данные. Для вашей же живучести используйте .encode("utf8")
connection.recv(1024) - получить 1024 байт данных. Для вашей же живучести используйте .decode("utf8")
Вот так мы будем получать информацию о том, что нам отправили. Это как будто вы перевернули почтовый ящик вверх дном и высыпаете оттуда всё до конца.
- Но зачем в начале нам отправлять данные?
Таким образом мы показываем клиенту что готовы работать. Если бы мы этого не сделали, клиент бы стоял молчал.
Раз уж с сервером окончено перейдём к клиенту
import socket

connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
IP = "айпи сервера"
PORT = 12333
connection.connect((IP, PORT))
rd = connection.recv(1024)
print(rd.decode('utf8'))
connection.send("И тебе привет!".encode('utf8'))
connection.close()
connection.connect() - начать подключение. Данные в функцию передаются кортежем!
connection.close() - закрыть соединение
Ну вот, теперь при запуске клиента по IP, от сервера мы получим "Привет", а сервер получит наше "И тебе привет!", а также продолжит ждать от нас ответ.
Прошу акцентировать внимание, что эти каналы легко прослушать, а по сему для передачи личных данных желательно пользоваться алгоритмами шифрования (например идеально подойдёт RSA).
Немного дополнительных вопросов:
  • Можно ли при помощи сокетов сделать консоль? Как реализовать в нём команды?
  • Конечно. Команды можно реализовать просто отправкой сообщения, а после получения обработкой через условия.
  • Как сделать таймаут?
  • Используйте на вашем подключении .settimeout(Время в секундах).
  • Что если я укажу чужой адрес в прослушивании сервера?
  • Вам просто выбьет ошибку.
  • Можно ли по сокетам передавать фото, видео и т.д.?
  • Да, конечно. Фото и видео это просто данные. Их можно прочесть, а соответственно и отправить.
Небольшая справочка по основным командам для создания сокетов:
socket.socket(socket.AF_INET, socket.SOCK_STREAM) - создать сокет
socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - настроить сокет
socket.gethostbyname(socket.gethostname()) - получить наш готовый IP
listener.bind((IP, PORT)) - прослушивать данный IP по порту. Данные передаются кортежем!
listener.listen(0) - разрешаем серверу принимать запросы
listener.accept() - принимаем запрос на передачу данных
connection.send() - отправить данные
connection.recv(1024) - получить данные. Значение в скобочках, количество байт которые можно принять за один раз
connection.settimeout() - установить таймаут соединения
connection.connect() - установить подключение к серверу
connection.close() - закрыть соединение
Дополнительные источники, полезные ссылки:
И на этом мы закончим вступление клиент-сервер в Python для зародышей. Напомню что данная статья не создана для Truehard кодеров, она создана для тех кто не разбирается в сокетах и хочет понять как с ними работать.

 
Сверху