- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- import socket
- host = "localhost"
- port = 44444
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind((host, port))
- s.listen(5)
- sock, addr = s.accept()
- while True:
- buf = sock.recv(1024)
- if buf == "exit":
- sock.send("bye")
- break
- elif buf:
- sock.send(buf)
- sock.close()
Разберем код нашего сервера.
Сначала в строке 4 мы подключаем модуль для работы с сокетами. Он содержит весь необходимый нам функционал.
Далее в строках 6 и 7 мы определим хост, на котором сервер будет ждать соединение, и порт, который он будет слушать.
9 строка создает сокет для Ipv4.
В 10 строке мы устанавливаем опцию повторного использования порта, чтобы не ждать пока он освободится после останова сервера.
Далее в строке 11 мы ассоциируем (биндим) сокет с хостом и портом.
В 12 строке мы указываем количество ожидающих обработки запросов.
В строке 13 функция accept() переводит приложение в режим ожидания подключения клиента. При успешном подключении accept возвратит кортеж (пару) из объекта соединения и адреса клиента. Полученный объект мы и будем использовать для взаимодействия с клиентом.
В строке 14 мы запускаем вечный цикл while, в котором читаем из объекта отправленные данные блоками указанной в 15 строке величины (в данном случае 1024).
В строках с 16 по 20 мы проверяем полученные данные. Если клиент прислал слово exit, то мы отправляем ему bye и выходим из цикла, закрывая соединение. Если же принятые данные не exit, то отправляем их обратно.
Теперь мы напишем клиент к нашему серверу на Python.
Разберем код клиента.
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- import socket
- host = "localhost"
- port = 44444
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host, port))
- while True:
- buf = raw_input(">>")
- s.send(buf)
- result = s.recv(1024)
- print result
- if buf == "exit":
- break
- s.close()
В начале все как у сервера. Создаём сокет, биндим к адресу и порту сервера. Далее в цикле организуем что то вроде чата с сервером.
В 12 строке читаем введенные с клавиатуры данные. Отправляем их серверу, получаем ответ и выводим в консоль.
Далее, если мы отправили серверу команду exit, то выходим из клиента или же переходим к следующей итерации цикла, возвращаясь к приему данных с клавиатуры.
Запустите на разных терминалах клиент с сервером на Python и попробуйте протестировать их взаимодействие.
Сервер получился самый простой. После закрытия соединения клиентом сервер сам закрывается.
Далее мы попробуем его немного усложнить так, чтобы он мог обрабатывать теоретически неограниченное количество подключений одновременно.
Для этого используем многопоточность из модуля threading.
Код многопоточный сервера на Python.
Разберем код многопоточного сервера.
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- import socket
- import threading
- host = "localhost"
- port = 44444
- class Connect(threading.Thread):
- def __init__(self, sock, addr):
- self.sock = sock
- self.addr = addr
- threading.Thread.__init__(self)
- def run (self):
- while True:
- buf = self.sock.recv(1024)
- if buf == "exit":
- self.sock.send("bye")
- break
- elif buf:
- self.sock.send(buf)
- self.sock.close()
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind((host, port))
- s.listen(5)
- while True:
- sock, addr = s.accept()
- Connect(sock, addr).start()
Сначала мы подключили модуль threading.
В 10 строке создаём класс Connect, являющийся наследником класса threading.Thread, в котором описываем взаимодействие с клиентом переопределив родительский метод run(). Именно этот метод создает отдельный поток и выполняет в нём своё содержимое.
Далее после создания и бинда сокета запускаем цикл в котором ожидаем подключение.
При соединении запускаем обработку отдельным потоком, то есть переходим к ожиданию следующего подключения независимо от состояния предыдущего.
Вот собственно и все. Через сокеты между клиентом и сервером можно передавать практически любую информацию, как текст так и байтовый поток. Это позволяет вам создать что угодно: например собственный чат или файловый сервер.
Воспользовавшись модулем pickle можно консервировать и отправлять любые структуры данных: объекты, списки и так далее.
Другой пример кода сервера на Python приведен ниже.
Код сервера.
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Импортируем необходимые библиотеки
import socket
import threading
# Указываем IP адрес и порт, на котором будет сервер
host = "127.0.0.1"
port = 12815
# Создаем класс-потомок threading.Thread в котором
# описываем взаимодействие с клиентом переопределив родительский
# метод run(). Именно этот метод создаёт отдельный поток и выполняет
# в нём своё содержимое.
class Connect(threading.Thread):
def __init__(self, sock, addr):
self.sock = sock
self.addr = addr
threading.Thread.__init__(self)
def run (self):
while True:
buf = self.sock.recv(1024)
# Если клиент запросил справку - выводим её
if buf == 'help':
sock.send('\nThis is help!')
# Если же прислал exit - разрываем соединение
elif buf == 'exit':
sock.send('Bye')
break
# Неопределенная команда вернёт её обратно
elif buf:
self.sock.send('Dont know what is '+buf)
self.sock.close()
# создаем и биндим на порту сокет
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(5)
# В этом цикле ожидаем подключения клиента вне зависимости от состояния
# предыдущих подключений
while True:
sock, addr = s.accept()
Connect(sock, addr).start()
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Импортируем необходимые библиотеки
import socket
# Указываем IP адрес и порт для подключения
host = "127.0.0.1"
port = 12815
# Приветственное сообщение
print 'Welcome to our superserver Client!\n\r'
# Задаем способ подключения и подключаемся к серверу по IP и порту
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
# Циклически "болтаем" с сервером о том и о сем :)
# А если вернее - принимаем команды, пока не будет exit
while True:
# получаем команду от пользователя
buf = raw_input('serv: >> ')
# отсылаем серверу
s.send(buf)
# получаем и выводим ответ
result = s.recv(1024)
print result
# елси команда была exit - разъединяемся
if buf == "exit":
break
s.close()
На Python такой тип программы можно реализовать достаточно просто, используя стандартные заготовки и библиотеки, такие как BaseHTTPServer.
Ниже следует листинг сервера с краткими комментариями.
# -*- coding: utf-8 -*-
import cgi
from os import curdir, sep
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import subprocess
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
try:
if self.path != "/output.exe":
f = open(curdir+sep+"upload.html")
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(f.read())
f.close()
else:
self.send_response(200)
self.send_header("Content-type", "application/octet-stream")
self.end_headers()
self.wfile.write(open(curdir+sep+"output.exe", "rb").read())
except IOError:
self.send_error(404,"File Not Found: %s" % self.path)
def do_POST(self):
try:
ctype, pdict = cgi.parse_header(self.headers.getheader("content-type"))
if ctype == "multipart/form-data":
query = cgi.parse_multipart(self.rfile, pdict)
self.send_response(200)
self.end_headers()
upfile = query.get("file")
f = open(curdir+sep+"output.exe", "wb")
f.write(upfile[0])
f.close()
params = " np output.exe"
p = query.get("encryption")
if p[0] == "aes":
params += " sf 1"
elif p[0] == "rc5":
params += " sf 2"
elif p[0] == "xor":
params += " sf 3"
else:
params += " sf 0"
p = query.get("hw_bind")
if p[0] == "yes":
p = query.get("hw_bind_serial")
assert len(p[0]) == 8
params += " sn " + p[0]
else:
params += " sn 0"
p = query.get("passwd")
assert len(p[0]) > 0
params += " pass " + p[0]
p = query.get("pack")
if p[0] == "yes":
params += " pack 1"
else:
params += " pack 0"
pipe = subprocess.Popen("processor.exe "+params, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pipe.stdin.close()
pipe.wait()
self.wfile.write('<a href="/output.exe">Download results</a>.');
except :
pass
if __name__ == "__main__":
try:
server = HTTPServer(("", 8080), MyHandler)
print "started httpserver..."
server.serve_forever()
except KeyboardInterrupt:
print "^C received, shutting down server"
server.socket.close()
Итак, для начала импортируем из библиотеки BaseHTTPServer два класса: HTTPserver - собственно сам сервер и BaseHTTPRequestHandler - класс, который служит для обработки запросов от пользователей.
Объявляем свой класс MyHandler, который наследуется от BaseHTTPRequestHandler. И перегружаем в нём два метода: do_GET и do_POST для обработки данных, переданных методом GET и POST соответственно.
В первом обработчике проверяем запрос от пользователя (содержится в self.path). Если запрашиваемый документ не является /output.exe, то выдаём форму для загрузки файла, иначе считываем и выдаём output.exe. Так же перед этим посылается ответ от сервера с кодом 200, что соответствует успешной обработке запроса от клиента и передаем заголовок, в котором указан тип содержимого (text/html для html-код или application/octet-stream для бинарного файла).
В обработке POST-запроса проверяем какие данные были переданы. Сначала получаем файл и записываем его под именем output.exe. Далее формируем строку параметров на основе переданных от пользователя данных, запускаем хранящуюся на сервере программу для обработки полученных файлов, ждем пока она завершится и выдаем ссылку на скачивание результата.
Далее в самой программе создаем сервер, указав при этом порт, на котором он будет работать (в примере это 8080) и класс, который необходимо использовать для обработки запросов от пользователей.
Недостаток заключается в том, что данная реализация не сможет обработать одновременную загрузку файлов от разных пользователей, так как. в лучшем случае просто один из запросов затрет файл output.exe, полученный в другом запросе.
Так же приведем код html-файла upload.html, который должен располагаться в той же директории, что и сервер на Python.
<form action="/" enctype="multipart/form-data" method="post">
<table border="1">
<tbody>
<tr>
<td>Encryption:</td>
<td>
<input checked="checked" name="encryption" type="radio" value="none" />None
<input name="encryption" type="radio" value="aes" />AES
<input name="encryption" type="radio" value="rc5" />RC5
<input name="encryption" type="radio" value="xor" />XOR</td>
</tr>
<tr>
<td>Hardware binding:</td>
<td>
<input checked="checked" name="hw_bind" type="radio" value="no" />No
<input name="hw_bind" type="radio" value="yes" />Yes
<input name="hw_bind_serial" value="media serial number" /></td>
</tr>
<tr>
<td>Password:</td>
<td>
<input name="passwd" type="password" /></td>
</tr>
<tr>
<td>Pack:</td>
<td>
<input checked="checked" name="pack" type="radio" value="no" />No
<input name="pack" type="radio" value="yes" />Yes</td>
</tr>
<tr>
<td colspan="2">
<input name="file" type="file" />
<input type="submit" value="Отправить" /></td>
</tr>
</tbody></table>
</form>
Надеемся, что приведенные примеры создания серверов на Python помогут вам в вашей работе.
Комментариев нет:
Отправить комментарий