Используя сокеты и потоки сокета
Эта статья объясняет, как работать с сокетами и потоками сокета на различных уровнях от POSIX до Основы.
На почти каждом уровне сетей программное обеспечение может быть разделено на две категории: клиенты (программы, соединяющиеся с другими приложениями) и службы (программы, которые другие приложения подключают с). На высоком уровне эти строки ясны. Большинство программ записанный использующий высокоуровневый APIs является просто клиентами. На более низком уровне, однако, строки являются часто расплывчатыми.
Сокет и поток, программирующий обычно, попадают в одну из следующих широких категорий:
Основанная на пакете коммуникация — Программы, воздействующие на один пакет за один раз, прислушиваясь к входящим пакетам, затем отправляя пакеты в ответ.
С основанной на пакете коммуникацией единственными различиями между клиентами и серверами является содержание пакетов, которые каждая программа отправляет и получает, и (по-видимому) что каждая программа делает с данными. Сам сетевой код идентичен.
Клиенты на основе потоков — Программы, использующие TCP, чтобы отправить и получить данные как два непрерывных потока байтов, один в каждом направлении.
С коммуникацией на основе потоков клиенты и серверы несколько более отличны. Фактическая часть обработки данных клиентов и серверов подобна, но способ, которым программа первоначально создает канал передачи, очень отличается.
Эта глава разделена на разделы на основе вышеупомянутых задач:
Выбор API Family — Описывает, как решить который семья API использовать при записи объединяющий код в сеть.
Запись Основанного на TCP Клиента — Описывает, как сделать исходящие соединения TCP к существующим серверам и службам.
Запись Основанного на TCP Сервера — Описывает, как прислушаться к входящим соединениям TCP при записи серверов и служб.
Работа с Основанными на пакете Сокетами — Описывает, как работать с протоколами не-TCP, такими как UDP.
Выбор семьи API
API, который Вы выбираете для основанных на сокете соединений, зависит от того, делаете ли Вы соединение с другим узлом или получаете соединение от другого узла. Это также зависит от того, используете ли Вы TCP или некоторый другой протокол. Вот несколько факторов для рассмотрения:
В OS X, если у Вас уже есть сетевой код, совместно использующийся с платформами не-Apple, можно использовать POSIX C сетевой APIs и продолжать использовать сетевой код как есть (на отдельном потоке). Если Ваша программа основывается на Базовой Основе или Какао (Основа) выполненный цикл, можно также использовать Базовую Основу
CFStreamAPI для интеграции сетевого кода 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, использовать
CFStreamAPI. Это интегрирует более легко с другой Базовой Основой 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; |
} |
Можно сделать ту же вещь с потоком вывода, но только необходимо сделать это с одним или другим, потому что потоки ввода и вывода для данного соединения всегда совместно используют тот же базовый собственный сокет.