Socket Framework لـ Swift باستخدام Swift Package Manager. يعمل على iOS و MacOS و Linux.
swift-5.1-RELEASE CHAIN ( الحد الأدنى المطلوب لأحدث الإصدار )swift-5.4-RELEASE CHAIN ( موصى به )ملحوظة:
إذا تم إنشاء خادم 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 في مشروع باستخدام قرطاج ، أضف خطًا إلى Cartfile مع منظمة Github وأسماء المشروع والإصدار. على سبيل المثال:
github "Kitura/BlueSocket" ~> <majorVersion>.<minor>
لتضمين Bluesocket في مشروع باستخدام Cocoapods ، يمكنك فقط إضافة 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.unixيوفر Bluesocket أربع طرق مختلفة للمصنع المستخدمة لإنشاء مثيل. هذه هي:
create() - هذا ينشئ مقبسًا افتراضيًا تم تكوينه بالكامل. يتم إنشاء مقبس افتراضي مع family: .inet ، type: .stream ، و proto: .tcp .create(family family: ProtocolFamily, type: SocketType, proto: SocketProtocol) - يتيح لك واجهة برمجة التطبيقات هذه إنشاء مثيل Socket تم تكوينه مخصصًا لتلبية احتياجاتك. يمكنك تخصيص عائلة البروتوكول ونوع المقبس وبروتوكول المقبس.create(connectedUsing signature: Signature) - سيتيح لك واجهة برمجة التطبيقات هذه إنشاء مثيل Socket ويحاول الاتصال بخادم استنادًا إلى المعلومات التي تمررها في Socket.Signature .create(fromNativeHandle nativeHandle: Int32, address: Address?) - يتيح لك واجهة برمجة التطبيقات لفائف واصف ملف أصلي يصف مقبسًا موجودًا في مثيل جديد 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 للاستماع إلى اتصال على مقبس ، يتم توفير واجهة برمجة التطبيقات التالية:
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_getAddrinfo_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:) طريقة المصنع الموضحة أعلاه ، يدعم Bluesocket ثلاث وظائف مثيل إضافية لتوصيل مثيل Socket بخادم. هم:
connect(to host: String, port: Int32, timeout: UInt = 0) - يتيح لك واجهة برمجة التطبيقات هذه الاتصال بخادم استنادًا إلى hostname port الذي تقدمه. ملاحظة: سيتم طرح exception من خلال هذه الوظيفة إذا لم تكن قيمة port في النطاق 1-65535 . اختياريا ، يمكنك تعيين timeout على عدد المللي ثانية لانتظار الاتصال. ملاحظة: إذا كان المقبس في وضع الحظر ، فسيتم تغييره إلى وضع عدم الحظر مؤقتًا إذا تم توفير timeout أكبر من الصفر (0). سيتم تعيين المقبس الذي تم إرجاعه إلى إعداده الأصلي (الحظر أو عدم الحظر) . إذا تم ضبط المقبس على عدم الحظر ولم يتم توفير قيمة مهلة ، فسيتم طرح استثناء. بدلاً من ذلك ، يمكنك ضبط المقبس على عدم الحظر بعد الاتصال بنجاح.connect(to path: String) - لا يمكن استخدام API إلا مع عائلة بروتوكول .unix . يسمح لك بالاتصال بخادم بناءً على path الذي تقدمه.connect(using signature: Signature) - يتيح لك واجهة برمجة التطبيقات هذه تحديد معلومات الاتصال من خلال توفير مثيل 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) - تتيح لك هذه الوظيفة قراءة البيانات في المخزن المؤقت بحجم محدد من خلال توفير مؤشر غير آمن لذلك المخزن المؤقت و integer يدل على حجم هذا المخزن المؤقت. سوف يرمي Socket.SOCKET_ERR_RECV_BUFFER_TOO_SMALL (API) truncate = true بالإضافة إلى أنواع أخرى bufSize الاستثناءات) مقبسًا. إذا كانت truncate = false ، فستحتاج إلى الاتصال مرة أخرى بحجم المخزن المؤقت المناسب (انظر Error.bufferSizeNeeded في Socket.swift لمزيد من المعلومات).readString() إرجاع الصفر (0). يمكن أن يشير هذا إلى أن الاتصال البعيد قد تم إغلاقه أو يمكن أن يشير إلى أن المقبس سيحظر (على افتراض أنك أوقفت منع الحظر). للتمييز بين الاثنين ، يمكن فحص Property remoteConnectionClosed . إذا كان true ، فقد أغلق الشريك البعيد المقبس الاتصال ويجب إغلاق مثيل 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 الذي تم تمريره. يرجع tuple التي تحتوي على عدد بايت قراءة Address المكان الذي نشأت فيه البيانات.listen(forMessage data: NSMutableData, on port: Int, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG) - تستمع هذه الوظيفة إلى بيانات بيانات واردة ، تقرأها وتُعيدها في كائن NSMutableData . يرجع tuple التي تحتوي على عدد بايت قراءة Address المكان الذي نشأت فيه البيانات.listen(forMessage buffer: UnsafeMutablePointer<CChar>, bufSize: Int, on port: Int, maxBacklogSize: Int = Socket.SOCKET_DEFAULT_MAX_BACKLOG) - تستمع هذه الوظيفة إلى بيانات datagram الواردة ، تقرأها وتُعيدها في كائن Data المُجارة. يرجع tuple التي تحتوي على عدد بايت قراءة 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 الذي تم تمريره. يرجع tuple التي تحتوي على عدد بايت قراءة Address المكان الذي نشأت فيه البيانات.readDatagram(into data: NSMutableData) - تقرأ هذه الوظيفة بيانات بيانات واردة وإرجاعها في كائن NSMutableData الذي تم تمريره. يرجع tuple التي تحتوي على عدد بايت قراءة Address المكان الذي نشأت فيه البيانات.readDatagram(into buffer: UnsafeMutablePointer<CChar>, bufSize: Int) - تقرأ هذه الوظيفة بيانات بيانات واردة وإرجاعها في كائن Data الذي تم تمريره. يرجع tuple التي تحتوي على عدد بايت قراءة Address المكان الذي نشأت فيه البيانات. إذا كانت كمية قراءة البيانات أكثر من أن يتم bufSize bufSize . سيتم التخلص من بقية قراءة البيانات.يوفر Bluesocket أيضًا أربع طرق لكتابة بيانات البيانات إلى مأخذ توصيل. هذه هي (في ترتيب الاستخدام الموصى به):
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 معين. عند الانتهاء بنجاح ، يتم إرجاع tuple التي تحتوي على hostname port .checkStatus(for sockets: [Socket]) - تتيح لك وظيفة الفئة هذه التحقق من حالة مجموعة من مثيلات Socket . عند الانتهاء ، يتم إرجاع tuple التي تحتوي على صفيفتين Socket . تحتوي المصفوفة الأولى على مثيلات Socket هي التي تحتوي على بيانات متاحة للقراءة والمصفوفة الثانية تحتوي على مثيلات Socket التي يمكن كتابتها إليها. لا يحظر API هذا. سيتحقق من حالة كل مثيل Socket ثم إرجاع النتائج.wait(for sockets: [Socket], timeout: UInt, waitForever: Bool = false) - تتيح هذه الوظيفة الفئة مراقبة مجموعة من مثيلات Socket ، في انتظار حدوث مهلة أو بيانات قابلة للقراءة في أحد مثيلات Socket التي تم مراقبتها. إذا تم تحديد مهلة صفر (0) ، فسيتحقق واجهة برمجة التطبيقات هذه كل مأخذ وعودة على الفور. خلاف ذلك ، سوف ينتظر حتى تنتهي صلاحية المهلة أو يمكن قراءة البيانات من واحد أو أكثر من مثيلات Socket المراقبة. في حالة حدوث مهلة ، nil API هذه. إذا كانت البيانات متوفرة على واحد أو أكثر من مثيلات Socket المراقبة ، فسيتم إرجاع هذه الحالات في صفيف. إذا تم تعيين علامة waitForever على TRUE ، فستنتظر الوظيفة إلى أجل غير مسمى حتى تصبح البيانات متاحة بغض النظر عن قيمة المهلة المحددة .createAddress(host: String, port: Int32) - تسمح وظيفة الفئة هذه بإنشاء تعداد Address إعطاء host port . عند النجاح ، تقوم هذه الوظيفة بإرجاع Address أو nil إذا لم يكن host محددًا موجودًا.isReadableOrWritable(waitForever: Bool = false, timeout: UInt = 0) - تسمح وظيفة المثيل هذه بتحديد ما إذا كان مثيل Socket قابل للقراءة و/أو قابلة للكتابة. يتم إرجاع tuple يحتوي على قيمتين Bool . الأول ، إذا كان صحيحًا ، يشير إلى أن مثيل Socket يحتوي على بيانات لقراءة ، والثاني ، إذا كان صحيحًا ، يشير إلى أنه يمكن كتابة مثيل Socket . waitForever إذا كان هذا صحيحًا ، يؤدي هذا الروتين إلى الانتظار حتى يصبح Socket إما قابلاً للقراءة أو قابلة للكتابة أو حدوث خطأ. إذا كان خطأ ، تحدد المعلمة timeout كم من الوقت للانتظار. إذا تم تحديد قيمة الصفر (0) لقيمة المهلة ، فستتحقق هذه الوظيفة من الحالة الحالية والعودة على الفور . تقوم هذه الوظيفة بإرجاع Tuple التي تحتوي على اثنين من المنطقيين ، الأول readable والثانية ، writable . يتم ضبطها على صحيح إذا كان Socket إما قابلاً للقراءة أو قابلة للكتابة. إذا لم يتم ضبط أي منهما على صحيح ، فقد حدثت مهلة. ملاحظة: إذا كنت تحاول الكتابة إلى مقبس متصل حديثًا ، فيجب عليك التأكد من أنه يمكن كتابته قبل محاولة العملية.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. يوضح المثال التالي كيفية إنشاء خادم صدى متعدد الخيوط بسيط نسبيًا باستخدام واجهة برمجة تطبيقات Dispatch 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
نحن نحب التحدث عن جانب الخادم السريع و Kitura. انضم إلى الركود لمقابلة الفريق!
هذه المكتبة مرخصة بموجب Apache 2.0. نص الترخيص الكامل متاح في الترخيص.