Используя сокеты и потоки сокета
Эта статья объясняет, как работать с сокетами и потоками сокета на различных уровнях от POSIX до Основы.
На почти каждом уровне сетей программное обеспечение может быть разделено на две категории: клиенты (программы, соединяющиеся с другими приложениями) и службы (программы, которые другие приложения подключают с). На высоком уровне эти строки ясны. Большинство программ записанный использующий высокоуровневый APIs является просто клиентами. На более низком уровне, однако, строки являются часто расплывчатыми.
Сокет и поток, программирующий обычно, попадают в одну из следующих широких категорий:
Основанная на пакете коммуникация — Программы, воздействующие на один пакет за один раз, прислушиваясь к входящим пакетам, затем отправляя пакеты в ответ.
С основанной на пакете коммуникацией единственными различиями между клиентами и серверами является содержание пакетов, которые каждая программа отправляет и получает, и (по-видимому) что каждая программа делает с данными. Сам сетевой код идентичен.
Клиенты на основе потоков — Программы, использующие TCP, чтобы отправить и получить данные как два непрерывных потока байтов, один в каждом направлении.
С коммуникацией на основе потоков клиенты и серверы несколько более отличны. Фактическая часть обработки данных клиентов и серверов подобна, но способ, которым программа первоначально создает канал передачи, очень отличается.
Эта глава разделена на разделы на основе вышеупомянутых задач:
Выбор API Family — Описывает, как решить который семья API использовать при записи объединяющий код в сеть.
Запись Основанного на TCP Клиента — Описывает, как сделать исходящие соединения TCP к существующим серверам и службам.
Запись Основанного на TCP Сервера — Описывает, как прислушаться к входящим соединениям TCP при записи серверов и служб.
Работа с Основанными на пакете Сокетами — Описывает, как работать с протоколами не-TCP, такими как UDP.
Выбор семьи API
API, который Вы выбираете для основанных на сокете соединений, зависит от того, делаете ли Вы соединение с другим узлом или получаете соединение от другого узла. Это также зависит от того, используете ли Вы TCP или некоторый другой протокол. Вот несколько факторов для рассмотрения:
В OS X, если у Вас уже есть сетевой код, совместно использующийся с платформами не-Apple, можно использовать POSIX C сетевой APIs и продолжать использовать сетевой код как есть (на отдельном потоке). Если Ваша программа основывается на Базовой Основе или Какао (Основа) выполненный цикл, можно также использовать Базовую Основу
CFStream
API для интеграции сетевого кода POSIX в полную архитектуру на основном потоке. Также при использовании Grand Central Dispatch (GCD) можно добавить сокет как источник отгрузки.В iOS обескураживают сетям POSIX, потому что они не активируют сотовую радиосвязь или по требованию VPN. Таким образом, как правило, необходимо разделить сетевой код от любых общих данных, обрабатывающих функциональность, и переписать сетевой код с помощью высокоуровневого APIs.
Для демонов и служб, слушающих на порту, или для несоединений TCP, POSIX использования или Базовой Основы (
CFSocket
) C сетевой APIs.Для клиентского кода в Objective C используйте Objective C Основы, объединяющий APIS В СЕТЬ. Основа определяет высокоуровневые классы для управления соединениями URL, потоками сокета, сетевыми службами и другими сетевыми задачами. Это - также основная платформа Objective C неUI в OS X и iOS, обеспечивая подпрограммы для выполненных циклов, строковой обработки, объектов коллекции, доступа к файлу, и т.д.
Для клиентского кода в C используйте Базовую Основу C сетевой APIs. Базовая платформа Основы и платформа CFNetwork являются двумя из основных платформ языка C в OS X и iOS. Вместе они определяют функции и структуры, на которых Основа создаются сетевые классы.
Запись основанного на TCP клиента
Путем Вы делаете исходящее соединение, зависит, на каком языке программирования Вы используете, на типе соединения (TCP, UDP, и т.д), и на том, пытаетесь ли Вы совместно использовать код с другим (неMac, не-iOS) платформы.
Использовать
NSStream
для исходящих соединений в Objective C.Если Вы соединяетесь с определенным узлом, создаете a
CFHost
объект (нетNSHost
— они не бесплатные соединенный мостом), затем используйтеCFStreamCreatePairWithSocketToHost
илиCFStreamCreatePairWithSocketToCFHost
открыть сокет соединились с тем узлом и портом и связать паруCFStream
объекты с ним. Можно тогда бросить их кNSStream
объект.Можно также использовать
CFStreamCreatePairWithSocketToNetService
функция с aCFNetServiceRef
возразите для соединения со службой Bonjour. Считайте Обнаружение и Распространение Сетевых служб в Сетевом Обзоре для получения дополнительной информации.Использовать
CFStream
для исходящих соединений в C.Если Вы пишете код, который не может включать Objective C, использовать
CFStream
API. Это интегрирует более легко с другой Базовой Основой APIs, чемCFSocket
, и включает сотовые аппаратные средства на iOS (где применимо), в отличие от APIs низшего уровня. Можно использоватьCFStreamCreatePairWithSocketToHost
илиCFStreamCreatePairWithSocketToCFHost
открыть сокет соединились с данным узлом и портом и связать паруCFStream
объекты с ним.Можно также использовать
CFStreamCreatePairWithSocketToNetService
функционируйте для соединения со службой Bonjour. Считайте Обнаружение и Распространение Сетевых служб в Сетевом Обзоре для получения дополнительной информации.Используйте вызовы POSIX, если требуется межплатформенная мобильность.
Если Вы пишете сетевой код, работающий исключительно в OS X и iOS, необходимо обычно избегать сетевых вызовов POSIX, потому что они более тверды работать с, чем высокоуровневый APIs. Однако, если Вы пишете сетевой код, который должен быть совместно использован с другими платформами, можно использовать POSIX, объединяющий APIS В СЕТЬ так, чтобы можно было использовать тот же код везде.
Никогда не используйте синхронный POSIX, объединяющий APIS В СЕТЬ на основном потоке приложения GUI. При использовании синхронных сетевых вызовов в приложении GUI необходимо сделать так на отдельном потоке.
Подразделы ниже описывают использование NSStream
. Кроме, где отмечено, CFStream
API имеет функции с аналогичными именами и ведет себя так же.
Для узнавания больше о POSIX снабжают API сокетом, читают Сокет UNIX FAQ в http://developerweb .net/.
Установление соединения
Как правило рекомендуемый способ установить соединение TCP к удаленному узлу с потоками. Потоки автоматически обрабатывают многие проблемы то настоящее соединений TCP. Например, потоки предоставляют возможность для соединения именем хоста, и в iOS, они автоматически активируют сотовый модем устройства или по требованию VPN при необходимости (в отличие от этого CFSocket
или сокеты BSD). Потоки являются также более подобным Какао сетевым интерфейсом, чем протоколы нижнего уровня, ведущие себя в пути, который в основном совместим с потоком файла Какао APIs.
Путем Вы получаете потоки ввода и вывода для узла, зависит от того, использовали ли Вы открытие службы для обнаружения узла:
Если Вы уже знаете имя DNS или IP-адрес удаленного узла, получаете Базовое чтение Основы (ввод) и запись (вывод) потоки с
CFStreamCreatePairWithSocketToHost
функция. Можно тогда использовать в своих интересах бесплатный мост междуCFStream
иNSStream
бросать ВашCFReadStreamRef
иCFWriteStreamRef
объекты кNSInputStream
иNSOutputStream
объекты.Если Вы обнаружили узел путем просмотра для сетевых служб с a
CFNetServiceBrowser
объект, Вы получаете потоки ввода и вывода для службы сCFStreamCreatePairWithSocketToNetService
функция. Считайте Обнаружение и Распространение Сетевых служб в Сетевом Обзоре для получения дополнительной информации.
Если Вы не используете автоматический подсчет ссылок, после получения потоков ввода и вывода необходимо сразу сохранить их. Тогда бросьте их к NSInputStream
и NSOutputStream
объекты, набор их объекты делегата (который должен соответствовать NSStreamDelegate
протокол), запланируйте их на текущий цикл выполнения и вызовите их open
методы.
Обработка событий
Когда stream:handleEvent:
к методу обращаются NSOutputStream
делегат объекта и значение streamEvent параметра NSStreamEventHasSpaceAvailable
, вызвать write:maxLength:
отправить данные. Этот метод возвращает число записанных байтов или отрицательное число на ошибке. Если бы меньше байтов было записано, чем Вы попытались отправить, то необходимо стоять в очереди остающиеся данные и отправить их после того, как метод делегата вызывают снова с NSStreamEventHasSpaceAvailable
событие. Если ошибка происходит, необходимо вызвать streamError
узнать, что пошло не так, как надо.
Когда stream:handleEvent:
к методу обращаются Ваш NSInputStream
делегат объекта и значение streamEvent параметра NSStreamEventHasBytesAvailable
, Ваш входной поток получил данные, которые можно считать с read:maxLength:
метод. Этот метод возвращает число чтения байтов или отрицательное число на ошибке.
Если бы меньше байтов было считано, чем Вам нужно, то необходимо поставить данные в очередь и ожидать, пока Вы не получаете другое потоковое событие с дополнительными данными. Если ошибка происходит, необходимо вызвать streamError
узнать, что пошло не так, как надо.
Если другой конец соединения закрывает соединение:
Ваш делегат соединения
stream:handleEvent:
с методом вызываютstreamEvent
набор кNSStreamEventHasBytesAvailable
. Когда Вы читаете из того потока, Вы получаете длину нуля (0
).Ваш делегат соединения
stream:handleEvent:
с методом вызываютstreamEvent
набор кNSStreamEventEndEncountered
.
Когда любое из этих двух событий имеет место, метод делегата ответственен за обнаружение условия конца файла и очистку.
Закрытие соединения
Для закрытия соединения не запланируйте его от цикла выполнения, установите делегата соединения в nil
(делегат не сохраняется), близко оба из связанных потоков с close
метод, и затем выпускает сами потоки (если Вы не используете ARC), или установите их в nil
(если Вы). По умолчанию это закрывает базовое сокетное соединение. Существует две ситуации, в которых необходимо закрыть его сами, однако:
Если Вы ранее устанавливаете
kCFStreamPropertyShouldCloseNativeSocket
кkCFBooleanFalse
путем вызоваsetProperty:forKey:
на потоке.Если Вы создали потоки на основе существующего сокета BSD путем вызова
CFStreamCreatePairWithSocket
.По умолчанию потоки, создаваемые из существующего собственного сокета, не закрывают свой базовый сокет. Однако можно включить автоматическое закрытие путем установки
kCFStreamPropertyShouldCloseNativeSocket
кkCFBooleanTrue
сsetProperty:forKey:
метод.
Для получения дополнительной информации
Для узнавания больше считайте Установку Потоков Сокета в Потоковом Руководстве по программированию, Используя NSStreams Для соединения TCP Без NSHost, или см. проекты примера кода SimpleNetworkStreams и RemoteCurrency.
Запись основанного на TCP сервера
Как упомянуто ранее, сервер и клиент подобны, как только установлено соединение. Основное различие - то, что клиенты делают исходящие соединения, тогда как серверы создают сокет слушания (иногда слушают сокет) — сокет, прислушивающийся к входящим соединениям — тогда принимают соединения на том сокете. После этого каждое получающееся соединение ведет себя точно так же, как соединение Вы могли бы сделать в клиенте.
API, который необходимо выбрать для сервера, зависит прежде всего от того, пытаетесь ли Вы совместно использовать код с другим (неMac, не-iOS) платформы. Существует только два APIs, предоставляющий возможность для прислушиваний к входящим сетевым соединениям: Базовая Основа снабжает API сокетом, и POSIX (BSD) снабжает API сокетом. Высокоуровневый APIs не может использоваться для принятия входящих соединений.
Если Вы пишете код для OS X и iOS исключительно, используйте сетевые вызовы POSIX для установки сетевых сокетов. Затем используйте GCD или
CFSocket
интегрировать сокеты в Ваш цикл выполнения.Используйте чистый сетевой код POSIX с основанным на POSIX циклом выполнения (
select
) если требуется межплатформенная мобильность с платформами не-Apple.Если Вы пишете сетевой код, работающий исключительно в OS X и iOS, необходимо обычно избегать сетевых вызовов POSIX, потому что они более тверды работать с, чем высокоуровневый APIs. Однако, если Вы пишете сетевой код, который должен быть совместно использован с другими платформами, можно использовать POSIX, объединяющий APIS В СЕТЬ так, чтобы можно было использовать тот же код везде.
Никогда не используйте
NSSocketPort
илиNSFileHandle
для общей связи с сокетом. Для получения дополнительной информации посмотрите, Не Используют NSSocketPort (OS X) или NSFileHandle для Общей Связи с сокетом в Сетевом Обзоре.
Следующие разделы описывают, как использовать этот APIs для прислушиваний к входящим соединениям.
Слушание с базовой основой
Для использования Базовой Основы APIs для прислушиваний к входящим соединениям необходимо сделать следующее:
Добавьте надлежащий, включает:
#include <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
Создайте объекты сокета (возвратился как a
CFSocketRef
объект) сCFSocketCreate
илиCFSocketCreateWithNative
функция. УказатьkCFSocketAcceptCallBack
какcallBackTypes
значение параметра. Обеспечьте указатель на aCFSocketCallBack
функция обратного вызова как значение параметра выноски.CFSocketRef myipv4cfsock = CFSocketCreate(
kCFAllocatorDefault,
PF_INET,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAcceptCallBack, handleConnect, NULL);
CFSocketRef myipv6cfsock = CFSocketCreate(
kCFAllocatorDefault,
PF_INET6,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAcceptCallBack, handleConnect, NULL);
Свяжите сокет с
CFSocketSetAddress
функция. Обеспечьте aCFData
объект, содержащий asockaddr
структура, указывающая информацию о требуемом порте и семье.struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET; /* Address family */
sin.sin_port = htons(0); /* Or a specific port */
sin.sin_addr.s_addr= INADDR_ANY;
CFDataRef sincfd = CFDataCreate(
kCFAllocatorDefault,
(UInt8 *)&sin,
sizeof(sin));
CFSocketSetAddress(myipv4cfsock, sincfd);
CFRelease(sincfd);
struct sockaddr_in6 sin6;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(sin6);
sin6.sin6_family = AF_INET6; /* Address family */
sin6.sin6_port = htons(0); /* Or a specific port */
sin6.sin6_addr = in6addr_any;
CFDataRef sin6cfd = CFDataCreate(
kCFAllocatorDefault,
(UInt8 *)&sin6,
sizeof(sin6));
CFSocketSetAddress(myipv6cfsock, sin6cfd);
CFRelease(sin6cfd);
Начните слушать на сокете путем добавления сокета к циклу выполнения.
Создайте источник цикла выполнения для сокета с
CFSocketCreateRunLoopSource
функция. Затем добавьте сокет к циклу выполнения путем обеспечения его источника цикла выполнения дляCFRunLoopAddSource
функция.CFRunLoopSourceRef socketsource = CFSocketCreateRunLoopSource(
kCFAllocatorDefault,
myipv4cfsock,
0);
CFRunLoopAddSource(
CFRunLoopGetCurrent(),
socketsource,
kCFRunLoopDefaultMode);
CFRunLoopSourceRef socketsource6 = CFSocketCreateRunLoopSource(
kCFAllocatorDefault,
myipv6cfsock,
0);
CFRunLoopAddSource(
CFRunLoopGetCurrent(),
socketsource6,
kCFRunLoopDefaultMode);
После этого можно получить доступ к базовому дескриптору сокета BSD с CFSocketGetNative
функция.
Когда Вы заканчиваете с сокетом, необходимо закрыть его путем вызова CFSocketInvalidate
.
В функции обратного вызова Вашего слушающего сокета (handleConnect
в этом случае), необходимо проверить, чтобы удостовериться, что значение callbackType параметра kCFSocketAcceptCallBack
, что означает, что было принято новое соединение. В этом случае параметр данных обратного вызова является указателем на a CFSocketNativeHandle
значение (целочисленное число сокета) представление сокета.
Для обработки новых входящих соединений можно использовать CFStream
, NSStream
, или CFSocket
APIs. APIs На основе потоков строго рекомендуется.
Сделать это:
Создайте чтение и потоки записи для сокета с
CFStreamCreatePairWithSocket
функция.Бросьте потоки к
NSInputStream
возразите иNSOutputStream
возразите, работаете ли Вы в Какао.Используйте потоки, как описано в письменной форме Основанный на TCP Клиент.
Для получения дополнительной информации см. Ссылку CFSocket. Для примера кода см. проекты примера кода RemoteCurrency и WiTap.
Слушание с POSIX снабжает APIS СОКЕТОМ
Сети POSIX довольно подобны CFSocket
API, за исключением того, что необходимо записать собственный код обработки цикла выполнения.
Вот основные шаги для создания сервера уровня POSIX:
Создайте сокет путем вызова
socket
. Например:int ipv4_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
int ipv6_socket = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
Свяжите его с портом.
При учитывании определенного порта в виду используйте это.
Если Вы не имеете определенного порта в виду, нуля передачи для номера порта, и операционная система присвоит Вас эфемерный порт. (Если Вы собираетесь распространить свою службу с Добрый день, необходимо почти всегда использовать эфемерный порт.)
Например:
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET; // or AF_INET6 (address family)
sin.sin_port = htons(0);
sin.sin_addr.s_addr= INADDR_ANY;
if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
// Handle the error.
}
При использовании эфемерного порта вызвать
getsockname
узнать, какой порт Вы используете. Можно тогда зарегистрировать этот порт в Добрый день. Например:socklen_t len = sizeof(sin);
if (getsockname(listen_sock, (struct sockaddr *)&sin, &len) < 0) {
// Handle error here
}
// You can now get the port number with ntohs(sin.sin_port).
Вызвать
listen
начать прислушиваться к входящим соединениям на том порту.
Следующие шаги зависят от того, намереваетесь ли Вы использовать чистый код сокета POSIX или высокоуровневую абстракцию.
Обработка событий с базовой основой
Вызвать CFSocketCreateWithNative
. Тогда следуйте за направлениями в Слушании с Базовой Основой, начинающейся на шаге 3.
Обработка событий с центральной отгрузкой
GCD позволяет Вам выполнять операции асинхронно и обеспечивает механизм очереди событий для определения, когда считать данные из сокета. После создания сокета слушания должен основанный на GCD сервер:
Вызвать
dispatch_source_create
создать источник отгрузки для сокета слушания, указанияDISPATCH_SOURCE_TYPE_READ
как исходный тип.Вызвать
dispatch_source_set_event_handler
(илиdispatch_source_set_event_handler_f
иdispatch_set_context
) установить обработчик, который вызывают каждый раз, когда новое соединение поступает в сокет.Когда слушать обработчик сокета вызывают (на новое соединение), он должен:
Вызвать
accept
. Эта функция заполняет новоеsockaddr
структура с информацией о соединении и возвратах новый сокет для того соединения.При желании вызовите
ntohl(my_sockaddr_obj.sin_addr.s_addr)
определить IP-адрес клиента.Вызвать
dispatch_source_create
создать источник отгрузки для клиентского сокета, указываяDISPATCH_SOURCE_TYPE_READ
как исходный тип.Вызвать
setsockopt
установитьSO_NOSIGPIPE
флаг на сокете.Вызвать
dispatch_source_set_event_handler
(илиdispatch_source_set_event_handler_f
иdispatch_set_context
) установить обработчик, который вызывают каждый раз, когда изменяется состояние соединения.
В клиентском обработчике сокета вызвать
dispatch_async
илиdispatch_async_f
и передайте вызывающий блокread
на сокете для захвата любых новых данных затем обработайте те данные соответственно. Этот блок может также отправить ответы путем вызоваwrite
на сокете.
Обработка событий с чистым кодом POSIX
Создайте набор дескриптора файла и добавьте новые сокеты к тому набору, поскольку входят новые соединения.
fd_set incoming_connections;
memset(&incoming_connections, 0, sizeof(incoming_connections));
Если необходимо периодически выполнять действия с сетевым потоком, создайте a
timeval
структура дляselect
тайм-аут.struct timeval tv;
tv.tv_sec = 1; /* 1 second timeout */
tv.tv_usec = 0; /* no microseconds. */
Важно выбрать тайм-аут, который разумен. Короткие значения тайм-аута срывают систему, заставляя Ваш процесс работать более часто, чем необходимо. Если Вы не делаете что-то очень необычное, Ваш
select
цикл не должен будить больше, чем несколько раз в секунду, самое большее, и на iOS, необходимо попытаться избежать делать это вообще. Для альтернатив читайте, Избегают Сокетов POSIX и CFSocket на iOS, Где Возможно в Сетевом Обзоре.Если Вы не должны выполнять периодические действия, передачу
NULL
.Вызвать
select
в цикле, передавая две отдельных копии того набора дескриптора файла (создаваемый путем вызоваFD_COPY
) для чтения и наборов дескриптора записи.select
системный вызов изменяет эти наборы дескриптора, очищая любые дескрипторы, которые не готовы к чтению или записи.Для
timeout
параметр, передайтеtimeval
структура Вы создали ранее. Несмотря на то, что OS X и iOS не изменяют эту структуру, некоторые другие операционные системы заменяют это значение остающимся количеством времени. Таким образом, для межплатформенной совместимости, необходимо сбросить это значение каждый раз, когда Вы вызываетеselect
.Для
nfds
параметр, передайте число, которое является один выше, чем фактически использующийся дескриптор файла с самым высоким номером.Считайте данные из сокетов, вызвав
FD_ISSET
определить, имеет ли данный сокет незаконченные данные.Запишите данные в вызов
FD_ISSET
определить, имеет ли данный сокет пространство для новых данных.Поддержите соответствующие очереди для поступления и выхода данных.
Как альтернатива POSIX select
функция, специфичное для BSD kqueue
API может также использоваться для обработки событий сокета.
Для получения дополнительной информации
Для узнавания больше о сетях POSIX читайте socket
, listen
, FD_SET
, и select
страницы руководства.
Работа с основанными на пакете сокетами
Рекомендуемый способ отправить и получить пакеты UDP путем объединения POSIX API и любой CFSocket
или GCD APIs. Для использования этого APIs необходимо выполнить следующие шаги:
Создайте сокет путем вызова
socket
.Свяжите сокет путем вызова
bind
. Обеспечьте asockaddr
структура, указывающая информацию о требуемом порте и семье.Подключите сокет путем вызова
connect
(дополнительный).Обратите внимание на то, что подключенный сокет UDP не является соединением в самом чистом значении слова. Однако это обеспечивает два преимущества перед несвязанным сокетом. Во-первых, это устраняет необходимость указать адрес назначения каждый раз, когда Вы отправляете новое сообщение. Во-вторых, когда пакет не может быть поставлен, Ваше приложение может получить ошибки. Эта ошибочная поставка не гарантируется с UDP, однако; это зависит от состояния сети, находящегося вне контроля Вашего приложения.
Оттуда, можно работать с соединением тремя способами:
Если Вы используете GCD для выполненной (рекомендуемой) интеграции цикла, создаете источник отгрузки путем вызова
dispatch_source_create
. Присвойте обработчик событий источнику отгрузки. Дополнительно присвойте обработчик отмены. Наконец, передайте источник отгрузкиdispatch_resume
функция, чтобы начать обрабатывать события.Если Вы используете
CFSocket
для интеграции этот метод несколько более сложен, но упрощает соединять интерфейсом с Вашим кодом с небольшим количеством Какао APIs. ОднакоCFSocket
объекты используют отдельный объект для представления соединения (во многом как сокеты на уровне POSIX), тогда как большая часть Какао, с которым APIs разработан для взаимодействия через интерфейс с APIs на основе потоков, использующим отдельные объекты для отправки и получения. В результате в сочетании с небольшим количеством Какао APIs, ожидающий чтение или потоки записи, может быть трудно использоватьCFSocketRef
объекты.Использовать
CFSocket
:Создайте объект использовать для управления соединением. Если Вы пишете код Objective C, это может быть классом. Если Вы пишете чистый код C, это должно быть Базовым объектом Основы, таким как непостоянный словарь.
Создайте объект контекста для описания того объекта.
CFSocketContext ctxt;
ctxt.version = 0;
ctxt.info = my_context_object;
ctxt.retain = CFRetain;
ctxt.release = CFRelease;
ctxt.copyDescription = NULL;
Создайте a
CFSocket
объект (CFSocketRef
) дляCFSocketNativeHandle
объект путем вызоваCFSocketCreateWithNative
.Обязательно установите (в минимуме)
kCFSocketDataCallBack
флаг в ВашемcallBackTypes
значение параметра. Не устанавливайтеkCFSocketAcceptCallBack
флаг.Необходимо будет также обеспечить указатель на a
CFSocketCallBack
функция обратного вызова как значение параметра выноски.Например:
CFSocketRef connection = CFSocketCreateWithNative(kCFAllocatorDefault,
sock,
kCFSocketDataCallBack,
handleNetworkData,
&ctxt);
Скажите Базовой Основе, что позволяется закрыть сокет, когда базовый Базовый объект Основы лишен законной силы.
CFOptionFlags sockopt = CFSocketGetSocketFlags(connection);
sockopt |= kCFSocketCloseOnInvalidate | kCFSocketAutomaticallyReenableReadCallBack;
CFSocketSetSocketFlags(connection, sockopt);
Создайте источник события для сокета и запланируйте его на свой цикл выполнения.
CFRunLoopSourceRef socketsource = CFSocketCreateRunLoopSource(
kCFAllocatorDefault,
connection,
0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketsource, kCFRunLoopDefaultMode);
Каждый раз, когда новые данные становятся доступными, обратный вызов обработчика данных вызывают. В Вашем обратном вызове, если значение callbackType параметра
kCFSocketConnectCallBack
, проверьте, что параметр данных передал в обратный вызов. Если этоNULL
, Вы соединились с узлом. Можно тогда отправить данные с помощьюCFSocketSendData
функция.Когда Вы закончены с сокетом, закрываете и лишаете законной силы его путем вызова
CFSocketInvalidate
функция.В любой точке можно также получить доступ к базовому сокету BSD путем вызова
CFSocketGetNative
функция.Для получения дополнительной информации см. Ссылку CFSocket. Для примера кода см. проект примера кода UDPEcho.
При использовании чистых сокетов POSIX используйте
select
системный вызов для ожидания данных затем используйтеread
иwrite
системные вызовы для выполнения I/O. Для узнавания больше об отправке и получении пакетов UDP с POSIX снабжают API сокетом, читают Сокет UNIX FAQ в http://developerweb .net/.
Получение собственного дескриптора сокета для потока сокета
Иногда при работе с основанными на сокете потоками (NSInputStream
, NSOutputStream
, CFReadStream
, или CFWriteStream
), Вы, возможно, должны получить базовый дескриптор сокета, связанный с потоком. Например, Вы могли бы хотеть узнать IP-адрес и номер порта для каждого конца потока с getsockname
и getpeername
, или набор снабжает опции сокетом с setsockopt
.
Для получения собственного дескриптора сокета для входного потока вызовите следующий метод:
-(int) socknumForNSInputStream: (NSStream *)stream |
{ |
int sock = -1; |
NSData *sockObj = [stream propertyForKey: |
(__bridge NSString *)kCFStreamPropertySocketNativeHandle]; |
if ([sockObj isKindOfClass:[NSData class]] && |
([sockObj length] == sizeof(int)) ) { |
const int *sockptr = (const int *)[sockObj bytes]; |
sock = *sockptr; |
} |
return sock; |
} |
Можно сделать ту же вещь с потоком вывода, но только необходимо сделать это с одним или другим, потому что потоки ввода и вывода для данного соединения всегда совместно используют тот же базовый собственный сокет.