ПРОКТИНА СВОЙСТВА ДЛЯ СВИФТ ИСПОЛЬЗОВАНИЕ SWIFT Package Manager. Работает на iOS, MacOS и Linux.
swift-5.1-RELEASE инструментов ( минимум требуется для последнего релиза )swift-5.4-RELEASE инструментов ( рекомендуется )Примечание:
Если создать UDP -сервер на iOS, вам может потребоваться выполнить несколько шагов:
Чтобы построить гнездо из командной строки:
% cd <path-to-clone>
% swift build
Чтобы запустить поставленные модульные тесты для сокета из командной строки:
% cd <path-to-clone>
% swift build
% swift test
Чтобы включить BlueSocket в пакет Swift Package Manager, добавьте его в атрибут dependencies , определенный в вашем файле Package.swift . Вы можете выбрать версию, используя majorVersion и minor параметры. Например:
dependencies: [
.Package(url: "https://github.com/Kitura/BlueSocket.git", majorVersion: <majorVersion>, minor: <minor>)
]
Чтобы включить Bluesocket в проект с использованием Carthage, добавьте линию в свой Cartfile с организацией Github и названиями проектов и версией. Например:
github "Kitura/BlueSocket" ~> <majorVersion>.<minor>
Чтобы включить BlueSocket в проект с использованием кокопод, вы просто добавляете BlueSocket в свой Podfile , например:
platform :ios, '10.0'
target 'MyApp' do
use_frameworks!
pod 'BlueSocket'
end
Первое, что вам нужно сделать, это импортировать рамку сокета. Это делается следующим образом:
import Socket
BlueSocket поддерживает следующие семейства, типы и протоколы:
Socket.ProtocolFamily.inetSocket.ProtocolFamily.inet6Socket.ProtocolFamily.unixSocket.SocketType.streamSocket.SocketType.datagramSocket.SocketProtocol.tcpSocket.SocketProtocol.udpSocket.SocketProtocol.unixBlueSocket предоставляет четыре различных заводских метода, которые используются для создания экземпляра. Это:
create() - Это создает полностью настроенный сокет по умолчанию. Сокет по умолчанию создается с family: .inet , type: .stream и proto: .tcp .create(family family: ProtocolFamily, type: SocketType, proto: SocketProtocol) - этот API позволяет создавать настроенный экземпляр Socket , настроенный для ваших потребностей. Вы можете настроить семейство протоколов, тип розетки и протокол розетки.create(connectedUsing signature: Signature) - этот API позволит вам создать экземпляр Socket и попробовать его попытаться подключиться к серверу на основе информации, которую вы передаете в Socket.Signature .create(fromNativeHandle nativeHandle: Int32, address: Address?) - Этот API позволяет вам обернуть дескриптор нативного файла, описывающий существующий розетка в новом экземпляре Socket . BlueSocket позволяет установить размер буфера для чтения, который он будет использовать. Затем, в зависимости от потребностей приложения, вы можете изменить его на более высокое или более низкое значение. По умолчанию установлено в Socket.SOCKET_DEFAULT_READ_BUFFER_SIZE , которое имеет значение 4096 . Минимальный размер буфера считывания - Socket.SOCKET_MINIMUM_READ_BUFFER_SIZE , который установлен на 1024 . Ниже показано, как изменить размер буфера чтения (обработка исключений опущена для краткости):
let mySocket = try Socket.create()
mySocket.readBufferSize = 32768
Приведенный выше пример устанавливает размер буфера для чтения по умолчанию на 32768 . Этот параметр должен быть выполнен до того , как использует экземпляр Socket в первый раз.
Чтобы закрыть гнездо открытого экземпляра, предоставлена следующая функция:
close() - Эта функция выполнит необходимые задачи, чтобы чисто закрыть открытый розетка.Чтобы использовать BlueSocket для прослушивания соединения на розетке. Предоставлено следующее API:
listen(on port: Int, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG, allowPortReuse: Bool = true, node: String? = nil) port Второй параметр, maxBacklogSize позволяет установить размер очереди, удерживающего ожидающие подключения. Функция будет определять соответствующую конфигурацию сокета на основе указанного port . Для удобства на macOS, постоянный Socket.SOCKET_MAX_DARWIN_BACKLOG Значением по умолчанию для всех платформ является Socket.SOCKET_DEFAULT_MAX_BACKLOG , в настоящее время установленное на 50 . Для использования сервера может потребоваться увеличить это значение. Чтобы разрешить повторное использование порта прослушивания, установите allowPortReuse true . Если вы установите на false , ошибка произойдет, если вы попытаетесь прослушать порт, уже используемый. Поведение DEFAULT состоит в том, чтобы allow повторное использование порта. Последний параметр, node , может использоваться для прослушивания конкретного адреса . Переданное значение - это необязательная строка, содержащая числовой сетевой адрес (для IPv4, номера и точек, для IPv6, шестнадцатеричной стртинг). Поведение DEFAULT заключается в поиске подходящего интерфейса. Если node неправильно отформатирован Socket_err_gretdrinfo_failed , будет возвращена ошибка. Если node правильно отформатирован, но указанный адрес недоступен, будет возвращен Socket_err_bind_failed .listen(on path: String, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG) Этот API может использоваться только с семейством протоколов .unix . Первый path параметра - это путь, который можно использовать для прослушивания. Второй параметр, maxBacklogSize позволяет установить размер очереди, удерживающего ожидающие подключения. Функция будет определять соответствующую конфигурацию сокета на основе указанного port . Для удобства на macOS, постоянный Socket.SOCKET_MAX_DARWIN_BACKLOG Значением по умолчанию для всех платформ является Socket.SOCKET_DEFAULT_MAX_BACKLOG , в настоящее время установленное на 50 . Для использования сервера может потребоваться увеличить это значение. Следующий пример создает экземпляр Socket по умолчанию, а затем немедленно начинает слушать на порту 1337 . Примечание. Обработка исключений опущена для краткости, см. Полный пример ниже для примера обработки исключений.
var socket = try Socket . create ( )
try socket . listen ( on : 1337 )Когда прослушивание обнаруживает входящий запрос на соединение, управление возвращается в вашу программу. Затем вы можете либо принять подключение, либо продолжить прослушивание, либо оба, если ваше приложение является многопоточным. BlueSocket поддерживает два различных способа принятия входящего соединения. Они есть:
acceptClientConnection(invokeDelegate: Bool = true) - Эта функция принимает соединение и возвращает новый экземпляр Socket на основе вновь подключенного сокета. Экземпляр, который слушал в незатронутых. Если invokeDelegate является false , и Socket прикреплен делегат SSLService , вы должны вызвать метод invokeDelegateOnAccept , используя экземпляр Socket , который возвращается этой функцией.invokeDelegateOnAccept(for newSocket: Socket) - Если экземпляр Socket имеет делегат SSLService , это приведет к тому, что делегаты принимают функцию для выполнения переговоров SSL. Это должно быть вызвано с помощью экземпляра Socket , возвращаемого acceptClientConnection . Эта функция вызовет исключение, если вызовут с неверным экземпляром Socket , вызывается несколько раз, или если экземпляр Socket не имеет делегата SSLService .acceptConnection() - Эта функция принимает входящее соединение, заменяя и закрывая существующий сокет прослушивания. Свойства, которые ранее были связаны с сокетом прослушивания, заменяются свойствами, которые имеют отношение к новому подключению. В дополнение к create(connectedUsing:) Factory Method, описанный выше, BlueSocket поддерживает три дополнительных функция экземпляра для подключения экземпляра Socket к серверу. Они есть:
connect(to host: String, port: Int32, timeout: UInt = 0) - Этот API позволяет подключаться к серверу на основе hostname и port , который вы предоставляете. ПРИМЕЧАНИЕ. exception будет добавлено этой функцией, если значение port не будет в диапазоне 1-65535 . При желании вы можете установить timeout на количество миллисекундов, чтобы ждать подключения. ПРИМЕЧАНИЕ. Если гнездо находится в режиме блокировки, он будет временно изменен в режим не блокировки, если будет предоставлен timeout превышающий нулевое (0). Возвращенный гнездо будет возвращен к его исходной настройке (блокировку или не блокировку) . Если розетка установлена на не блокировку и не будет предоставлено значение тайм-аута , будет выброшено исключение. В качестве альтернативы вы можете установить розетку на не блокировку после успешного подключения.connect(to path: String) - Этот API может использоваться только с семейством протоколов .unix . Это позволяет вам подключаться к серверу на основе предоставленного вами path .connect(using signature: Signature) - этот API позволяет вам указать информацию об соединении, предоставляя экземпляр Socket.Signature , содержащий информацию. Обратитесь к Socket.Signature в Socket.Swift для получения дополнительной информации.Bluesocket поддерживает четыре различных способа считывания данных из сокета. Это (в рекомендуемом порядке использования):
read(into data: inout Data) - Эта функция считывает все данные, доступные на сокете и возвращает их в объект Data , который был передан.read(into data: NSMutableData) - Эта функция считывает все данные, доступные на розетке и возвращает их в объект NSMutableData , который был передан.readString() - Эта функция считывает все данные, доступные на гнездо, и возвращает их в виде String . nil возвращается, если данные не доступны для чтения.read(into buffer: UnsafeMutablePointer<CChar>, bufSize: Int, truncate: Bool = false) топонтер Этот API truncate = true в дополнение к другим типам bufSize ) будет выбросить Socket.SOCKET_ERR_RECV_BUFFER_TOO_SMALL Если truncate = false , вам нужно будет снова позвонить с правильным размером буфера (см Error.bufferSizeNeeded в Socket.Swift для получения дополнительной информации).readString() могут вернуть ноль (0). Это может указывать на то, что удаленное соединение было закрыто или может указывать на то, что гнездо будет блокировать (при условии, что вы отключили блокировку). Чтобы различить их, можно проверить свойство remoteConnectionClosed . Если true , удаленный партнер Socket закрыл соединение, и этот экземпляр Socket должен быть закрыт.В дополнение к чтению из сокета, BlueSocket также поставляет четыре метода для написания данных в розетку. Это (в рекомендуемом порядке использования):
write(from data: Data) - Эта функция записывает данные, содержащиеся в объекте Data в сокет.write(from data: NSData) - эта функция записывает данные, содержащиеся в объекте NSData в сокет.write(from string: String) - Эта функция записывает данные, содержащиеся в String , предоставленную для сокета.write(from buffer: UnsafeRawPointer, bufSize: Int) - эта функция записывает данные, содержащиеся в буфере указанного размера, предоставляя небезопасную указатель на этот буфер и целое число, которое обозначает размер этого буфера.Bluesocket поддерживает три разных способа прослушивания входящих данных. Это (в рекомендуемом порядке использования):
listen(forMessage data: inout Data, on port: Int, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG) - эта функция прослушивает входящую дейтаграмму, читает его и возвращает в переданный объект Data . Он возвращает кортеж, содержащий количество чтения байтов, и Address , где возникли данные.listen(forMessage data: NSMutableData, on port: Int, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG) - эта функция прослушивает входящий Datagram, читает его и возвращает в переданный объект NSMutableData . Он возвращает кортеж, содержащий количество чтения байтов, и Address , где возникли данные.listen(forMessage buffer: UnsafeMutablePointer<CChar>, bufSize: Int, on port: Int, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG) топ Data Он возвращает кортеж, содержащий количество чтения байтов, и Address , где возникли данные.port . Установка значения port на ноль (0) приведет к определению подходящего свободного порта.maxBacklogSize позволяет установить размер очереди, удерживающего ожидающие подключения. Функция будет определять соответствующую конфигурацию сокета на основе указанного port . Для удобства на macOS, постоянный Socket.SOCKET_MAX_DARWIN_BACKLOG Значением по умолчанию для всех платформ является Socket.SOCKET_DEFAULT_MAX_BACKLOG , в настоящее время установленное на 50 . Для использования сервера может потребоваться увеличить это значение.BlueSocket поддерживает три разных способа чтения входящих данных. Это (в рекомендуемом порядке использования):
readDatagram(into data: inout Data) - Эта функция считывает входящую дейтаграмму и возвращает ее в переданный объект Data . Он возвращает кортеж, содержащий количество чтения байтов, и Address , где возникли данные.readDatagram(into data: NSMutableData) - Эта функция считывает входящую дейтаграмму и возвращает ее в пройденном объекте NSMutableData . Он возвращает кортеж, содержащий количество чтения байтов, и Address , где возникли данные.readDatagram(into buffer: UnsafeMutablePointer<CChar>, bufSize: Int) - Эта функция считывает входящую дейтаграмму и возвращает ее в переданный объект Data . Он возвращает кортеж, содержащий количество чтения байтов, и Address , где возникли данные. Если объем чтения данных больше bufSize bufSize Остальная часть чтения данных будет отброшена.BlueSocket также поставляет четыре метода для написания Datagrams в розетку. Это (в рекомендуемом порядке использования):
write(from data: Data, to address: Address) - Эта функция записывает датаграмму, содержащуюся в объекте Data в сокет.write(from data: NSData, to address: Address) - эта функция записывает документ, содержащуюся в объекте NSData в сокет.write(from string: String, to address: Address) - Эта функция записывает датаграмму, содержащуюся в String , предоставленной для сокета.write(from buffer: UnsafeRawPointer, bufSize: Int, to address: Address) - эта функция записывает данные, содержащиеся в буфере указанного размера, предоставляя небезопасную указатель на этот буфер и целое число, которое обозначает размер этого буфера.address представляет адрес для пункта назначения, на который вы отправляете дейтаграмму. АПИ для чтения и записи выше, в которых используются либо NSData , либо NSMutableData , вероятно, будут устареть в не столь отдаленном будущем.
hostnameAndPort(from address: Address) - Эта функция класса предоставляет средства для извлечения имени хоста и порта из данного Socket.Address . Address. При успешном завершении возвращается кортеж, содержащий hostname и port .checkStatus(for sockets: [Socket]) - эта функция класса позволяет проверять состояние массива экземпляров Socket . После завершения возвращается кортеж, содержащий два массива Socket . Первый массив содержит экземпляры Socket , которые имеют доступные данные для чтения, а второй массив содержит экземпляры Socket , которые могут быть записаны. Этот API не блокирует. Он проверит состояние каждого экземпляра Socket , а затем вернет результаты.wait(for sockets: [Socket], timeout: UInt, waitForever: Bool = false) - эта функция класса позволяет контролировать массив экземпляров Socket , ожидая, когда произойдет время ожидания, либо данные будут читаемыми на одном из контролируемых экземпляров Socket . Если указан тайм -аут (0), этот API проверит каждый розетка и немедленно вернется. В противном случае он будет ждать, пока не истекает время ожидания, либо данные не будут считываемыми из одного или нескольких экземпляров отслеживаемого Socket . Если произойдет тайм -аут, этот API вернет nil . Если данные доступны на одном или нескольких экземплярах Socket , эти экземпляры будут возвращены в массиве. Если флаг waitForever установлен на True, функция будет ждать бесконечно, чтобы данные станут доступными независимо от указанного значения тайм -аута .createAddress(host: String, port: Int32) - Эта функция класса позволяет создавать перечисление Address с учетом host и port . При успехе эта функция возвращает Address или nil если указанный host не существует.isReadableOrWritable(waitForever: Bool = false, timeout: UInt = 0) - эта функция экземпляра позволяет определить, является ли экземпляр Socket читаемым и/или записи. Возвращается кортеж, содержащий два значения Bool . Первый, если истина, указывает, что экземпляр Socket имеет данные для чтения, второе, если истина, указывает, что экземпляр Socket может быть записан. waitForever Если это правда, заставляет эту подпрограмму ждать, пока Socket будет либо читаемой, либо для записи, или возникает ошибка. Если ложно, параметр timeout указывает, как долго ждать. Если значение нуля (0) указано для значения тайм -аута, эта функция проверит текущее состояние и немедленно возвращает. Эта функция возвращает кортеж, содержащий два логического, первого readable и второго, writable . Они настроены на True, если Socket либо читается, либо для записи, что можно записать. Если ни один из них не установлен на True, произошел тайм -аут. ПРИМЕЧАНИЕ. Если вы пытаетесь написать в недавно подключенном розетке , вы должны убедиться, что он подлежит записи, прежде чем пытаться выполнить операцию.setBlocking(shouldBlock: Bool) - Эта функция экземпляра позволяет управлять вам, следует ли размещать этот экземпляр Socket в режим блокировки или нет. Примечание. Все экземпляры Socket по умолчанию создаются в режиме блокировки .setReadTimeout(value: UInt = 0) - Эта функция экземпляра позволяет устанавливать тайм -аут для операций чтения. value - это UInt . Указывает время для операции чтения, чтобы подождать, прежде чем вернуться. В случае тайм -аута операция чтения вернет 0 байт чтения, и errno будет установлен в EAGAIN .setWriteTimeout(value: UInt = 0) - эта функция экземпляра позволяет устанавливать тайм -аут для операций записи. value - это UInt . Указывает время, когда операция записи ждать, прежде чем вернуться. В случае тайм -аута операция записи вернет записанные 0 байт, и errno будет установлен в EAGAIN для TCP и Unix Sockets, для UDP операция записи будет успешной независимо от значения тайм -аута.udpBroadcast(enable: Bool) - эта функция экземпляра используется для включения режима трансляции в розетке UDP. Передайте true , чтобы включить трансляцию, false отключить. Эта функция вызовет исключение, если экземпляр Socket не является сокетом UDP. В следующем примере показано, как создать относительно простой многопоточный сервер ECHO, используя новый API диспетчерского API GCD based . Далее следует код для простого эхо -сервера, который после запуска может быть доступен через telnet ::1 1337 .
import Foundation
import Socket
import Dispatch
class EchoServer {
static let quitCommand : String = " QUIT "
static let shutdownCommand : String = " SHUTDOWN "
static let bufferSize = 4096
let port : Int
var listenSocket : Socket ? = nil
var continueRunningValue = true
var connectedSockets = [ Int32 : Socket ] ( )
let socketLockQueue = DispatchQueue ( label : " com.kitura.serverSwift.socketLockQueue " )
var continueRunning : Bool {
set ( newValue ) {
socketLockQueue . sync {
self . continueRunningValue = newValue
}
}
get {
return socketLockQueue . sync {
self . continueRunningValue
}
}
}
init ( port : Int ) {
self . port = port
}
deinit {
// Close all open sockets...
for socket in connectedSockets . values {
socket . close ( )
}
self . listenSocket ? . close ( )
}
func run ( ) {
let queue = DispatchQueue . global ( qos : . userInteractive )
queue . async { [ unowned self ] in
do {
// Create an IPV6 socket...
try self . listenSocket = Socket . create ( family : . inet6 )
guard let socket = self . listenSocket else {
print ( " Unable to unwrap socket... " )
return
}
try socket . listen ( on : self . port )
print ( " Listening on port: ( socket . listeningPort ) " )
repeat {
let newSocket = try socket . acceptClientConnection ( )
print ( " Accepted connection from: ( newSocket . remoteHostname ) on port ( newSocket . remotePort ) " )
print ( " Socket Signature: ( String ( describing : newSocket . signature ? . description ) ) " )
self . addNewConnection ( socket : newSocket )
} while self . continueRunning
}
catch let error {
guard let socketError = error as? Socket . Error else {
print ( " Unexpected error... " )
return
}
if self . continueRunning {
print ( " Error reported: n ( socketError . description ) " )
}
}
}
dispatchMain ( )
}
func addNewConnection ( socket : Socket ) {
// Add the new socket to the list of connected sockets...
socketLockQueue . sync { [ unowned self , socket ] in
self . connectedSockets [ socket . socketfd ] = socket
}
// Get the global concurrent queue...
let queue = DispatchQueue . global ( qos : . default )
// Create the run loop work item and dispatch to the default priority global queue...
queue . async { [ unowned self , socket ] in
var shouldKeepRunning = true
var readData = Data ( capacity : EchoServer . bufferSize )
do {
// Write the welcome string...
try socket . write ( from : " Hello, type 'QUIT' to end session n or 'SHUTDOWN' to stop server. n " )
repeat {
let bytesRead = try socket . read ( into : & readData )
if bytesRead > 0 {
guard let response = String ( data : readData , encoding : . utf8 ) else {
print ( " Error decoding response... " )
readData . count = 0
break
}
if response . hasPrefix ( EchoServer . shutdownCommand ) {
print ( " Shutdown requested by connection at ( socket . remoteHostname ) : ( socket . remotePort ) " )
// Shut things down...
self . shutdownServer ( )
return
}
print ( " Server received from connection at ( socket . remoteHostname ) : ( socket . remotePort ) : ( response ) " )
let reply = " Server response: n ( response ) n "
try socket . write ( from : reply )
if ( response . uppercased ( ) . hasPrefix ( EchoServer . quitCommand ) || response . uppercased ( ) . hasPrefix ( EchoServer . shutdownCommand ) ) &&
( !response . hasPrefix ( EchoServer . quitCommand ) && !response . hasPrefix ( EchoServer . shutdownCommand ) ) {
try socket . write ( from : " If you want to QUIT or SHUTDOWN, please type the name in all caps. ? n " )
}
if response . hasPrefix ( EchoServer . quitCommand ) || response . hasSuffix ( EchoServer . quitCommand ) {
shouldKeepRunning = false
}
}
if bytesRead == 0 {
shouldKeepRunning = false
break
}
readData . count = 0
} while shouldKeepRunning
print ( " Socket: ( socket . remoteHostname ) : ( socket . remotePort ) closed... " )
socket . close ( )
self . socketLockQueue . sync { [ unowned self , socket ] in
self . connectedSockets [ socket . socketfd ] = nil
}
}
catch let error {
guard let socketError = error as? Socket . Error else {
print ( " Unexpected error by connection at ( socket . remoteHostname ) : ( socket . remotePort ) ... " )
return
}
if self . continueRunning {
print ( " Error reported by connection at ( socket . remoteHostname ) : ( socket . remotePort ) : n ( socketError . description ) " )
}
}
}
}
func shutdownServer ( ) {
print ( " n Shutdown in progress... " )
self . continueRunning = false
// Close all open sockets...
for socket in connectedSockets . values {
self . socketLockQueue . sync { [ unowned self , socket ] in
self . connectedSockets [ socket . socketfd ] = nil
socket . close ( )
}
}
DispatchQueue . main . sync {
exit ( 0 )
}
}
}
let port = 1337
let server = EchoServer ( port : port )
print ( " Swift Echo Server Sample " )
print ( " Connect with a command line window by entering 'telnet ::1 ( port ) ' " )
server . run ( ) Этот сервер может быть создан, указав следующий файл Package.swift с помощью Swift 4.
import PackageDescription
let package = Package (
name : " EchoServer " ,
dependencies : [
. package ( url : " https://github.com/Kitura/BlueSocket.git " , from : " 1.0.8 " ) ,
] ,
targets : [
. target (
name : " EchoServer " ,
dependencies : [
" Socket "
] ) ,
]
) Или, если вы все еще используете Swift 3, указав следующий файл Package.swift .
import PackageDescription
let package = Package (
name : " EchoServer " ,
dependencies : [
. Package ( url : " https://github.com/Kitura/BlueSocket.git " , majorVersion : 1 , minor : 0 ) ,
] ,
exclude : [ " EchoServer.xcodeproj " ]
) Следующая командная последовательность будет создавать и запустить сервер Echo на Linux. Если вы работаете на MacOS или с любым инструментами, новым , чем 8/18, вы можете опустить переключатель -Xcc -fblocks поскольку он больше не нужен.
$ swift build -Xcc -fblocks
$ .build/debug/EchoServer
Swift Echo Server Sample
Connect with a command line window by entering 'telnet ::1 1337'
Listening on port: 1337
Мы любим говорить на стороне сервера Swift и Kitura. Присоединяйтесь к нашему Slack, чтобы встретиться с командой!
Эта библиотека лицензирована в соответствии с Apache 2.0. Полный текст лицензии доступен в лицензии.