-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path6
More file actions
115 lines (81 loc) · 13.9 KB
/
6
File metadata and controls
115 lines (81 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
6
TCP-клиент
Давайте напишем первого сетевого клиента, который будет подключаться к серверу miminet.ru на порт 80
(TCP), отправлять HTTP-запрос, получать HTTP-ответ и печатать содержимое ответа в консоль. Схема работы
показана на рисунке ниже.
HTTP (HyperText Transfer Protocol) - протокол для коммуникации с веб-сервером. HTTP-запрос - запрос
отправляемый клиентом на веб-сервер. В ответ на такой запрос веб-сервер отправляет данные
(HTML страницу, JS код, картинки, видео и т.д.) или отправляет информационное сообщение.
По умолчанию, веб-сервер для работы использует порт 80 (TCP).
Нам необходимо выполнить следующие действия:
- создать ТСР сокет
- подключиться к веб-серверу miminet на порт 80
- отправить HTTP-запрос
- получить ответ и вывести его на экран
#!/usr/bin/python
import socket
HOST = 'miminet.ru'
PORT = 80
ip_addr = socket.gethostbyname(HOST)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_addr, PORT))
s.send(b'GET / HTTP/1.0\n\n')
data = s.recv(4096)
print(data)
Вы можете скопировать этот код и попробовать выполнить его у себя. В результате его работы в консоль
должно вывестись следующее сообщение:
ilya@miminet:~$ python3 tcp-client.py
b'HTTP/1.1 200 OK\r\nServer: nginx/1.25.4\r\nDate: Tue, 10 Jun 2025 07:47:59 GMT\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 26422\r\nConnection: close\r\nVary: Cookie\r\n\r\n<!DOCTYPE html>\n<html lang="en">\n<head>
#у iDONi другой ответ дал
b'HTTP/1.1 200 OK\r\nServer: nginx/1.25.4\r\nDate: Sun, 10 Aug 2025 09:37:41 GMT\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 26422\r\nConnection: close\r\nVary: Cookie\r\n\r\n<!DOCTYPE html>\n<html lang="en">\n<head>\n\n <!-- Google Tag Manager -->\n <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({\'gtm.start\':\nnew Date().getTime(),event:\'gtm.js\'});var f=d.getElementsByTagName(s)[0],\nj=d.createElement(s),dl=l!=\'dataLayer\'?\'&l=\'+l:\'\';j.async=true;j.src=\n\'https://www.googletagmanager.com/gtm.js?id=\'+i+dl;f.parentNode.insertBefore(j,f);\n})(window,document,\'script\',\'dataLayer\',\'GTM-KT5XZVF\');</script>\n<!-- End Google Tag Manager -->\n\n\n <link rel="icon" href="/images/favlogo.ico" type="image/x-icon">\n <meta charset="utf-8">\n <meta name="viewport" content="width=device-width, initial-scale=1">\n <meta name="description" content="\xd0\xad\xd0\xbc\xd1\x83\xd0\xbb\xd1\x8f\xd1\x82\xd0\xbe\xd1\x80 \xd0\xba\xd0\xbe\xd0\xbc\xd0\xbf\xd1\x8c\xd1\x8e\xd1\x82\xd0\xb5\xd1\x80\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd0\xb5\xd1\x82\xd0\xb8 \xd0\xb4\xd0\xbb\xd1\x8f \xd0\xbe\xd0\xb1\xd1\x80\xd0\xb0\xd0\xb7\xd0\xbe\xd0\xb2\xd0\xb0\xd1\x82\xd0\xb5\xd0\xbb\xd1\x8c\xd0\xbd\xd1\x8b\xd1\x85 \xd1\x86\xd0\xb5\xd0\xbb\xd0\xb5\xd0\xb9 \xd0\xbd\xd0\xb0 \xd0\xb1\xd0\xb0\xd0\xb7\xd0\xb5 \xd0\x9e\xd0\xa1 Linux">\n <meta name="author" content="\xd0\x98\xd0\xbb\xd1\x8c\xd1\x8f \xd0\x97\xd0\xb5\xd0\xbb\xd0\xb5\xd0\xbd\xd1\x87\xd1\x83\xd0\xba, \xd0\x97\xd0\xb8\xd0\xbd\xd0\xb0\xd0\xb8\xd0\xb4\xd0\xb0 \xd0\xa0\xd0\xbe\xd0\xbc\xd0\xb0\xd0\xbd\xd0\xbe\xd0\xb2\xd0\xb0">\n\n <title>\xd0\xad\xd0\xbc\xd1\x83\xd0\xbb\xd1\x8f\xd1\x82\xd0\xbe\xd1\x80 \xd0\xba\xd0\xbe\xd0\xbc\xd0\xbf\xd1\x8c\xd1\x8e\xd1\x82\xd0\xb5\xd1\x80\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd0\xb5\xd1\x82\xd0\xb8</title>\n\n \n\n <!-- Vendor Styles -->\n <link rel="stylesheet" media="screen" href="/assets/vendor/boxicons/css/boxicons.min.css"/>\n <link rel="stylesheet" media="screen" href="/assets/vendor/nouislider/dist/nouislider.min.css"/>\n\n\n <!-- Main Theme Styles + Bootstrap -->\n <link rel="stylesheet" media="screen" href="/assets/css/theme.min.css">\n <link rel="stylesheet" media="screen" href="/assets/css/style.css">\n\n</head>\n<body>\n<!-- Google Tag Manager (noscript) -->\n<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KT5XZVF"\nheight="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>\n<!-- End Google Tag Manager (noscript) -->\n\n\n<nav class="header navbar navbar-expand-lg position-absolute navbar-sticky">\n <div class="container">\n <a href="/" class="navbar-brand">\n <img src="/images/logo.png" width="170" alt="Miminet">\n </a>\n\n \n \n \n <div class="navbar-nav">\n <li id="courses-nav-item" class="nav-item">\n <a class="nav-link" href="/course">\xd0\xa3\xd1\x87\xd0\xb5\xd0\xb1\xd0\xbd\xd1\x8b\xd0\xb5 \xd0\xba\xd1\x83\xd1\x80\xd1\x81\xd1\x8b</a>\n </li>\n <li id="trainer-nav-item" id="trainer-nav-item" class="nav-item">\n <a class="nav-link" href="/auth/login.html">\xd0\xa2\xd0\xb5\xd1\x81\xd1\x82\xd0\xb8\xd1\x80\xd0\xbe\xd0\xb2\xd0\xb0\xd0\xbd\xd0\xb8\xd0\xb5</a>\n </li>\n <li id="examples-nav-item" class="nav-item">\n <a class="nav-link" href="/examples">\xd0\x9f\xd1\x80\xd0\xb8\xd0\xbc\xd0\xb5\xd1\x80\xd1\x8b \xd1\x81\xd0\xb5\xd1\x82\xd0\xb5\xd0\xb9</a>\n </li>\n <li id="auth-nav-item" class="nav-item">\n <a href="/auth/login.html" class="btn btn-outline-primary">\n <i class="bx fs-5 lh-1 me-1"></i>\xd0\x92\xd1\x85\xd0\xbe\xd0\xb4\n </a>\n </li>\n </div>\n \n \n \n\n </div>\n</nav>\n\n\n\n\n\n\n\n<main>\n <section class="py-5 position-relative overflow-hidden">\n <div class="container my-lg-5">\n <div class="row">\n <div class="col-md-5">\n <div id="index_network_example" class="border border-light" style="height: 300px;cursor: pointer"></div>\n </div>\n <div class="col-md-7">\n <div class="d-flex align-items-center justify-content-center text-center" style="height: 300px;">\n <p class="fs-2">\xd0\xad\xd0\xbc\xd1\x83\xd0\xbb\xd1\x8f\xd1\x82\xd0\xbe\xd1\x80 \xd0\xba\xd0\xbe\xd0\xbc\xd0\xbf\xd1\x8c\xd1\x8e\xd1\x82\xd0\xb5\xd1\x80\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd0\xb5\xd1\x82\xd0\xb8 \xd0\xb4\xd0\xbb\xd1\x8f \xd0\xbe\xd0\xb1\xd1\x80\xd0\xb0\xd0\xb7\xd0\xbe\xd0\xb2\xd0\xb0\xd1\x82\xd0\xb5\xd0\xbb\xd1\x8c\xd0\xbd\xd1\x8b\xd1\x85 \xd1\x86\xd0\xb5\xd0\xbb\xd0\xb5\xd0\xb9 \xd0\xbd\xd0\xb0 \xd0\xb1\xd0\xb0\xd0\xb7\xd0\xb5 \xd0\x9e\xd0\xa1 Linux\n <br>\n <a href="/home" class="btn btn-primary">\xd0\xa1\xd0\xbe\xd0\xb7\xd0\xb4\xd0\xb0\xd1\x82\xd1\x8c \xd1\x81\xd0\xb5\xd1\x82\xd1\x8c</a>\n </p>\n </div>\n\n </div>\n </div>\n </div>\n </section>\n\n <section class="pt-4 pt-lg-0 pb-4 pb-lg-5 bg-secondary">\n\t <d'
На самом деле вывод занял весь экран. Я намеренно его обрезал, так как это не помешает нам разбираться
с сокетами.
Я постарался прокомментировать каждую строку в коде, но давайте подробней разберем, что тут происходит.
#/usr/bin/python
import socket
Импортируем модуль socket. Как раз он нам позволит работать с сокетами.
ip_adder = socket.gethostbyname(HOST)
Для создания ТСР соединения необходимо знать IP-адрес хоста и порт, на который мы хотим подключиться. Пока мы
знаем только имя хоста (miminet.ru) и порт 80. Функция gethostbyname позволяет по DNS имени определить
IP-адрес хоста. Т.е. по имени miminet.ru она вернет IP-адрес хоста. Сохраним его в переменную ip_addr.
DNS (Domain Name System) - система позволяющая по имени хоста узнать его IP-адрес. Каждый раз, когды вы
в браузере вбиваете ya.ru, miminet.ru, yadro.com или любое другое имя, ваш хост, через DNS, узнает IP-адрес хоста.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Данный код создает ТСР сокет и сохраняет его в переменную s. Функция socket позволяет создавать не только
TCP или UDP сокеты. Она принимает два обязательных параметра:
- socket.AF_INET - когда мы хотим открыть сокет из семейства протоколов TCP/IP
- socket.SOCK_STREAM - указывает тип сокета.
Если посмотреть в документацию https://docs.python.org/3/library/socket.html#socket.socket,
то видно, что функция socket принимает 4 параметра, первые два обязательные, вторые два опциональные.
Третьи (proto-опциональный) параметр указывает, какой именно протокол будет использоваться. Так как
в стеке TCP/IP на транспортном уровне работает только один потоковый проток TCP, то явно указывать
это не обязательно. Достаточно указать использование потокового протокола socket.SOCK_STREAM.
И так, сокет создан, IP-адрес и порт известны, можем подключаться.
s.connect((ip_addr, PORT))
Функция connect устанавливает TCP соединения (3-х разовое рукопожатие SYN, SYN+ACK, ACK) на заданный
IP-адрес и порт. Обратите внимание, что функция connect принимает только один порт - это порт назначения.
Т.е. порт, на который мы устанавливаем соединение. У клиентов (тот кто инициирует установку соединения)
порт источника выбирается случайным образом.
s.send(b'GET / HTTP/1.0\n\n')
Функция send отправляет данные в установленное TCP соединение. В данном случае мы отправили HTTP - запрос
на веб-сервер miminet.ru. HTTP - это текстовый протокол, поэтому, запрос можно написать прямым текстом.
Два подряд идущие перевода строки \n\n означают окончание HTTP-запроса. В этом HTTP-запросе мы хотим
получить главную страницу miminet.ru
data = s.recv(4096)
Получаем данные из сокета. Функция recv по умолчанию работает в блокирующем режиме. Это означает,
что вызвав эту функцию программа будет ожидать, пока не придут данные. Так как заранее неизвестно,
сколько данных придет, я указал 4096 байт. Если данных придет меньше, вернут их, если придет больше,
вернут не более 4096 байт. Полученные данные сохраняем в переменную data.
print(data)
Печатаем полученные данные в консоль.
На самом деле можно опустить вызов функции gethostbyname, так как функция connect может
самостоятельно по имени определить IP-адрес. Я решил оставить gethostbyname для наглядности.
Вот так, всего в несколько строк кода можно создать сокет, установить TCP соединение с приложением
на удаленном хосте, отправить туда запрос и прочитать ответ.
Закрытие сокета (close)
Если у вас есть опыт в программировании, то возможно вы зададитесь вопросом - почему мы создали
сокет, установили соединение, но не закрывали его перед завершением прогарммы?
Когда программа завершиться, то ОС будет очищать выделенные программой структуры, в том числе и
сокеты. Обнаружив совет ОС самостоятельно закроет его. Если вы планируете работать только с одним
сокетом, как в нашем примере, то не обязательно в конце вызывать close. С другой стороны, функция
close будет очень полезна, когда мы будем рассматривать примеры с множеством сокетов.