Сетевой (IP)
Маршрутизирует информационные пакеты от узла к узлу.
Транспортный (TCP, UDP и т.п.)
Обеспечивает прозрачную передачу данных между двумя точками соединения.
Сеансовый
Управляет сеансом соединения между участниками сети. Начинает, координирует и завершает соединения.
Представления
Обеспечивает независимость данных от формы их представления путем преобразования форматов. На этом уровне может выполняться прозрачное (с точки зрения вышележащего уровня) шифрование и дешифрование данных.
Приложений (HTTP, FTP, SMTP, NNTP, POP3, IMAP и т.д.)
Поддерживает конкретные сетевые приложения. Протокол зависит от типа сервиса.
Каждый сокет относится к одному из коммуникационных доменов. Модуль socket поддерживает домены UNIX и Internet. Каждый домен подразумевает свое семейство протоколов и адресацию. Данное изложение будет затрагивать только домен Internet, а именно протоколы TCP/IP и UDP/IP, поэтому для указания коммуникационного домена при создании сокета будет указываться константа socket.AF_INET.
В качестве примера следует рассмотреть простейшую клиент–серверную пару. Сервер будет принимать строку и отвечать клиенту. Сетевое устройство иногда называют хостом (host), поэтому будет употребляться этот термин по отношению к компьютеру, на котором работает сетевое приложение.
Сервер:
Листинг
import socket, string
def do_something(x):
lst = map(None, x);
lst.reverse();
return string.join(lst, "")
HOST = "" # localhost
PORT = 33333
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.bind((HOST, PORT))
while 1:
print «Слушаю порт 33333»
srv.listen(1)
sock, addr = srv.accept()
while 1:
pal = sock.recv(1024)
if not pal:
break
print «Получено от %s:%s:" % addr, pal
lap = do_something(pal)
print «Отправлено %s:%s:" % addr, lap
sock.send(lap)
sock.close()
Клиент:
Листинг
import socket
HOST = "" # удаленный компьютер (localhost)
PORT = 33333 # порт на удаленном компьютере
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
sock.send(«ПАЛИНДРОМ»)
result = sock.recv(1024)
sock.close()
print «Получено:", result
Примечание:
В примере использованы русские буквы: необходимо указывать кодировку.
Прежде всего, нужно запустить сервер. Сервер открывает сокет на локальной машине на порту 33333, и адресе 127.0.0.1. После этого он слушает (listen()) порт. Когда на порту появляются данные, принимается (accept()) входящее соединение. Метод accept() возвращает пару — Socket–объект и адрес удаленного компьютера, устанавливающего соединение (пара — IP–адрес, порт на удаленной машине). После этого можно применять методы recv() и send() для общения с клиентом. В recv() задается число байтов в очередной порции. От клиента может прийти и меньшее количество данных.
Код программы–клиента достаточно очевиден. Метод connect() устанавливает соединение с удаленным хостом (в приведенном примере он расположен на той же машине). Данные передаются методом send() и принимаются методом recv() — аналогично тому, что происходит на сервере.
Модуль socket имеет несколько вспомогательных функций. В частности, функции для работы с системой доменных имен (DNS):
Листинг
>>> import socket
>>> socket.gethostbyaddr('www.onego.ru')
('www.onego.ru', [], ['195.161.136.4'])
>>> socket.gethostbyaddr('195.161.136.4')
('www.onego.ru', [], ['195.161.136.4'])
>>> socket.gethostname()
'rnd.onego.ru'
В новых версиях Python появилась такая функция как socket.getservbyname(). Она позволяет преобразовывать наименования Интернет–сервисов в общепринятые номера портов:
Листинг
>>> for srv in 'http', 'ftp', 'imap', 'pop3', 'smtp':
… print socket.getservbyname(srv, 'tcp'), srv
…
80 http
21 ftp
143 imap
110 pop3
25 smtp
Модуль также содержит большое количество констант для указания протоколов, типов сокетов, коммуникационных доменов и т.п. Другие функции модуля socket можно при необходимости изучить по документации.
Модуль smtplib
Сообщения электронной почты в Интернете передаются от клиента к серверу и между серверами в основном по протоколу SMTP (Simple Mail Transfer Protocol, простой протокол передачи почты). Протокол SMTP и ESMTP (расширенный вариант SMTP) описаны в RFC 821 и RFC 1869. Для работы с SMTP в стандартной библиотеке модулей имеется модуль smtplib. Для того чтобы начать SMTP–соединение с сервером электронной почты, необходимо в начале создать объект для управления SMTP–сессией с помощью конструктора класса SMTP:
Листинг
smtplib.SMTP([host[, port]])
Параметры host и port задают адрес и порт SMTP–сервера, через который будет отправляться почта. По умолчанию, port=25. Если host задан, конструктор сам установит соединение, иначе придется отдельно вызывать метод connect(). Экземпляры класса SMTP имеют методы для всех распространенных команд SMTP–протокола, но для отправки почты достаточно вызова конструктора и методов sendmail() и quit():
Листинг
# -*- coding: cp1251 -*-from smtplib import SMTP
fromaddr = «[email protected]» # От кого
toaddr = «[email protected]» # Кому
message = ""«From: Student <%(fromaddr)s>
To: Lecturer <%(toaddr)s>
Subject: From Python course student
MIME–Version: 1.0
Content–Type: text/plain; charset=Windows–1251
Content–Transfer–Encoding: 8bit
Здравствуйте! Я изучаю курс по языку Python и
отправляю письмо его автору.
"""
connect = SMTP('mail.onego.ru')
connect.set_debuglevel(1)
connect.sendmail(fromaddr, toaddr, message % vars())
connect.quit()
Следует заметить, что toaddr в сообщении (в поле To) и при отправке могут не совпадать. Дело в том, что получатель и отправитель в ходе SMTP–сессии передается командами SMTP–протокола. При запуске указанного выше примера на экране появится отладочная информация (ведь уровень отладки задан равным 1):
Листинг
send: 'ehlo rnd.onego.ru\r\n'
reply: '250–mail.onego.ru Hello as3–042.dialup.onego.ru [195.161.147.4], pleased to meet you\r\n'
send: 'mail FROM:<[email protected]> size=270\r\n'
reply: '250 2.1.0 <[email protected]>… Sender ok\r\n'
send: 'rcpt TO:<[email protected]>\r\n'
reply: '250 2.1.5 <[email protected]>… Recipient ok\r\n'
send: 'data\r\n'
reply: '354 Enter mail, end with ".» on a line by itself\r\n'
send: 'From: Student <[email protected]>\r\n … '
reply: '250 2.0.0 iBPFgQ7q028433 Message accepted for delivery\r\n'
send: 'quit\r\n'
reply: '221 2.0.0 mail.onego.ru closing connection\r\n'
Из этой (несколько сокращенной) отладочной информации можно увидеть, что клиент отправляет (send) команды SMTP–серверу (EHLO, MAIL FROM, RCPT TO, DATA, QUIT), а тот выполняет команды и отвечает (reply), возвращая код возврата.
В ходе одной SMTP–сессии можно отправить сразу несколько писем подряд, если не вызывать quit().
В принципе, команды SMTP можно подавать и отдельно: для этого у объекта–соединения есть методы (helo(), ehlo(), expn(), help(), mail(), rcpt(), vrfy(), send(), noop(), data()), соответствующие одноименным командам SMTP–протокола.
Можно задать и произвольную команду SMTP–серверу с помощью метода docmd(). В следующем примере показан простейший сценарий, который могут использовать те, кто время от времени принимает почту на свой сервер по протоколу SMTP от почтового сервера, на котором хранится очередь сообщений для некоторого домена: