Сокеты в Python, чат в 50 строк

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

На очередной практике по Java, не предвещающей ничего необычного, преподаватель ворвался в аудиторию и с порога заявил: "Сегодня мы с вами познакомимся с сокетами и напишем прототип собственного чата".

"А вечер-то перестаёт быть томным" - подумал я и не ошибся. Чёрт возьми, это какая-то магия, вертелось в моей голове по пути домой. Тут надо отметить, что я не только бедный студент, но ещё и преподаватель в кружке программирования, поэтому после столкновения с такой интересной темой во мне затаилось жгучее желание поделиться ею со своими ребятами.

Так и родилась эта статья, которая является крайне упрощенным пособием по использованию веб-сокетов для начинающих изучать программирование.

Для начала определимся, что это за зверь такой - Сокет?

Представим себе работу ресторана быстрого питания, пусть будет Burger Queen, так вот работник этого заведения и будет сокетом, то есть программой, которая отвечает за обмен данными(бургерами) между клиентом и заведением. Как сокет узнает кому отдать заветное хрючево(данные)? У него есть чек с номером заказа! Вот и у сокета есть порт к которому он привязан, то есть и в Burger Queen и в сетевых технологиях есть приложение, тот самый сокет, который отвечает за работу с определенным портом, так же как и работник, который отвечает за обработку конкретного номера заказа, ведь и к конкретному компьютеру и к конкретной забегаловке одновременно конектятся разные клиенты, и всем им нужны разные данные.

Как и от работников сети быстрого питания от сокетов большого набора скиллов не требуется, поэтому мы создадим отдельно серверный сокет, он будет принимать данные от одного клиента и передавать другому и клиентский сокет, он будет отправлять данные на сервер и получать ответ.

Пишем сервер

Фуууух, самая потная часть статьи позади, расчехляем питонов! Первым делом напишем программу серверного сокета, работника Burger Queen, который принимает заказы.

import socket # Подключаем необходимую библиотеку, она встроена


new_socket = socket.socket() #Создаём объект сокета
new_socket.bind(('127.0.0.1', 5000)) # Привязываем наш объект к ip и порту
new_socket.listen(2) # Указываем нашему сокету, что он будет слушать 2 других

print("Server is up now!")

Каюсь, в объяснении сокетов я ни слова не сказал про IP, но тут тоже всё просто, наш работник(сокет) работает в конкретном ресторане по конкретному адресу, то есть, подытоживая можно сказать, в конкретном ресторане по определенному IP адресу работает много работников, сокетов, каждый из которых обслуживает свой порт, номер заказа.

Почему я выбрал 5000-ный порт, потому что методом проб и ошибок только с ним запустилась моя программа, очевидно другие - заняты, а IP 127.0.0.1 - стандартный локальный адрес любого компьютера(совсем любого).

Ползём дальше, получаем коннекты.

conn1, add1 = new_socket.accept() 
# сохраняем объект сокета нашего клиента и его адрес
print("First client is connected!")

conn2, add2 = new_socket.accept()
#аналогично со вторым клиентом
print("Second client is connected!")

Далее создадим две функции, которые принимают данные от одного клиента и отправляют их другому.

def acceptor1():
  # Запустим бесконечный цикл, мы хотим общаться постоянно!
    while True:
#Получим 1024 байта от первого клиента
        a = conn1.recv(1024)
 #Перешлём их второму
        conn2.send(a)

def acceptor2():
# А здесь мы получим 1024 байта от второго клиента и перешлём первому.
    while True:
        b = conn2.recv(1024)
        conn1.send(b)

Вуаля, осталось запустить функции, но вот проблема, они будут выполнятся последовательно(вспомним физику 8-го класса), то есть друг за другом, а нам хотелось бы, что они выполянлись параллельно, для этого в программировании существует концепция так называемых потоков, тут я снова встал на скользкую дорожку, потому что про потоки можно говорить часами и всё равно будут оставаться вопросы(лично у меня так бывает), поэтому здесь, памятуя, что статья для новичков, скажу лишь одно, поток - волшебный класс, который позволяет выполянять указанные функции параллельно.

from threading import Thread # Подключили класс потока

#Создаём потоки, в качестве именнованного аргумента передаем нашу ф-ю
tread1 = Thread(target=acceptor1) 
tread2 = Thread(target=acceptor2)

#Запускаем потоки
tread1.start()
tread2.start()

Здесь вся магия заключается в том, что мы указываем каждому потоку, какую функцию он будет выполнять, а затем стартуем их и отныне наши функции выполянются параллельно.

Вот собственно и все, можно запустить наш сервер и попробовать подключиться к нему из командной строки, с помощью следующей команды:

curl 127.0.0.1:5000

Здесь мы просто говорим системной утилите curl законектится к указанному адресу и порту, при успешном подключении сервер сообшит нам о нём в консоли.

Итак, приведём полный код сервера.

import socket 
from threading import Thread

new_socket = socket.socket()
new_socket.bind(('127.0.0.1', 5000))

new_socket.listen(2)

print("Server is up now!")

conn1, add1 = new_socket.accept()
print("First client is connected!")

conn2, add2 = new_socket.accept()
print("Second client is connected!")

def acceptor1():
    while True:
        a = conn1.recv(1024)
        conn2.send(a)

def acceptor2():
    while True:
        b = conn2.recv(1024)
        conn1.send(b)

tread1 = Thread(target=acceptor1)
tread2 = Thread(target=acceptor2)

tread1.start()
tread2.start()

Пишем клиента

С клиентом, а точнее с клиентами всё куда проще, идеалогически мы всего лишь создаём новый объект сокета и заставляем его отправлять и принимать данные, делает он это конечно же параллельно, смотрим сразу весь код и вникаем.

#Подключаем зависимости
import socket
from threading import Thread
#Создаём новый сокет
client_socket = socket.socket()
#Заставляем его подключиться к серверному сокету
client_socket.connect(("127.0.0.1", 5000))
#Создаём ф-и отправки и получения сообщений
def sender():
    while True:
      #Читаем строку с клавиатуры
        a = input()
        #Отправляем её, предварительно закодировав
        client_socket.send(a.encode("utf-8"))
def reciver():
    while True:
      #Получаем строку от сервера
        b = client_socket.recv(1024)
       #Печатаем, предварительно раскодировав
        print(b.decode("utf-8"))
#Создаём по отдельному потоку для каждой функции
tread1 = Thread(target=sender)
tread2 = Thread(target=reciver)
#Потоки запушены, клиент готов получать и отправлять сообщения
tread1.start()
tread2.start()

Вот и всё! Создаем двух клиентов и переписываемся без мам пап, ватсапов и телеграмов, как говорится, мы и сами с усами.

P. S.

Буду рад любым комментариям, новичкам подскажу, опытных разработчиков выслушаю и приму к сведению.

Источник: https://habr.com/ru/post/596985/


Интересные статьи

Интересные статьи

Привет, Хаброжители! Python — стремительно развивающийся язык программирования современности. В этом увлекательном и необычном руководстве материал разбивается на доступные пошаговые фра...
Умение модели распознавать намерения собеседника, то есть понимать зачем человек совершил то или иное действие, применимо в большом числе прикладных NLP-задач. К примеру, чат-ботам, г...
Что? Строки могут быть «грязными»? Да, могут. //.....Какой-то код console.log(typeof str); // string console.log(str.length); // 15 console.log(str); // zzzzzzzzzzzzzzz Вы думаете, в этом пр...
Тема статьи навеяна результатами наблюдений за методикой создания шаблонов различными разработчиками, чьи проекты попадали мне на поддержку. Порой разобраться в, казалось бы, такой простой сущности ка...
Эта статья посвящена одному из способов сделать в 1с-Битрикс форму в всплывающем окне. Достоинства метода: - можно использовать любые формы 1с-Битрикс, которые выводятся компонентом. Например, добавле...