Обработчики запросов
Обработчики запросов выполняют большую часть работы по получению входящих запросов и принятию решения о том, какие действия предпринять. Обработчик отвечает за реализацию протокола поверх уровня сокетов (например, HTTP, XML-RPC или AMQP). Обработчик запросов считывает запрос из входящего канала данных, обрабатывает его и записывает ответ обратно. Доступны три метода переопределения.
- : подготавливает обработчик запроса к запросу. В метод создает файловые объекты для чтения и записи в сокет.
- : выполняет реальную работу по запросу. Разберите входящий запрос, обработайте данные и отправьте ответ.
- : очищает все, что было создано во время .
Многие обработчики могут быть реализованы только с помощью метода .
Непрерывная Связь
Наш текущий сервер блокируется до тех пор, пока клиент не подключится к нему, а затем снова блокируется для прослушивания сообщения от клиента, после одного сообщения он закрывает соединение, потому что мы не занимались непрерывностью.
Таким образом, это полезно только в запросах ping, но представьте, что мы хотели бы внедрить сервер чата, и, безусловно, потребуется непрерывная обратная связь между сервером и клиентом.
Нам придется создать цикл while, чтобы постоянно наблюдать за входящим потоком сервера для входящих сообщений.
Давайте создадим новый сервер под названием EchoServer.java чья единственная цель-эхо-отклик на любые сообщения, которые он получает от клиентов:
public class EchoServer { public void start(int port) { serverSocket = new ServerSocket(port); clientSocket = serverSocket.accept(); out = new PrintWriter(clientSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { if (".".equals(inputLine)) { out.println("good bye"); break; } out.println(inputLine); } }
Обратите внимание, что мы добавили условие завершения, при котором цикл while завершается, когда мы получаем символ точки. Мы запустим Echo Server , используя метод main так же, как мы это сделали для GreetServer
На этот раз мы запускаем его на другом порту, таком как 4444 чтобы избежать путаницы
Мы запустим Echo Server , используя метод main так же, как мы это сделали для GreetServer . На этот раз мы запускаем его на другом порту, таком как 4444 чтобы избежать путаницы.
Клиент Echo похож на Отличный клиент , поэтому мы можем дублировать код. Мы разделяем их для ясности.
В другом тестовом классе мы создадим тест, чтобы показать, что несколько запросов к EchoServer будут обслуживаться без закрытия сокета сервером. Это верно до тех пор, пока мы отправляем запросы от одного и того же клиента.
Работа с несколькими клиентами-это другой случай, который мы рассмотрим в следующем разделе.
Давайте создадим метод setup для инициирования соединения с сервером:
@Before public void setup() { client = new EchoClient(); client.startConnection("127.0.0.1", 4444); }
Мы также создадим метод tearDown для освобождения всех наших ресурсов, это лучшая практика для каждого случая, когда мы используем сетевые ресурсы:
@After public void tearDown() { client.stopConnection(); }
Затем давайте протестируем наш эхо – сервер с помощью нескольких запросов:
@Test public void givenClient_whenServerEchosMessage_thenCorrect() { String resp1 = client.sendMessage("hello"); String resp2 = client.sendMessage("world"); String resp3 = client.sendMessage("!"); String resp4 = client.sendMessage("."); assertEquals("hello", resp1); assertEquals("world", resp2); assertEquals("!", resp3); assertEquals("good bye", resp4); }
Это улучшение по сравнению с первоначальным примером, когда мы общались только один раз, прежде чем сервер закрыл наше соединение; теперь мы посылаем сигнал завершения, чтобы сообщить серверу, когда мы закончим сеанс .
Методы класса ServerSocket
Ниже приведены некоторые из распространенных методов в Java класса ServerSocket.
№ | Методы и описание |
1 | public int getLocalPort()Возвращает порт, который прослушивает сокет сервера. Этот метод полезен, если вы передали 0 в качестве номера порта в конструкторе и позволили серверу найти порт. |
2 | public Socket accept() throws IOExceptionОжидает входящего клиента. Этот метод блокируется до тех пор, пока клиент не подключится к серверу на указанном порту или не истечет время ожидания сокета, при условии, что значение времени ожидания было установлено с помощью метода setSoTimeout(). В противном случае этот метод блокируется на неопределенный срок. |
3 | public void setSoTimeout(int timeout)Устанавливает значение времени ожидания клиента сокетом сервера во время accept(). |
4 | public void bind (хост SocketAddress, int backlog)Привязывает сокет к указанному серверу и порту в объекте SocketAddress. Используйте этот метод, если вы создали ServerSocket с помощью конструктора без аргументов. |
Когда ServerSocket вызывает accept(), метод не возвращается, пока клиент не подключится. После того, как клиент все-таки подключится, ServerSocket создает новый сокет для неуказанного порта и возвращает ссылку на этот новый сокет. Теперь между клиентом и сервером существует TCP-соединение, и связь может установиться.
SOCK_STREAM vs SOCK_DGRAM¶
См.также
- UDP
- TCP
Потоковый (SOCK_STREAM) | Дейтаграммный (SOCK_DGRAM) |
---|---|
Устанавливает соединение | Нет |
Гарантирует доставку данных | Нет в случае UDP |
Гарантирует порядок доставки пакетов | Нет в случае UDP |
Гарантирует целостность пакетов | Тоже |
Разбивает сообщение на пакеты | Нет |
Контролирует поток данных | Нет |
TCP гарантирует доставку пакетов, их очередность, автоматически разбивает
данные на пакеты и контролирует их передачу, в отличии от UDP.
Но при этом TCP работает медленнее за счет повторной передачи потерянных
пакетов и большему количеству выполняемых операций над пакетами. Поэтому
там где требуется гарантированная доставка (Веб-браузер, telnet, почтовый клиент) используется TCP, если же требуется передавать данные в реальном
времени (многопользовательские игры, видео, звук) используют UDP.
Решение TypeError: объект ‘int’ не является подписываемым
Мы сделаем такую же программу печати данных о рождении, принимая входные данные от пользователя. В этой программе мы преобразовали дату рождения в целое число, поэтому мы не могли выполнять такие операции, как индексация и нарезка.
Чтобы решить эту проблему сейчас, мы удалим оператор int() из нашего кода и запустим тот же код.
#remove int() from the input() ("what is your birth date?") print(" birth_date:",birth_date) print("birth_month:",birth_month) print("birth_year:",birth_year)
Выход:
Объяснение:
Здесь мы только что взяли входные данные в строку, просто удалив int(), и теперь мы можем сделать индексацию и href=”https://docs.python.org/2.3/whatsnew/section-slices.html”>нарезать в нем легко, так как он стал списком, который можно подписывать, так что никакой ошибки не возникает. href=”https://docs.python.org/2.3/whatsnew/section-slices.html”>нарезать в нем легко, так как он стал списком, который можно подписывать, так что никакой ошибки не возникает.
Повседневный Пример Того, Как может Произойти typeerror: ‘int’ объект не является подписываемым
Давайте возьмем простой и повседневный пример вашей даты рождения, записанной в дате, месяце и году. Мы напишем программу, которая возьмет ввод пользователя и распечатает дату, месяц и год отдельно.
#Our program begins from here (input("what is your birth date?")) print(" birth_date:",birth_date) print("birth_month:",birth_month) print("birth_year:",birth_year)
Выход:
Объяснение:
Здесь, во-первых, мы взяли программу для печати даты рождения отдельно с помощью индексации. Во-вторых, мы взяли целочисленные входные данные даты рождения в виде даты, месяца и года. В-третьих, мы разделили дату, месяц и год с помощью индексации, а после этого печатаем их отдельно, но получаем вывод ad TypeError: объект ‘int’ не поддается подписке. Как мы изучали выше, объект integer не является подписываемым.
How to use
Import and configure SocketIoModule
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io'; const config: SocketIoConfig = { url: 'http://localhost:8988', options: {} }; @NgModule({ declarations: AppComponent, imports: BrowserModule, SocketIoModule.forRoot(config), providers: , bootstrap: AppComponent, }) export class AppModule {}
Now we pass the configuration to the static method of
Using your socket Instance
The provides now a configured service that can be injected anywhere inside the .
import { Injectable } from '@angular/core'; import { Socket } from 'ngx-socket-io'; import { map } from 'rxjs/operators'; @Injectable() export class ChatService { constructor(private socket: Socket) {} sendMessage(msg: string) { this.socket.emit('message', msg); } getMessage() { return this.socket.fromEvent('message').pipe(map((data) => data.msg)); } }
Using multiple sockets with different end points
In this case we do not configure the directly using . What we have to do is: extend the service, and call with the object type (passing & if any).
import { Injectable, NgModule } from '@angular/core'; import { Socket } from 'ngx-socket-io'; @Injectable() export class SocketOne extends Socket { constructor() { super({ url: 'http://url_one:portOne', options: {} }); } } @Injectable() export class SocketTwo extends Socket { constructor() { super({ url: 'http://url_two:portTwo', options: {} }); } } @NgModule({ declarations: //components , imports: SocketIoModule, //... , providers: SocketOne, SocketTwo, bootstrap: /** AppComponent **/ , }) export class AppModule {}
Now you can inject , in any other services and / or components.
Отправка данных через UDP
UDP — это протокол без установления соединения. Сообщения другим процессам или компьютерам отправляются без установления какого-либо соединения. Там нет автоматического подтверждения, если ваше сообщение было получено. UDP обычно используется в приложениях, чувствительных к задержке, или в приложениях, отправляющих широковещательные сообщения в сети.
Следующий код отправляет сообщение процессу, прослушивающему порт localhost 6667 с использованием UDP
Обратите внимание , что нет никакой необходимости «закрыть» сокет после отправки, поскольку UDP является установление соединения
socket()¶
См.также
- http://unixhelp.ed.ac.uk/CGI/man-cgi?socket+2
Создаёт конечную точку соединения и возвращает файловый дескриптор.
Принимает три аргумента:
-
domain указывающий семейство протоколов создаваемого сокета
- AF_INET для сетевого протокола IPv4
- AF_INET6 для IPv6
- AF_UNIX для локальных сокетов (используя файл)
-
type
-
SOCK_STREAM (надёжная потокоориентированная служба (сервис) или
потоковый сокет) -
SOCK_DGRAM (служба датаграмм или датаграммный
сокет) - SOCK_RAW (Сырой сокет — сырой протокол поверх сетевого уровня).
-
SOCK_STREAM (надёжная потокоориентированная служба (сервис) или
-
protocol
Протоколы обозначаются символьными константами с префиксом IPPROTO_*
(например, IPPROTO_TCP или IPPROTO_UDP). Допускается значение
protocol=0 (протокол не указан), в этом случае используется значение по
умолчанию для данного вида соединений.
Примечание
Функция возвращает −1 в случае ошибки. Иначе, она возвращает целое число,
представляющее присвоенный дескриптор.
Пример на Си
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> void error(const char *msg) { perror(msg); exit(); } int main(int argc, char *argv[]) { int sockfd, portno, n; struct sockaddr_in serv_addr; struct hostent *server; char buffer256]; if (argc < 3) { fprintf(stderr,"usage %s hostname port\n", argv]); exit(); } // Задаем номер порта portno = atoi(argv2]); // Создаем сокет sockfd = socket(AF_INET, SOCK_STREAM, ); if (sockfd < ) error("ERROR opening socket"); // Конвертирует имя хоста в IP адрес server = gethostbyname(argv1]); if (server == NULL) { fprintf(stderr,"ERROR, no such host\n"); exit(); } // Указываем тип сокета Интернет bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; // Указаваем адрес IP сокета bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); // Указываем порт сокета serv_addr.sin_port = htons(portno); // Устанавливаем соединение if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < ) error("ERROR connecting"); // Вводим сообщение из консоли printf("Please enter the message: "); bzero(buffer, 256); fgets(buffer, 255, stdin); // Отправляем данные n = write(sockfd, buffer, strlen(buffer)); if (n < ) error("ERROR writing to socket"); // Сбрасываем буфер bzero(buffer, 256); // Читаем ответ n = read(sockfd, buffer, 255); if (n < ) error("ERROR reading from socket"); printf("%s\n", buffer); close(sockfd); return ; }
Пример на Python
Использование сокетов на практике
Сокеты Unix обычно используются в качестве альтернативы сетевым TCP-соединениям, когда процессы выполняются на одном компьютере. Данные обычно по-прежнему отправляются по тем же протоколам; но поскольку они просто остаются на той же машине, в том же домене (отсюда и название сокеты домена UNIX), поэтому им никогда не нужно беспокоить петлевой (loopback) сетевой интерфейс для подключения к самому себе.
Самым ярким примером этого является Redis, чрезвычайно быстрое хранилище значений ключей, которое полностью работает в памяти. Redis часто используется на том же сервере, который обращается к нему, поэтому обычно можно использовать сокеты. На таких низких уровнях и с учётом того, насколько быстр Redis, сокеты обеспечивают повышение производительности на 25% в некоторых синтетических тестах.
Если вы подключаетесь к базе данных MySQL, вы также можете использовать сокет. Обычно вы подключаетесь к host:port из удалённой системы, но если вы подключаетесь к базе данных на том же сервере (например, REST API обращается к базе данных), вы можете использовать сокеты для ускорения. Это не повлияет на нормальное использование, но очень заметно при нагрузке, более 20% на 24 ядрах высокого класса со 128 одновременными пользователями и миллионом запросов в секунду. Увидите ли вы выгоду от сокетов при таких условиях — это совсем другое дело, но на этом этапе, вероятно, всё равно придётся заняться репликацией и балансировкой нагрузки.
Если вы хотите работать с сокетами вручную, вы можете использовать утилиту socat, чтобы открыть их через сетевые порты:
socat TCP-LISTEN:12345 UNIX-CONNECT:/var/lib/socket.sock
Это технически противоречит назначению сокетов домена Unix, но может использоваться для отладки на транспортном уровне.
Но почему именно UDP?
Скорость — основной приоритет для потоковой передачи в реальном времени. Когда футбольный матч транслируется в режиме реального времени, люди, которые смотрят его по телевидению, должны получать обновленную информацию о событиях сразу же, как только они происходят на игровой площадке.
Неспособность поставлять контент в режиме реального времени может критически сказаться на росте поставщика потоковой передачи и его позиции на рынке.
Кроме того, нет никакой пользы в повторной трансляции точных данных, даже если контент был поврежден на пути к конечному пользователю, просто потому, что они больше не являются “контентом в реальном времени”. По этим причинам при взаимодействии в реальном времени UDP предпочтительнее TCP.
Примечание: если вы хотите получить представление о том, что такое сокеты и как работает коммуникация TCP-сокетов, пожалуйста, ознакомьтесь с этой статьей.
- socket() — прежде всего сокет определяется как для сервера, так и для клиента. Это не обязательно должно происходить одновременно. Чтобы объяснение было исчерпывающим, я буду на каждом этапе обсуждать действия как сервера, так и клиента.
- bind() — сокету, который определен, присваивается идентификатор и порт на работающей машине. Для клиентского сокета это необязательно, потому что даже если клиентский сокет не привязан, привязка осуществляется автоматически всякий раз, когда клиент инициирует подключение к серверу.
- recvfrom() — после привязки к порту компьютера серверный сокет ожидает подключения от клиентского сокета. Тем временем дальнейшее выполнение текущего потока останавливается (блокируется) до тех пор, пока серверный сокет не получит соединение. То же самое происходит и с клиентским сокетом при ожидании ответа сервера.
- sendto() — после соединения с клиентом серверный сокет отправляет данные клиенту. Этот же метод используется клиентским сокетом для выполнения запроса на подключение к серверу.
- close() — после успешного обмена данными оба сокета закрываются, т.е. освобождаются ресурсы системы, выделенные для сокетов.
Отправка данных через TCP
Отправка данных через Интернет возможна с помощью нескольких модулей. Модуль сокетов обеспечивает низкоуровневый доступ к операциям базовой операционной системы, отвечающим за отправку или получение данных с других компьютеров или процессов.
Следующий код посылает строку байт на сервер TCP на порту 6667 на хосте локального хоста и закрывает соединение после завершения:
По умолчанию вывод сокетов блокируется, это означает, что программа будет ожидать подключения и отправлять вызовы, пока действие не будет «завершено». Для подключения это означает, что сервер фактически принимает соединение. Для отправки это означает только то, что в операционной системе достаточно буферного пространства, чтобы поставить в очередь данные для последующей отправки.
Розетки всегда должны быть закрыты после использования.
Сокеты
С протоколами разобрались. Но как этими протоколами пользоваться нашим клиентам и сервером? Это взаимодействие осуществляется с помощью сокета. Сокет — абстрактный объект, представляющий конечную точку соединения. . С сокетом в Питоне можно работать, как с файлом — считывать его и получать данные. Сокет содержит в себе два параметра: IP-адрес и порт.
Сервер, принимая соединение присваивает своему сокету определенный порт. Порт — число в заголовках пакетов TCP, UDP, указывающее, для какого приложения в системе предназначен данный IP-пакет. Использовать порты с номерами 0-1023 нельзя — они зарезервированы под служебные сетевые протоколы (например, 21 — FTP, 80 — HTTP и т.д.). Клиент, отправляя данные тоже должен создать свой сокет. Два сокета с обоих сторон создают виртуальное соединение по которому будет идти передача данных. Нужно отметить, что при работе с протоколом TCP, создается два сокета: один из них — слушающий (listen). Он переходит в режим ожидания и активизируется при появлении нового соединения. При этом можно проверять актуальные активные соединения, установить периодичность операции. Второй — сокет для обмена данных с клиентом (accept). Это два разных сокета, не путайте
Что такое сокет и как он создается?
# создал INET, STREAM сокет sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # подключился к веб-серверу по обычному https-порту sock.connect((`docs-python.ru`, 443))
Когда устанавливается соединение , то сокет можно использовать для отправки текста страницы. Тот же сокет прочитает ответ, а затем будет уничтожен. Клиентские сокеты обычно используются только для разового обмена данными или небольшого набора последовательных обменов данными.
То, что происходит на веб-сервере, немного сложнее. Сначала веб-сервер создает серверный сокет:
# создает INET, STREAM сокет serv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # связывает сокет с общедоступным хостом и хорошо известным портом serv_sock.bind((socket.gethostname(), 443)) # и начинает ждать подключений serv_sock.listen(5)
Следует отметить пару вещей: в коде выше использовалась функция , чтобы сокет был виден внешнему миру. Если использовать вызов или , то серверный сокет был бы виден только локальной машине. Вызов указывает, что сокет доступен по любому адресу.
Второе замечание: порты с небольшим числом (обычно до 3-х цифр) зарезервированы для «хорошо известных» служб `(HTTPS, SNMP и т. д.), по этому, в качестве номера порта сокета всегда необходимо использовать числа их 4-х цифр, например 8000.
Наконец, аргумент 5 вызова , говорит модулю , чтобы сервер поставил в очередь до 5 клиентов на подключение (нормальный максимум), прежде чем отклонять остальные запросы. Если остальная часть кода написана правильно, то этого должно быть достаточно.
Теперь, когда есть серверный сокет, прослушивающий 443 порт, можно войти в основной цикл веб-сервера:
while True # принимаем подключение (clientsocket, address) = serv_sock.accept() # здесь что-то делают с `serv_sock`... # сделаем вид, что это потоковый сервер client = client_thread(client_sock) client.run()
Существует 3 основных способа, которыми этот цикл может работать:
- диспетчеризация потока для работы с клиентским сокетом,
- создание нового процесса для работы с клиентским сокетом,
- реструктуризация всего приложения для использования неблокирующих сокетов и мультиплексирование между серверным сокетом и любыми активными клиентскими сокетами используя модуль .
Подробнее об этом позже
Сейчас важно понять, что это все, что делает серверный сокет. Он не отправляет и не получает никаких данных
Он просто воспроизводит/создает клиентские сокеты. Каждый клиентский сокет создается в ответ на то, что какой-то новый клиентский сокет выполняет подключение к серверу на определенный хост и порт. На этот запрос, сервер создает новый клиентский сокет, и как только он это сделает то сразу возвращается к прослушиванию следующих подключений. Два клиента могут свободно общаться, например на каком-нибудь динамически выделенном порту, который будет закрыт после общения.
Межпроцессорное взаимодействие (IPC).
Если необходим быстрый IPC между двумя процессами на одной машине, то следует изучить каналы или общую память (объекты и ). Если все же решите использовать сокеты, то необходимо привязать серверный сокет к . На большинстве платформ это позволит сократить несколько уровней сетевого кода и будет работать немного быстрее.
Протокол
Сетевое программирование — это та его часть, которая подразумевает обмен данными между сервером и клиентом. При этом клиент и сервер — это не обязательно разные физические сервера, компьютеры, гаджеты. Клиент-серверная архитектура может быть реализована логически, на одном «железе», в одной комнате, за одним столом. Для взаимодействия между ними используется некоторый свод правил по транспортировке, инкапсуляции, очередности пакетов и сетевого взаимодействия — это называется протоколом передачи данных. Приложения, написанные на Python обычно используют протоколы транспортного уровня TCP и UDP.
TCP используют, чтобы формировать между компьютерами двусторонний канал обмена данными. Благодаря TCP пакеты гарантированно доставляются с соблюдением порядка их очередности, с автоматическим разбиением данных на пакеты и контролем их передачи. В то же время TCP работает медленно, так как потерянные пакеты многократно повторно отправляются, а операций, выполняемых над пакетами, слишком много.
Протокол UDP — низкоуровневый. С его помощью компьютеры могут отправлять и получать информацию в виде отдельных пакетов, не создавая логическое соединение. В отличие от TCP, взаимодействия по протоколу UDP не отличаются надежностью. Это усложняет управление ими в приложениях, в которых при обмене информацией нужны гарантии. Поэтому большинство интернет-приложений используют TCP.
Принципы сокетов¶
Каждый процесс может создать слушающий сокет (серверный сокет) и привязать его
к какому-нибудь порту операционной системы (в UNIX непривилегированные
процессы не могут использовать порты меньше 1024). Слушающий процесс обычно
находится в цикле ожидания, то есть просыпается при появлении нового
соединения. При этом сохраняется возможность проверить наличие соединений на
данный момент, установить тайм-аут для операции и т.д.
Каждый сокет имеет свой адрес. ОС семейства UNIX могут поддерживать много
типов адресов, но обязательными являются INET-адрес и UNIX-адрес. Если
привязать сокет к UNIX-адресу, то будет создан специальный файл (файл сокета)
по заданному пути, через который смогут сообщаться любые локальные процессы
путём чтения/записи из него (см. Доменный сокет Unix). Сокеты типа INET
доступны из сети и требуют выделения номера порта.
1.3 Подробное описание параметров
(1) SOL_SOCKET: Общий параметр сокета.
(2) IPPROTO_IP: параметр IP.
(3) IPPROTO_TCP: опция TCP.
1.3.2 optname указывает метод управления (название опции)
(1) SOL_SOCKET
Название опции |
Описание |
тип данных |
SO_BROADCAST |
Разрешить отправку широковещательных данных |
int |
SO_DEBUG |
Разрешить отладку |
int |
SO_DONTROUTE |
Не найти маршрут |
int |
SO_ERROR |
Получить ошибку сокета |
int |
SO_KEEPALIVE |
Оставайтесь на связи |
int |
SO_LINGER |
Задержка закрытия соединения |
struct linger |
SO_OOBINLINE |
Внеполосные данные в нормальный поток данных |
int |
SO_RCVBUF |
Размер буфера приема |
int |
SO_SNDBUF |
Размер буфера отправки |
int |
SO_RCVLOWAT |
Нижний предел буфера приема |
int |
SO_SNDLOWAT |
Нижний предел буфера отправки |
int |
SO_RCVTIMEO |
Время ожидания приема |
struct timeval |
SO_SNDTIMEO |
Тайм-аут отправки |
struct timeval |
SO_REUSERADDR |
Разрешить повторное использование локального адреса и порта |
int |
SO_TYPE |
Получить тип сокета |
int |
SO_BSDCOMPAT |
Совместим с системой BSD |
int |
(2) IPPROTO_IP
Название опции |
Описание |
тип данных |
IP_HDRINCL |
Включить заголовок IP в пакет |
int |
IP_OPTINOS |
Параметры заголовка IP |
int |
IP_TOS |
Тип Обслуживания |
int |
IP_TTL |
Время выживания |
int |
(3) IPPRO_TCP
Название опции |
Описание |
тип данных |
TCP_MAXSEG |
Максимальный размер сегмента данных TCP |
int |
TCP_NODELAY |
Не используйте алгоритм Нагла |
int |
Каждый сокет SO_RCVBUF и SO_SNDBUF имеет буфер отправки и буфер приема.Используйте эти две опции сокета, чтобы изменить размер буфера по умолчанию.
нота:
При установке размера приемного буфера TCP-сокета очень важен порядок вызовов функций, потому что параметр размера окна TCP обменивается с другой стороной с помощью SYN при установлении соединения. Для клиентов параметр O_RCVBUF должен быть установлен перед подключением; для серверов параметр SO_RCVBUF должен быть установлен перед прослушиванием.
2 использование setsockopt
(1) Закройте (обычно он не будет закрыт сразу, но должен пройти процесс TIME_WAIT), а затем захотите продолжить повторное использование сокета.
(2) Если сокет, который уже находится в подключенном состоянии, принудительно закрывается после вызова close, он не пройдет процесс TIME_WAIT.
(3) В процессе send (), recv (), иногда из-за сетевых условий и других причин, отправка и получение не могут выполняться должным образом, и устанавливается ограничение по времени отправки и получения.
(4) В send () возвращается фактически отправленный байт (синхронно) или байт, отправленный в буфер сокета (асинхронно); состояние системы по умолчанию составляет 8688 байтов (примерно 8.5K); В реальном процессе объем отправленных и полученных данных относительно велик, и можно установить буферы сокетов, что позволяет избежать непрерывного цикла отправки и получения send () и recv ().
(5) Если вы не хотите, чтобы копия из системного буфера в буфер сокета не влияла на производительность программы при отправке данных.
(6) Как и выше, завершите указанную выше функцию в recv () (по умолчанию содержимое буфера сокета копируется в системный буфер).
(7) Обычно при отправке дейтаграмм UDP есть надежда, что данные, отправленные сокетом, имеют широковещательные характеристики.
(8) В процессе подключения клиента к серверу, если сокет в неблокирующем режиме находится в процессе connect (), задержка connect () может быть установлена до вызова accpet () (эта функция устанавливается только в неблокирующем процессе. Это имеет значительный эффект и мало влияет на блокировку вызовов функций).
(9) Если close () вызывается в процессе отправки данных (send () не был завершен и данные не были отправлены), обычная мера, которую мы предприняли ранее, — это «спокойно закрыть» завершение работы (s, SD_BOTH), но данные Его необходимо потерять. Настройте программу в соответствии с требованиями конкретных приложений (то есть закройте сокет после отправки незавершенных данных).
Сервер сокетов
Мы сохраним программу сервера сокетов, как socket_server.py. Чтобы использовать соединение, нам нужно импортировать модуль сокета.
Затем последовательно нам нужно выполнить некоторую задачу, чтобы установить соединение между сервером и клиентом.
Мы можем получить адрес хоста с помощью функции socket.gethostname(). Рекомендуется использовать адрес порта пользователя выше 1024, поскольку номер порта меньше 1024 зарезервирован для стандартного интернет-протокола.
Смотрите приведенный ниже пример кода сервера:
import socket def server_program(): # get the hostname host = socket.gethostname() port = 5000 # initiate port no above 1024 server_socket = socket.socket() # get instance # look closely. The bind() function takes tuple as argument server_socket.bind((host, port)) # bind host address and port together # configure how many client the server can listen simultaneously server_socket.listen(2) conn, address = server_socket.accept() # accept new connection print("Connection from: " + str(address)) while True: # receive data stream. it won't accept data packet greater than 1024 bytes data = conn.recv(1024).decode() if not data: # if data is not received break break print("from connected user: " + str(data)) data = input(' -> ') conn.send(data.encode()) # send data to the client conn.close() # close the connection if __name__ == '__main__': server_program()
Итак, наш сервер сокетов работает на порту 5000 и будет ждать запроса клиента. Если вы хотите, чтобы сервер не завершал работу при закрытии клиентского соединения, просто удалите условие if и оператор break. Цикл while используется для бесконечного запуска серверной программы и ожидания клиентского запроса.
1.2 Использование
(1) Функциональный прототип
(2) Анализ параметров
параметр |
Описание |
sock |
Сокет, который нужно установить или получить параметры. |
level |
Уровень протокола, на котором находится опция. |
optname |
Имя опции, к которой нужно получить доступ. |
optval |
Для getsockopt () указывает на буфер, который возвращает значение параметра. Для setsockopt () указывает на буфер, содержащий новое значение параметра. |
optlen |
Для getsockopt () в качестве входного параметра максимальная длина значения параметра. Для setsockopt () в качестве параметра выхода фактическая длина значения параметра. |
(3) Описание возврата
При успешном выполнении возвращается 0. Возвращает -1 в случае неудачи, для errno устанавливается одно из следующих значений
ошибка |
Описание |
EBADF |
sock не является допустимым дескриптором файла. |
EFAULT |
Память, на которую указывает optval, не является допустимым пространством процесса. |
EINVAL |
При вызове setsockopt () optlen недействителен. |
ENOPROTOOPT |
Указанный уровень протокола не распознает эту опцию. |
ENOTSOCK |
Sock не описывает розетки. |
Как работают сокеты?
TCP-сокет устанавливает связь между клиентом и сервером в несколько этапов.
Блок-схема коммуникации TCP-сокета
- Socket() — на сервере создается конечная точка для коммуникации.
- Bind() — сокету присваивается уникальный номер и для него резервируется уникальная комбинации IP-адреса и порта.
- Listen() — после создания сокета сервер ожидает подключения клиента.
- Accept() — сервер получает запрос на подключение от клиентского сокета.
- Connect() — клиент и сервер соединены друг с другом.
- Send()/Recieve() — обмен данными между клиентом и сервером.
- Close() — после обмена данными сервер и клиент разрывают соединение.
На каждой из перечисленных выше стадий коммуникации сокетов “под капотом» происходит много всего сложного. Однако этих знаний вполне достаточно для понимания и демонстрации того, как работает коммуникация посредством TCP-сокетов.
К настоящему времени мы уже достаточно знаем о TCP-сокетах. Давайте теперь посмотрим на них в действии.