Очень простой чат(клиент/сервер) на Python.

В этой статье мы напишем очень простой консольный чат на популярном языке Python. Состоять он будет из двух частей. Первая чаcть это сервер, куда будут приходить сообщения клиентов которые подключены к серверу. Втора чаcть это клиент, которые отправляет сообщения серверу и получает сообщения от сервера.

Постановка задачи.

  • Написать сервер для приема сообщений от клиента и отправки сообщений всем остальным клиентам подключенным к серверу. Будем использовать протокол TCP/IP.
  • Собственно сам клиент. Который коннектится к серверу по TCP/IP. Отправляет и получает сообщения от сервера.
  • Ну и реализуем какое нибудь простое шифрование. Что бы сообщения могли читать только клиенты.

Часть первая. Сервер.

Первым делом нам надо создать сокет, который будет принимать соединения скажем на порту 5050 . Для работы с сокет в Python есть модуль который так и называется socket. Подключим его :

import socket 

Создадим сам сокет:

sock = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)


socket.AF_INET — для сокета используем IPv4 . socket.SOCK_DGRAM — тип сокета. Датаграммный сокет — это сокет, предназначенный для передачи данных в виде отдельных сообщений (датаграмм). По сравнению с потоковым сокетом, обмен данными происходит быстрее, но является ненадёжным: сообщения могут теряться в пути, дублироваться и переупорядочиваться. Датаграммный сокет допускает передачу сообщения нескольким получателям (multicasting) и широковещательную передачу (broadcasting).

Теперь свяжем сокет с адресом(интерфейсом) и портом :

sock.bind (('',5050))

Пустые кавычки значат что сокет слушает все доступные интерфейсы.

Теперь нам надо как то принимать сообщения. Нам совершенно все равно от кого и что получать. Задача получить и отправить остальным известным клиентам. По этому, мы будем использовать функцию socket.recvfrom(bufsize)  которая нам вернет данные и адрес сокета с которого получены эти данные.

data , addres = sock.recvfrom(1024)  # Буфер в байтах

Для отправки данных будем использовать функцию socket.sendto( bytes, address ) :

 sock.sendto(data,addres) 

Итог у нас такой :

import socket
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind (('94.250.252.115',5050))
client = [] # Массив где храним адреса клиентов
print ('Start Server')
while 1 :
         data , addres = sock.recvfrom(1024)
         print (addres[0], addres[1])
         if  addres not in client : 
                 client.append(addres)# Если такого клиента нету , то добавить
         for clients in client :
                 if clients == addres : 
                     continue # Не отправлять данные клиенту, который их прислал
                 sock.sendto(data,clients)

Клиентская часть.

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

import threading

Первым делом создадим функцию которая будет получать сообщения от сервера:

def read_sok():
while 1 :
data = sor.recv(1024)
print(data.decode('utf-8'))

Теперь нам надо создать поток и запустить в нем эту функцию:

potok = threading.Thread(target= read_sok)
potok.start()

Теперь весь код с комментариями :

import socket
import threading
def read_sok():
while 1 :
data = sor.recv(1024)
print(data.decode('utf-8'))
server = '192.168.0.1', 5050 # Данные сервера
alias = input() # Вводим наш псевдоним
sor = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sor.bind(('', 0)) # Задаем сокет как клиент
sor.sendto((alias+' Connect to server').encode('utf-8'), server)# Уведомляем сервер о подключении
potok = threading.Thread(target= read_sok)
potok.start()
while 1 :
mensahe = input()
sor.sendto(('['+alias+']'+mensahe).encode('utf-8'), server)

Шифрование .

У нас очень упрощенный вариант, думаю c шифрованием мудрить не будем. Возьмем самый простой симметричный алгоритм XOR. Основная идея алгоритма состоит в том, что если у нас есть некая величина, есть некий шифровальный ключ (другая величина), то можно зашифровать исходные данные через этот ключ, применив операцию XOR побитно. Т.е. если у нас есть исходная фраза a и ключ k, то x = a ^ k. Теперь, если к шифру x опять применить ключ, то получим исходную фразу, т.е. a = x ^ k .

key = 567  # Ключ шифрования

crypt = ''
for i in  message  :
    crypt += chr(ord(i)^key)
 message  = crypt

Я не рассчитываю на уникальность материала, сам учусь ) Строго не судите. Подсказки и доработки приветствуются.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

37 Replies to “Очень простой чат(клиент/сервер) на Python.

  1. чувак, сделай правки плез, у тебя там есть сразу две неправильных переменных + ты называешь переменные кое-как например вместо Message Mesahne очень много ошибок но код — редактируемый спасибо за идею реализации, скажу как-только правлю код

    1. Спасибо, правки сделал.

      P.S. Если есть еще кому не сложно отправьте опечатки через Ctrl+Enter

    1. Должен вас расстроить, все работает. Скорей всего у вас проблемы с настройкой сети, например сервер за NAT.

  2. Да все работает, запустил я на компьютере где подключен роутер и все прекрасно, я немного улучшил, так же сделал несколько команд на управление компом, (два компа и один телефон) и пишу на телефоне команду определенную и на компе срабатавает, материал хороший

  3. Ребят, при запуске не телефоне выдаёт ошибку:

    sor.sendto((‘[‘+alias+’]’+mensahe).encode(‘utf-8’), server

    ^

    IndentationError: unexpacted indent

    В питоне ещё слаб поэтому не могу ничего иссправить, кто знает как исправить?

    1. IndentationError: неожиданный отступ .. Думаю тут очевидно. Или забыли отступ или лишний отступ и т.д. Это синтактическая ошибка.

  4. File «client.py», line 7

      server = ‘127.0.0.1’, 5050 # Данные сервера

                             ^

    IndentationError: unindent does not match any outer indentation level

    1. Если вы пытаетесь создать сокет то писать надо так : .bind = ((127.0.0.1′, 5050)). Методу bind передается кортеж ( Адрес , Порт)

      1. Прошу прошения я тупанул просто куча пробелов скопировал и даже не проверил

    1. Все верно. SOCK_DGRAM означает что для передачи используется протокол UDP (SOCK_STREAM использует TCP)

  5. Работает только на одном компьютере, на втором «соединение было закрыто удаленным хостром». Я только начинаю изучать работу с сеть. и не понимаю почему эта ошибка, помогите пж(

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

    1. ConnectionResetError: [WinError 10054] Удаленный хост принудительно разорвал существующее подключение.. Да есть такое дело.. Нужно просто в в исключение try отправлять сообщения клиентам.Возможно он уже отключился.

    1. У меня не было проблем с этим. Главное что бы серверная часть не была за NAT или делайте проброс портов (например в роутере)

  7. alias = input() # Вводим наш псевдоним

    в этой строчке в inpu выбивает unindent does not match any outer indentation level

    что делать? подскажите пожалуста

    1. Вроде все написано по английски. Проблемы с отступом, синтаксическая ошибка.

  8. Все запустилось как правильно отправить сообщение в чате?? выдает ошибку если ввожу сразу текст сообщения

  9. Немножко выше задавался вопрос что делать в случае если сервер чата отключается после выхода или переподключения клиента, но я немного не понял как мне прописать это через try

    Скажите, пожалуйста, как заставить сервер оставаться устойчивым через такую конструкцию и после выхода пользователя из чата? Как именно следует обработать и прописать эту строку?

    1. Что бы сервер не падал в ошибку, нужно тот участок кода который может вызвать исключение обернуть в конструкцию try … except . Например оправка сообщения клиенту
      try:
      sock.sendto(data,clients)
      except:
      continue

      1. В моем случае проблема была немного с иной спецификой, поскольку клиент при подключении отправляет на сервер приветственное слово, потому пришлось чуть переработать через continue и else с манипуляцией списка, но спасибо за ваши наводки и труды).

        Ваш материал здорово помог мне в моем собственном проекте мессенджера с графическим интерфейсом, много взял нового и интересного в своем изучении Питона

        1. Спасибо за отзыв. Вы бы молги поделиться своим опытом графического интерфейса в статье на нашем сайте, если конечно есть желание. Все вопросы через форму обратной связи )))

  10. Приветствую! Есть несколько вопросов по чату.
    Серверная и клиентская часть пишутся в одном проекте но в разных файлах?
    Компиляция единым проектом идёт?
    При запуске чата на компе, как происходит подключение к серверу и вообще развёртывание сервера? Допустим есть виртуальный сервер. Нужно в коде дать его IP?
    Спасибо!

    1. Питон интерпретируемый язык, то есть не компилируется. Вы можете просто запустить каждый скрипт отдельно. В клиенте конечно нужно указывать IP сервера, он может вполне быть одинаковый у клиента и сервера.

  11. Почему если не писать ничего, то подключения хватает только на минуту. И все, дальше связь прерывается и если ничего не писать, то ты не будешь видеть сообщения других.

    1. «Почему если не писать ничего, то подключения хватает только на минуту.» Если мы используем TCP, то скорей всего это Timeout. То есть данные не передаються и соединение закрывается самим сокетом что бы не занимать канал.»если ничего не писать, то ты не будешь видеть сообщения других» Тут все логично, серверу больше некому отправлять сообщения, так как такова соединения закрыто… Это очень простой пример и тут есть много недочетов. Вы можете использовать например Sock.settimeout(), а вообще нужно почитать мануал библиотеке.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *