Swiftパッケージマネージャーを使用したSwiftのソケットフレームワーク。 iOS、MacOS、およびLinuxで動作します。
swift-5.1-RELEASE Toolchain(最新リリースに必要な最小限)swift-5.4-RELEASE Toolchain(推奨)注記:
iOSでUDPサーバーを作成する場合、いくつかのステップに従う必要がある場合があります。
コマンドラインからソケットを構築するには:
% cd <path-to-clone>
% swift build
コマンドラインからソケットの供給されたユニットテストを実行するには:
% cd <path-to-clone>
% swift build
% swift test
BlueSocketをSwift Package Managerパッケージに含めるには、 Package.swiftファイルに定義されているdependencies属性に追加します。 majorVersionとminorパラメーターを使用してバージョンを選択できます。例えば:
dependencies: [
.Package(url: "https://github.com/Kitura/BlueSocket.git", majorVersion: <majorVersion>, minor: <minor>)
]
Carthageを使用したプロジェクトにBlueSocketを含めるには、GitHubの組織とプロジェクト名とバージョンでCartfileにラインを追加します。例えば:
github "Kitura/BlueSocket" ~> <majorVersion>.<minor>
Cocoapodsを使用したプロジェクトにBlueSocketを含めるには、 PodfileにBlueSocketを追加するだけです。たとえば
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は、インスタンスを作成するために使用される4つの異なる工場メソッドを提供します。これらは:
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最小読み取りバッファサイズは、 1024に設定されているSocket.SOCKET_MINIMUM_READ_BUFFER_SIZEです。以下は、読み取りバッファサイズを変更する方法を示しています(Brevityのために省略された例外処理):
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 、聴くために使用されるポートです。 2番目のパラメーターでmaxBacklogSize使用すると、保留中の接続を保持するキューのサイズを設定できます。この関数は、指定されたportに基づいて適切なソケット構成を決定します。 MacOSの便利さのために、定数Socket.SOCKET_MAX_DARWIN_BACKLOGを設定して、最大許容バックログサイズを使用できます。すべてのプラットフォームのデフォルト値は、現在50に設定されているSocket.SOCKET_DEFAULT_MAX_BACKLOGです。サーバーの使用には、この値を増やす必要がある場合があります。リスニングポートの再利用を許可するには、 allowPortReuseをtrue設定します。 falseに設定すると、すでに使用されているポートで聞こうとするとエラーが発生します。 DEFAULT動作は、ポートの再利用allowことです。最後のパラメーターでnode使用して、特定のアドレスで聞くことができます。渡された値は、数値ネットワークアドレスを含むオプションの文字列です(IPv4、IPv6の数値、およびDots表記、16進ストリングの場合)。 DEFAULT動作は、適切なインターフェイスを検索することです。 nodeが不適切にフォーマットされている場合、 socket_err_getaddrinfo_failedエラーが返されます。 nodeが適切にフォーマットされているが、指定されたアドレスが使用できない場合、 socket_err_bind_failedが返されます。listen(on path: String, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG)このAPIは、 .unixプロトコルファミリーでのみ使用できます。最初のパラメーターpath 、聴くために使用されるパスです。 2番目のパラメーターでmaxBacklogSize使用すると、保留中の接続を保持するキューのサイズを設定できます。この関数は、指定されたportに基づいて適切なソケット構成を決定します。 MacOSの便利さのために、定数Socket.SOCKET_MAX_DARWIN_BACKLOGを設定して、最大許容バックログサイズを使用できます。すべてのプラットフォームのデフォルト値は、現在50に設定されているSocket.SOCKET_DEFAULT_MAX_BACKLOGです。サーバーの使用には、この値を増やす必要がある場合があります。 次の例では、デフォルトのSocketインスタンスを作成し、すぐにポート1337でリスニングを開始します。注:例外処理は、簡潔にするために省略されています。例外処理の例については、以下の完全な例を参照してください。
var socket = try Socket . create ( )
try socket . listen ( on : 1337 )リスニングソケットが着信接続要求を検出すると、コントロールがプログラムに返されます。その後、接続を受け入れるか、リスニングを続行するか、アプリケーションがマルチスレッドされている場合はその両方です。 Bluesocketは、着信接続を受け入れる2つの異なる方法をサポートしています。彼らです:
acceptClientConnection(invokeDelegate: Bool = true) - この関数は接続を受け入れ、新しく接続されたソケットに基づいて新しいSocketインスタンスを返します。影響を受けていないインスタンス。 invokeDelegateがfalseで、 SocketにSSLService Delegateが添付されている場合、この関数によって返されるSocketインスタンスを使用してinvokeDelegateOnAcceptメソッドを呼び出す必要があります。invokeDelegateOnAccept(for newSocket: Socket) - SocketインスタンスにSSLService Delegateがある場合、これによりSSL交渉を実行するために代表者が受け入れる機能が呼び出されます。 acceptClientConnectionによって返されるSocketインスタンスで呼び出される必要があります。この関数は、複数回呼び出された間違ったSocketインスタンスで呼び出される場合、またはSocketインスタンスにSSLServiceデリゲートがない場合、例外をスローします。acceptConnection() - この関数は、着信接続を受け入れ、既存のリスニングソケットを置き換えて閉じます。以前はリスニングソケットに関連付けられていたプロパティは、新しく接続されたソケットに関連するプロパティに置き換えられます。上記のcreate(connectedUsing:) Factoryメソッドに加えて、 BlueSocketはSocketインスタンスをサーバーに接続するための3つの追加インスタンス関数をサポートしています。彼らです:
connect(to host: String, port: Int32, timeout: UInt = 0) - このAPIを使用すると、 hostnameとportに基づいてサーバーに接続できます。注: portの値が範囲1-65535にない場合、この関数によってexceptionがスローされます。オプションで、 timeoutミリ秒数に設定して接続を待つことができます。注:ソケットがブロッキングモードの場合、ゼロ(0)を超えるtimeoutが提供されると、一時的に非ブロッキングモードに変更されます。返されたソケットは、元の設定(ブロッキングまたは非ブロック)に戻されます。ソケットが非ブロッキングに設定され、タイムアウト値が提供されない場合、例外がスローされます。または、正常に接続した後、ソケットを非ブロッキングに設定できます。connect(to path: String) - このAPIは、 .unixプロトコルファミリーでのみ使用できます。これにより、提供するpathに基づいてサーバーに接続できます。connect(using signature: Signature)情報を含むSocket.Signatureインスタンスを提供することにより、接続情報を指定できます。詳細については、 socket.socket.swiftのSocket.Signatureを参照してください。Bluesocketは、ソケットからデータを読み取る4つの異なる方法をサポートしています。これらは(推奨される使用順序で):
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 bufSize他のタイプの例外に加えtruncate = true )は、 Socket.SOCKET_ERR_RECV_BUFFER_TOO_SMALLをスローします。 truncate = falseの場合、適切なバッファサイズで再度呼び出す必要があります(詳細については、socket.swiftでError.bufferSizeNeeded in socket.swiftを参照してください)。readString()を除く上記のすべてのAPIはすべてゼロ(0)を返すことができます。これは、リモート接続が閉じられていることを示したり、ソケットがブロックされることを示すことができます(ブロックをオフにしたと仮定して)。 2つを区別するために、プロパティremoteConnectionClosed確認できます。 trueの場合、ソケットリモートパートナーが接続を閉じており、このSocketインスタンスは閉じている必要があります。ソケットからの読み取りに加えて、 BlueSocketはソケットにデータを書き込むための4つの方法も提供します。これらは(推奨される使用順序で):
write(from data: Data) - この関数は、 Dataオブジェクト内に含まれるデータをソケットに書き込みます。write(from data: NSData) - この関数は、 NSDataオブジェクト内に含まれるデータをソケットに書き込みます。write(from string: String) - この関数は、ソケットに提供されるStringに含まれるデータを書き込みます。write(from buffer: UnsafeRawPointer, bufSize: Int) - この関数は、そのバッファーとそのバッファのサイズを示す整数に安全でないポインターを提供することにより、指定されたサイズのバッファ内に含まれるデータを書き込みます。Bluesocketは、着信データグラムをリッスンする3つの異なる方法をサポートしています。これらは(推奨される使用順序で):
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) - この関数は着信データグラムの聴き、読み取り、通過した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を設定して、最大許容バックログサイズを使用できます。すべてのプラットフォームのデフォルト値は、現在50に設定されているSocket.SOCKET_DEFAULT_MAX_BACKLOGです。サーバーの使用には、この値を増やす必要がある場合があります。BlueSocketは、着信データグラムを読み取る3つの異なる方法をサポートしています。これらは(推奨される使用順序で):
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は、データグラムをソケットに書き込むための4つの方法も提供します。これらは(推奨される使用順序で):
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いずれかを使用する上記のAPIの読み取りと書き込みは、それほど遠くない将来におそらく非推奨です。
hostnameAndPort(from address: Address) - このクラス関数は、特定のSocket.Addressからホスト名とポートを抽出する手段を提供します。正常に完了すると、 hostnameとportを含むタプルが返されます。checkStatus(for sockets: [Socket]) - このクラス関数を使用すると、 Socketインスタンスの配列のステータスを確認できます。完了すると、2つのSocketアレイを含むタプルが返されます。最初の配列には、 Socketインスタンスが含まれています。データが読み取られ、2番目の配列には書き込みできるSocketインスタンスが含まれています。このAPIはブロックしません。各Socketインスタンスのステータスを確認し、結果を返します。wait(for sockets: [Socket], timeout: UInt, waitForever: Bool = false) - このクラス関数は、 Socketインスタンスの配列を監視することができ、タイムアウトが発生するか、監視対象のSocketインスタンスのいずれかでデータを読み取ることができます。ゼロ(0)のタイムアウトが指定されている場合、このAPIは各ソケットをチェックしてすぐに返します。それ以外の場合、タイムアウトの有効期限が切れるか、監視されているSocketインスタンスの1つ以上からデータが読み取られるまで待ちます。タイムアウトが発生した場合、このAPIはnilを返します。監視対象のSocketインスタンスの1つ以上でデータが利用可能な場合、それらのインスタンスは配列で返されます。 waitForeverフラグがtrueに設定されている場合、関数は、指定されたタイムアウト値に関係なくデータが利用可能になるまで無期限に待機します。createAddress(host: String, port: Int32) - このクラス関数により、 hostとportが与えられたAddress列挙の作成が可能になります。成功すると、この関数は、指定されたhostが存在しない場合、 Addressまたはnilを返します。isReadableOrWritable(waitForever: Bool = false, timeout: UInt = 0) - このインスタンス関数により、 Socketインスタンスが読み取り可能か/または書き込み可能かどうかを判断できます。 2つのBool値を含むタプルが返されます。 1つ目は、TRUEの場合、 Socketインスタンスに読み取りデータがあることを示します。2番目の場合、TRUEの場合、 Socketインスタンスを書き込むことができることを示します。 Trueの場合は、 Socketが読み取り可能または書き込み可能になるか、エラーが発生するまでこのルーチンを待機させwaitForever 。 falseの場合、 timeoutパラメーターは待機時間を指定します。タイムアウト値に対してゼロ(0)の値が指定されている場合、この関数は現在のステータスを確認し、すぐに戻ります。この関数は、最初のreadable 2つのブリアンを含むタプルをwritableます。 Socketが読み取り可能であるか、繰り返し執筆可能である場合、それらはtrueに設定されます。どちらも真実に設定されていない場合、タイムアウトが発生しました。注:新しく接続されたソケットに書き込もうとしている場合は、操作を試みる前に書き込み可能であることを確認する必要があります。setBlocking(shouldBlock: Bool) - このインスタンス関数を使用すると、このSocketインスタンスをブロッキングモードに配置するかどうかを制御できます。注:すべてのSocketインスタンスは、デフォルトではブロッキングモードで作成されます。setReadTimeout(value: UInt = 0) -このインスタンス関数を使用すると、読み取り操作のタイムアウトを設定できます。 value 、読み取り操作が戻る前に待機する時間を指定するUIntです。タイムアウトが発生した場合、読み取り操作は0バイトの読み取りを返し、 errno EAGAINに設定されます。setWriteTimeout(value: UInt = 0) -このインスタンス関数を使用すると、書き込み操作のタイムアウトを設定できます。 value 、書き込み操作が戻る前に待機する時間を指定するUIntです。タイムアウトが発生した場合、書き込み操作は書き込まれた0バイトを返し、 errno TCPおよびUNIXソケットのEAGAINに設定されます。UDPの場合、タイムアウト値に関係なく書き込み操作が成功します。udpBroadcast(enable: Bool) - このインスタンス関数は、UDPソケットでブロードキャストモードを有効にするために使用されます。 true渡してブロードキャストを有効にし、 falseを無効にします。 SocketインスタンスがUDPソケットではない場合、この関数は例外をスローします。次の例は、新しいGCD basedディスパッチAPIを使用して比較的シンプルなマルチスレッドエコーサーバーを作成する方法を示しています。以下は、一度実行されると、 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 ( )このサーバーは、Swift 4を使用して次のPackage.swiftファイルを指定して構築できます。
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 "
] ) ,
]
)または、次のPackage.swiftファイルを指定して、まだSwift 3を使用している場合。
import PackageDescription
let package = Package (
name : " EchoServer " ,
dependencies : [
. Package ( url : " https://github.com/Kitura/BlueSocket.git " , majorVersion : 1 , minor : 0 ) ,
] ,
exclude : [ " EchoServer.xcodeproj " ]
)次のコマンドシーケンスは、LinuxでEchoサーバーを構築および実行します。 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について話すのが大好きです。チームに会うために私たちのスラックに参加してください!
このライブラリは、Apache 2.0の下でライセンスされています。完全なライセンステキストはライセンスで利用できます。