четверг, 6 декабря 2012 г.

Пишем TCP сервер на Python

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

Python имееет в своем наборе стандартную библиотеку для работы с сокетами подключения.

from socket import *

Название многих методов socket достаточно просты и понятны:
socket.listen() – прослушивать входящие подключения.
socket.accept() – принимать входящие подключения.
socket.recv() – вернуть входящие данные, преобразовав их в строку.
socket.send() – послать данные socket'у клиента*.
socket.close() – закрыть socket.

* данном контексте socket клиента может быть как сокетом браузера, так и сокетом другого сервера. Когда клиент подсоединяется к серверу, то сервер создает сокет для клиента на своей стороне. Два клиента (каждый на своей стороне) могут общаться друг с другом через сокет сервера, который остается открытым для входящих подключений.

Напишем собственный сервер.

Сперва установим сокет нашего сервера.

## Файл server.py
from socket import * # Импортируем библиотеку socket

## Установим несколько констант
HOST = ""  # Хост - это наш сервер
PORT = 29876 # Устанавливаем порт, который никем не занят
ADDR = (HOST,PORT)  # Для хранения адреса сервера мы используем кортеж
BUFSIZE = 4096 # Устанавливаем максимальный размер данных, который может обработать наш сервер

## Теперь создадим новый объект socket (serv)
## Для более подробной информации по socket types/flags посмотрите документацию по Python
serv = socket( AF_INET,SOCK_STREAM)   

## Привяжите наш socket к адресу
serv.bind((ADDR)) # двойные скобки используются для создания кортежа из одного элемента
serv.listen(5) # 5 - это максимальное число подключений в очереди, которые мы разрешаем

Теперь у нас есть сервер, который может прослушивает порт в поисках подключений или по крайней мере делает это до тех пор пока сценарий не достигнет своего конца.

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

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

## Файл client.py
from socket import *

HOST = 'localhost'
PORT = 29876 # Устанавливаем тот же порт, что и у сервера
ADDR = (HOST,PORT)
BUFSIZE = 4096

cli = socket( AF_INET,SOCK_STREAM)
cli.connect((ADDR))

Обратите внимание, что на стороне клиента мы создаем другой сокет-объект, но вместо того, чтобы привязывать его (bind) и прослушивать (listen) мы используем метод connect() для присоединения к серверу.

Так что в итоге получится, если мы запустим наш сервер и наш клиентt? Пока немного. Наш сервер будет прослушивать порт до тех пор пока не завершится весь сценарий. Вместо этого, нам необходимо заставить его ждать до того момента, пока он примет соединение с клиентом и затем сделает что-нибудь с этим соединением. socket.accept() делает то, что нам нужно и возвращает две вещи: новый сокет для  клиента и адрес, привязанный к сокету на другом конце. Как только мы установим эту связь, то сможем отправить данные.

Дополним код сервера в файле server.py

serv = socket( AF_INET,SOCK_STREAM)   

## Привяжите наш socket к адресу
serv.bind((ADDR)) # двойные скобки используются для создания кортежа из одного элемента
serv.listen(5) # 5 - это максимальное число подключений в очереди, которые мы разрешаем
print 'listening...'

conn,addr = serv.accept() # Принимаем соединение с клиентом
print '...connected!'
conn.send('TEST')

conn.close()

На последнем шаге мы дополним код клиентаи в файле client.py, который скажет клиенту ожидать получения данных от сервера.

cli = socket( AF_INET,SOCK_STREAM)
cli.connect((ADDR))

data = cli.recv(BUFSIZE)
print data

cli.close()

Теперь, когда вы запустите сервер он будет ждать до тех пор, пока к нему не присоединится клиент. Как только вы запустите клиент, то он сразу присоединится к серверу и получит от него короткое сообщение (слово "TEST") и выведет его на экран в консоль. Если вы хотите, чтобы клиент послал серверу обратный ответ, то используйте те же методы send() и recv(), но в обратном направлении (к серверу).

Убедитесь, что вы закрыли close() все подключения, после того, как завершили свою работу клиента с сервером. Если вы не закроете подключения, то они останутся висеть до тех пор пока вы не убъете в операционной системе процесс самого интерпретатора Python.

Само по себе использование такого варианта подключения клиента к серверу не очень-то полезно, особенно учитывая то, что мы можем управлять только одним подключением за раз, после которого производится выход из программы. Но, благодаря добавлению всего нескольких циклов while и создания многопоточности мы можем сделать гораздо больше. Это мы сделаем  в другой теме. 

1 комментарий:

  1. Было бы полезно увидить как это дело на джангу прикручивать

    ОтветитьУдалить