Как закрыть сокет в си

Обновлено: 07.07.2024

Сокеты (англ. socket — разъём) — название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения.

Принципы сокетов¶

Каждый процесс может создать слушающий сокет (серверный сокет) и привязать его к какому-нибудь порту операционной системы (в UNIX непривилегированные процессы не могут использовать порты меньше 1024). Слушающий процесс обычно находится в цикле ожидания, то есть просыпается при появлении нового соединения. При этом сохраняется возможность проверить наличие соединений на данный момент, установить тайм-аут для операции и т.д.

Каждый сокет имеет свой адрес. ОС семейства UNIX могут поддерживать много типов адресов, но обязательными являются INET-адрес и UNIX-адрес. Если привязать сокет к UNIX-адресу, то будет создан специальный файл (файл сокета) по заданному пути, через который смогут сообщаться любые локальные процессы путём чтения/записи из него (см. Доменный сокет Unix). Сокеты типа INET доступны из сети и требуют выделения номера порта.

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

Основные функции¶

socket()¶

Создаёт конечную точку соединения и возвращает файловый дескриптор. Принимает три аргумента:

domain указывающий семейство протоколов создаваемого сокета

  • AF_INET для сетевого протокола IPv4
  • AF_INET6 для IPv6
  • AF_UNIX для локальных сокетов (используя файл)

type

  • SOCK_STREAM (надёжная потокоориентированная служба (сервис) или потоковый сокет)
  • SOCK_DGRAM (служба датаграмм или датаграммный сокет)
  • SOCK_RAW (Сырой сокет — сырой протокол поверх сетевого уровня).

protocol

Протоколы обозначаются символьными константами с префиксом IPPROTO_* (например, IPPROTO_TCP или IPPROTO_UDP). Допускается значение protocol=0 (протокол не указан), в этом случае используется значение по умолчанию для данного вида соединений.

Функция возвращает −1 в случае ошибки. Иначе, она возвращает целое число, представляющее присвоенный дескриптор.

Пример на Python

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

  1. sockfd — дескриптор, представляющий сокет при привязке
  2. serv_addr — указатель на структуру sockaddr, представляющую адрес, к которому привязываем.
  3. addrlen — поле socklen_t, представляющее длину структуры sockaddr.

Возвращает 0 при успехе и −1 при возникновении ошибки.

Пример на Python

Автоматическое получение имени хоста.

listen()¶

Подготавливает привязываемый сокет к принятию входящих соединений. Данная функция применима только к типам сокетов SOCK_STREAM и SOCK_SEQPACKET. Принимает два аргумента:

  1. sockfd — корректный дескриптор сокета.
  2. backlog — целое число, означающее число установленных соединений, которые могут быть обработаны в любой момент времени. Операционная система обычно ставит его равным максимальному значению.

После принятия соединения оно выводится из очереди. В случае успеха возвращается 0, в случае возникновения ошибки возвращается −1.

Пример на Python

accept()¶

Используется для принятия запроса на установление соединения от удаленного хоста. Принимает следующие аргументы:

  1. sockfd — дескриптор слушающего сокета на принятие соединения.
  2. cliaddr — указатель на структуру sockaddr, для принятия информации об адресе клиента.
  3. addrlen — указатель на socklen_t, определяющее размер структуры, содержащей клиентский адрес и переданной в accept(). Когда accept() возвращает некоторое значение, socklen_t указывает сколько байт структуры cliaddr использовано в данный момент.

Функция возвращает дескриптор сокета, связанный с принятым соединением, или −1 в случае возникновения ошибки.

Пример на Python

connect()¶

Устанавливает соединение с сервером.

Некоторые типы сокетов работают без установления соединения, это в основном касается UDP-сокетов. Для них соединение приобретает особое значение: цель по умолчанию для посылки и получения данных присваивается переданному адресу, позволяя использовать такие функции как send() и recv() на сокетах без установления соединения.

Загруженный сервер может отвергнуть попытку соединения, поэтому в некоторых видах программ необходимо предусмотреть повторные попытки соединения.

Возвращает целое число, представляющее код ошибки: 0 означает успешное выполнение, а −1 свидетельствует об ошибке.

Пример на Python

Передача данных¶

Для передачи данных можно пользоваться стандартными функциями чтения/записи файлов read и write, но есть специальные функции для передачи данных через сокеты:

Отличие Web сокетов от сокетов в Boost Asio
Здравствуйте, Кто нибудь может пожалуйста подсказать, есть ли различие между WebSocket и.


Закрытие дочернего окна вызывает закрытие программы
Здравствуйте! Не могу никак разобраться, как сделать так, чтобы дочернее окно при его закрытии не.

Вызов модальной формы из модальной, нужно:закрытие последней и не закрытие первой.
1. Вызываю модальную форму из главной формы (немодальной). 2. Вызываю другую модальную форму из.

Тестирование сокетов
Сделал клиент и сервер на сокетах. Возник вопрос: как протестировать их в "боевых" условиях.

1. Пожалуйста, используйте теги для вставки кода, в таком виде Ваш код мало читаем.
2. В статье (и "оригинальном" перловом скрипте) речь о udp, но раз у Вас хоть как-то работает. Дайте ссылку на полное описание протокола. Например, схема коннект на url для меня выглядит странно. Да и наличие кип-аливэ запроса как-бы намекает. Вдруг сокет вообще не надо закрывать?
3. select() на запись в listen сокет - жуть, в таком виде Вам select() вообще не нужен.
4. обертка вокруг read() выглядит странно. Например EWOULDBLOCK Вы там никогда не получите, результат меньше 0 ф-я тоже не вернет, ну и читать по байту как-то странно.
5. Не ленитесь, сделайте send() одним куском, будет чуть быстрей.
6. Делайте shutdown()+close() хотя я уже говорил, это выглядит не типично для подобных протоколов.
А тормоза cisco. ну так, она тупо ждет пока Ваш сервер пережует 1 запрос (ведь сейчас как правило, одна html страничка это десятки url), запросы накапливаются и в конце концов, часть запросов отваливается по таймауту. Логично?

Отправка сокетов
Пытаюсь через сокеты просто "открыть" страничку на локальном сайте (запущен через Apache). При.


Перехват сокетов
Набросайте, пожалуйста, программу, которая умеет перехватывать сокеты, направленные от клиента.

Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

Закрывает подключение Socket и освобождает все связанные ресурсы.

Перегрузки

Закрывает подключение Socket и освобождает все связанные ресурсы.

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

Close()

Закрывает подключение Socket и освобождает все связанные ресурсы.

Примеры

В следующем примере кода закрывается Socket .

Комментарии

CloseМетод закрывает подключение к удаленному узлу и освобождает все управляемые и неуправляемые ресурсы, связанные с Socket . После закрытия Connected свойству присваивается значение false .

Для протоколов, ориентированных на соединение, рекомендуется вызывать Shutdown метод перед вызовом Close метода. Это гарантирует, что все данные отправляются и получаются на подключенном сокете до его закрытия.

Если необходимо вызвать Close без первого вызова Shutdown , можно убедиться, что данные, поставленные в очередь для исходящей передачи, будут отправлены, задав DontLinger Socket для параметра значение false и указав интервал времени ожидания, отличный от нуля. Close будет блокироваться до тех пор, пока эти данные не будут отправлены или пока не истечет указанное время ожидания. Если задано DontLinger значение false и задан нулевой интервал времени ожидания, Close освобождает соединение и автоматически удаляет исходящие данные из очереди.

Чтобы присвоить DontLinger параметру сокета значение false , создайте LingerOption , задайте для свойства Enabled значение true , а для LingerTime Свойства — требуемый период времени ожидания. Используйте его LingerOption вместе с DontLinger параметром Socket для вызова SetSocketOption метода.

См. также раздел

Применяется к

Close(Int32)

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

Параметры

Примеры

В следующем примере кода показано, как закрыть Socket .

Комментарии

CloseМетод закрывает подключение к удаленному узлу и освобождает все управляемые и неуправляемые ресурсы, связанные с Socket . После закрытия Connected свойству присваивается значение false .

Для протоколов, ориентированных на соединение, рекомендуется вызывать метод Shutdown перед вызовом метода Close . Это гарантирует, что все данные отправляются и получаются на подключенном сокете до его закрытия.

Если необходимо вызвать Close без первого вызова Shutdown , можно убедиться, что данные, поставленные в очередь для исходящей передачи, будут отправлены, задав DontLinger для параметра значение false и указав интервал времени ожидания, отличный от нуля. Close будет блокироваться до тех пор, пока эти данные не будут отправлены или пока не истечет указанное время ожидания. Если задано DontLinger значение false и задан нулевой интервал времени ожидания, Close освобождает соединение и автоматически удаляет исходящие данные из очереди.

Чтобы присвоить DontLinger параметру сокета значение false , создайте LingerOption , задайте для свойства Enabled значение true и задайте LingerTime для свойства требуемый период ожидания. Используйте его LingerOption вместе с DontLinger параметром Socket для вызова SetSocketOption метода.

Для обеспечения сетевых коммуникаций используются сокеты. Сокет это конечная точка сетевых коммуникаций. Каждый использующийся сокет имеет тип и ассоциированный с ним процесс. Сокеты существуют внутри коммуникационных доменов. Домены это абстракции, которые подразумевают конкретную структуру адресации и множество протоколов, которое определяет различные типы сокетов внутри домена. Примерами коммуникационных доменов могут быть: UNIX домен, Internet домен, и т.д.

В Internet домене сокет - это комбинация IP адреса и номера порта, которая однозначно определяет отдельный сетевой процесс во всей глобальной сети Internet. Два сокета, один для хоста-получателя, другой для хоста-отправителя, определяют соединение для протоколов, ориентированных на установление связи, таких, как TCP.

  • Создание сокета
  • Привязка к локальным именам
  • Установление связи
  • Передача данных
  • Закрывание сокетов
  • Пример функции, для установления WWW коннекции

Создание сокета

Для создания сокета используется системный вызов socket.

Этот вызов основывается на информации о коммуникационном домене и типе сокета. Для использования особенностей Internet, значения параметров должны быть следующими:

  • communication domain - AF_INET (Internet протоколы).
  • type of the socket - SOCK_STREAM; Этот тип обеспечивает последовательный, надежный, ориентированный на установление двусторонней связи поток байтов.

Выше был упомянут сокет с типом stream. Краткое описание других типов сокетов приведено ниже:

  • Datagram socket - поддерживает двусторонний поток данных. Не гарантируется, что этот поток будет последовательным, надежным, и что данные не будут дублироваться. Важной характеристикой данного сокета является то, что границы записи данных предопределены.
  • Raw socket - обеспечивает возможность пользовательского доступа к низлежащим коммуникационным протоколам, поддерживающим сокет-абстракции. Такие сокеты обычно являются датаграм- ориентированными.

Функция socket создает конечную точку для коммуникаций и возвращает файловый дескриптор, ссылающийся на сокет, или -1 в случае ошибки. Данный дескриптор используется в дальнейшем для установления связи.

Для создания сокета типа stream с протоколом TCP, обеспечивающим коммуникационную поддержку, вызов функции socket должен быть следующим:

Привязка к локальным именам

В Internet домене связывание сокета и имени может быть весьма сложным, но, к счастью, обычно нет необходимости специально привязывать адрес и номер порта к сокету, так как функции connect и send автоматически свяжут данный сокет с подходящим адресом, если это не было сделано до их вызова.

Для связывания сокета с адресом и номером порта используют системный вызов bind:

Привязываемое имя (name) это строка байт переменной длины, которая интерпретируется поддерживаемым протоколом. Интерпретация может различаться в различных коммуникационных доменах.

Установление связи

Со стороны клиента связь устанавливается с помощью стандартной функции connect:

которая инициирует установление связи на сокете, используя дескриптор сокета s и информацию из структуры serveraddr, имеющей тип sockaddr_in, которая содержит адрес сервера и номер порта на который надо установить связь. Если сокет не был связан с адресом, connect автоматически вызовет системную функцию bind.

Connect возвращает 0, если вызов прошел успешно. Возвращенная величина -1 указывает на то, что в процессе установления связи произошла некая ошибка. В случае успешного вызова функции процесс может работать с дескриптором сокета, используя функции read и write, и закрывать канал используя функцию close.

Со стороны сервера процесс установления связи сложнее. Когда сервер желает предложить один из своих сервисов, он связывает сокет с общеизвестным адресом, ассоциирующимся с данным сервисом, и пассивно слушает этот сокет. Для этих целей используется системный вызов listen:

где s это дескриптор сокета, а qlength это максимальное количество запросов на установление связи, которые могут стоять в очереди, ожидая обработки сервером; это количество может быть ограничено особенностями системы.

Когда сервер получает запрос от клиента и принимает решение об установлении связи, он создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'. Для Internet домена это означает тот же самый номер порта. Для этой цели используется системный вызов accept:

Сокет, ассоциированный клиентом, и сокет, который был возвращен функцией accept, используются для установления связи между сервером и клиентом.

Передача данных

Вызовы send и recv практически идентичны read и write, за исключением того, что добавляется аргумент флагов.

Могут быть указаны один или более флагов с помощью ненулевых значений, таких, как следующие:

  • MSG_OOB - Посылать/получать данные, характерные для сокетов типа stream.
  • MSG_PEEK - Просматривать данные без чтения. когда указывается в recv, любые присутствующие данные возвращаются пользователю, но сами данные остаются как "непрочитанные". Следующий read или recv вызванный на данном сокете вернет прочитанные в прошлый раз данные.
  • MSG_DONTROUTE - посылать данные без маршрутизации пакетов. (Используется только процессами, управляющими таблицами маршрутизации.)

Закрывание сокетов

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

Если сокет больше не используется, процесс может закрыть его с помощью функции close, вызвав ее с соответствующим дескриптором сокета:

Если данные были ассоциированы с сокетом, обещающим доставку (сокет типа stream), система будет пытаться осуществить передачу этих данных. Тем не менее, по истечении довольно таки длительного промежутка времени, если данные все еще не доставлены, они будут отброшены. Если пользовательский процесс желает прекратить любую передачу данных, он может сделать это с помощью вызова shutdown на данном сокете для его закрытия. Вызов shutdown вызывает "моментальное" отбрасывание всех стоящих в очереди данных. Формат вызова следующий:

Кратко ознакомиться с интерфейсом сокетов можно по следующему ниже описанию и примерам, а такеж по книге А.Робачевского "ОС Unix" с. 420-426, 264-277. Для программистов настоятельно рекомендуется книга Й.Снейдер. "Эффективное программирование TCP/IP. Библиотека программиста" - СПб: "Питер", 2002.

При выполнении задания предполагается интенсивное использование студентом документации man по требуемым функциям (socket, bind, listen, accept, connect, recv, send, fork, gethostbyname, inet_ntoa, bzero, bcopy, htons).

Примеры простых программ

Ниже приведены тексты двух простых программ на языке С - клиента и сервера, - взаимодействующие через протокол TCP, используя интерфейс сокетов. Операционная система - Solaris.

Сервер работает как эхо: каждому подсоединившемуся клиенту он возвращает полученные от него данные, предварив их строкой с именем и IP-адресом клиента. Сервер использует порт 7890 для приема соединений от клиентов.

Клиент, запускаясь, подсоединяется к серверу, отправляет ему строку "Hello", получает отклик и закрывает соединение.

Некоторые особенности и приемы

1. Создание сокета

Для создания сокета используется функция socket(). Аргументы:

Пример создания сокета для работы с протоколом TCP:

2. Структуры sockaddr_in и sockaddr

Структуры sockaddr_in , описывающая сокет в домене AF_INET (TCP/IP), и sockaddr , описывающая сокет вообще, определены следующим образом:

При вызове функций bind(), connect(), accept() и некоторых других, указатель на сокет типа sockaddr_in , содержащий адреса клиента или сервера, приводится к указателю на sockaddr , например, если сокет сервера описан как:

то в вызовах указанных выше функций имеем, например:

3. Манипуляции IP-адресами

IP-адрес (поле sin_addr в sockaddr_in ) является структурой. Если требуется указать нулевой адрес (например, при формировании серверного сокета для того, чтобы соединения принимались со всех адресов), эта структура заполняется нулями с помощью функции bzero():

Для записи в эту структуру некоторого IP-адреса, содержащегося в строке из 4-х октетов, расположенных в "правильном" (сетевом) порядке, вызывается функция bcopy(). Получить такую строку по доменному имени узла сети можно из поля h_addr структуры hostent , указатель на которую возвращает функция gethostbyname():

Для преобразования адреса, содержащегося в структуре типа struct in_addr , в точечно-десятичную нотацию используется функция inet_ntoa(), возвращающая указатель на строку символов, содержащую требуемый адрес в виде " d.d.d.d ":

4. Функция listen()

Функция listen() переводит сокет в состояние пассивного открытия, но собственно прослушивание сети и прием поступающих соединений делается функцией accept(). Если требуется обрабатывать несколько соединений, функция accept() вызывается в цикле (см. также п. 4).

5. Обслуживание нескольких клиентов одновременно

Для того чтобы сервер продолжал слушать сеть после установления очередного соединения и мог принять запрос от другого клиента, распространенным приемом является создание дочернего процесса для обслуживания каждого клиента. Для обработки вновь поступившего соединения порождается дочерний процесс с помощью функции fork(). В этом случае используется следующая схема:

Читайте также: