ตามเนื้อผ้าอุปกรณ์เชื่อมต่อสำหรับการสื่อสารแบบเพียร์ทูเพียร์เป็นสิ่งที่ต้องยกระดับหนัก แอปพลิเคชันต้องค้นพบสิ่งที่อยู่รอบตัวเปิดการเชื่อมต่อทั้งสองด้านจากนั้นดูแลรักษาให้เป็นโครงสร้างพื้นฐานเครือข่ายการเชื่อมต่อระยะทาง ฯลฯ การเปลี่ยนแปลงทั้งหมด ตระหนักถึงความยากลำบากที่มีอยู่ในกิจกรรมเหล่านี้ใน iOS 7 และ macOS 10.10 Apple ได้เปิดตัว MultipeerConnectivity framework (ต่อจากนี้ไป MPC) ออกแบบมาเพื่อให้แอปทำงานเหล่านี้ได้โดยใช้ความพยายามค่อนข้างต่ำ
MPC ดูแลโครงสร้างพื้นฐานที่จำเป็นส่วนใหญ่ที่นี่:
ในบทความนี้เราจะกล่าวถึงการใช้งาน iOS เป็นหลัก แต่ส่วนใหญ่ถ้าไม่ใช่ทั้งหมดนี้จะใช้ได้กับ macOS และ tvOS
วงจรชีวิตหลายเซสชัน:
MCNearbyServiceAdvertiser.startAdvertisingForPeers()
MCNearbyServiceBrowser.startBrowsingForPeers()
MCNearbyServiceBrowserDelegate.browser(:foundPeer:withDiscoveryInfo:)
MCNearbyServiceBrowser.invitePeer(...)
MCNearbyServiceAdvertiserDelegate.didReceiveInvitationFromPeer(...)
invitationHandler
ใน didReceiveInvitation
Create the MCSession
MCSession.send(...)
MCSessionDelegate.session(_:didReceive:data:peerID)
MCSession.disconnect()
กลับมาดูภาพนี้เป็นครั้งคราว
มีแบบฝึกหัดและตัวอย่าง MultipeerConnectivity มากมายที่ตั้งใจจะเดิน นักพัฒนา iOS ผ่านการใช้งานแอปพลิเคชันที่ใช้ MPC อย่างไรก็ตามจากประสบการณ์ของฉันพวกเขามักจะไม่สมบูรณ์และมีแนวโน้มที่จะเข้าใจปัญหาที่อาจสะดุดที่สำคัญบางอย่างกับ MPC ในบทความนี้ฉันหวังว่าทั้งสองจะแนะนำผู้อ่านผ่านการใช้งานแอปดังกล่าวโดยพื้นฐานและระบุพื้นที่ที่ฉันพบว่าติดขัดได้ง่าย
ความแตกต่าง s corp c corp
กนง. ขึ้นอยู่กับจำนวนชั้นเรียน เรามาดูรายชื่อสิ่งที่พบบ่อยและสร้างความเข้าใจเกี่ยวกับกรอบงาน
MCSession
- เซสชันจัดการการสื่อสารทั้งหมดระหว่างเพื่อนที่เกี่ยวข้อง คุณสามารถส่งข้อความไฟล์และสตรีมผ่านเซสชันและผู้รับมอบสิทธิ์จะได้รับแจ้งเมื่อได้รับสิ่งเหล่านี้จากเพียร์ที่เชื่อมต่อMCPeerID
- รหัสเพียร์ช่วยให้คุณระบุอุปกรณ์เพียร์แต่ละเครื่องภายในเซสชัน มีชื่อเชื่อมโยงอยู่ แต่โปรดระวัง: รหัสเพียร์ที่มีชื่อเดียวกันจะไม่ถือว่าเหมือนกัน (ดูกฎพื้นฐานด้านล่าง)MCNearbyServiceAdvertiser
- ผู้โฆษณาอนุญาตให้คุณเผยแพร่ชื่อบริการของคุณไปยังอุปกรณ์ใกล้เคียง สิ่งนี้ช่วยให้พวกเขาเชื่อมต่อกับคุณMCNearbyServiceBrowser
- เบราว์เซอร์ช่วยให้คุณค้นหาอุปกรณ์โดยใช้ MCNearbyServiceAdvertiser
การใช้สองคลาสนี้ร่วมกันช่วยให้คุณค้นพบอุปกรณ์ที่อยู่ใกล้เคียงและสร้างการเชื่อมต่อแบบเพียร์ทูเพียร์ของคุณMCBrowserViewController
- สิ่งนี้มี UI พื้นฐานสำหรับการเรียกดูบริการอุปกรณ์ใกล้เคียง (จำหน่ายผ่าน MCNearbyServiceAdvertiser
) แม้ว่าจะเหมาะสำหรับการใช้งานบางกรณี แต่เราจะไม่ใช้สิ่งนี้เนื่องจากจากประสบการณ์ของฉันสิ่งที่ดีที่สุดอย่างหนึ่งของ MCP คือความราบรื่นมีสองสิ่งที่ควรคำนึงถึงเมื่อสร้างเครือข่าย MPC:
ก่อนที่เราจะสร้างเครือข่ายได้เราจำเป็นต้องทำความสะอาดเล็กน้อยจากนั้นตั้งค่าคลาสผู้ลงโฆษณาและเบราว์เซอร์เพื่อค้นหาอุปกรณ์อื่น ๆ ที่เราสามารถสื่อสารด้วยได้ เรากำลังจะสร้างซิงเกิลตันที่เราจะใช้เพื่อเก็บตัวแปรสถานะสองสามตัว (MCPeerID ในพื้นที่ของเราและอุปกรณ์ที่เชื่อมต่อใด ๆ ) จากนั้นเราจะสร้าง MCNearbyServiceAdvertiser
และ MCNearbyServiceBrowser
. ออบเจ็กต์สองชิ้นสุดท้ายนี้ต้องมีประเภทบริการซึ่งเป็นเพียงสตริงที่ระบุแอปพลิเคชันของคุณ ต้องมีอักขระน้อยกว่า 16 ตัวและควรไม่ซ้ำกันมากที่สุด (เช่น“ MyApp-MyCo” ไม่ใช่“ Multipeer”) เราสามารถระบุพจนานุกรม (ขนาดเล็ก) ให้กับผู้ลงโฆษณาของเราได้มากกว่าที่เบราว์เซอร์จะอ่านได้เพื่อให้ข้อมูลเพิ่มเติมเล็กน้อยเมื่อดูอุปกรณ์ที่อยู่ใกล้เคียง (อาจเป็นประเภทเกมหรือบทบาทของอุปกรณ์)
เนื่องจาก MPC อาศัย API ที่ระบบให้มาและสัมพันธ์กับวัตถุในโลกแห่งความเป็นจริง (อุปกรณ์อื่น ๆ รวมถึง 'เครือข่าย' ที่ใช้ร่วมกันระหว่างอุปกรณ์เหล่านี้จึงเหมาะสำหรับรูปแบบซิงเกิลตัน แม้ว่าจะมีการใช้งานมากเกินไป แต่เสื้อกล้ามก็เหมาะสำหรับทรัพยากรที่ใช้ร่วมกันเช่นนี้
คำจำกัดความของซิงเกิลตันของเรามีดังนี้
class MPCManager: NSObject { var advertiser: MCNearbyServiceAdvertiser! var browser: MCNearbyServiceBrowser! static let instance = MPCManager() let localPeerID: MCPeerID let serviceType = 'MPC-Testing' var devices: [Device] = [] override init() { if let data = UserDefaults.standard.data(forKey: 'peerID'), let id = NSKeyedUnarchiver.unarchiveObject(with: data) as? MCPeerID { self.localPeerID = id } else { let peerID = MCPeerID(displayName: UIDevice.current.name) let data = try? NSKeyedArchiver.archivedData(withRootObject: peerID) UserDefaults.standard.set(data, forKey: 'peerID') self.localPeerID = peerID } super.init() self.advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: self.serviceType) self.advertiser.delegate = self self.browser = MCNearbyServiceBrowser(peer: localPeerID, serviceType: self.serviceType) self.browser.delegate = self } }
โปรดทราบว่าเรากำลังจัดเก็บ MCPeerID
ของเรา ในค่าเริ่มต้นของผู้ใช้ (โดยใช้ NSKeyedArchiver
) และใช้ซ้ำ ดังที่ได้กล่าวไว้ข้างต้นนี่เป็นสิ่งสำคัญและความล้มเหลวในการแคชไม่ทางใดก็ทางหนึ่งอาจทำให้เกิดข้อบกพร่องที่คลุมเครืออยู่ไกลออกไป
นี่คือคลาสอุปกรณ์ของเราซึ่งเราจะใช้เพื่อติดตามว่ามีการค้นพบอุปกรณ์ใดบ้างและสถานะของอุปกรณ์นั้นเป็นอย่างไร:
class Device: NSObject { let peerID: MCPeerID var session: MCSession? var name: String var state = MCSessionState.notConnected init(peerID: MCPeerID) { self.name = peerID.displayName self.peerID = peerID super.init() } func invite() { browser.invitePeer(self.peerID, to: self.session!, withContext: nil, timeout: 10) } }
ตอนนี้เราได้สร้างคลาสเริ่มต้นแล้วก็ได้เวลาย้อนกลับมาคิดถึงการทำงานร่วมกันระหว่างเบราว์เซอร์และผู้ลงโฆษณา ใน MPC อุปกรณ์สามารถโฆษณาบริการที่นำเสนอและสามารถเรียกดูบริการที่สนใจในอุปกรณ์อื่น ๆ เนื่องจากเราให้ความสำคัญกับการสื่อสารระหว่างอุปกรณ์โดยใช้เพียงแอปของเราเราจึงทั้งโฆษณาและเรียกดูบริการเดียวกัน
ในการกำหนดค่าไคลเอ็นต์ / เซิร์ฟเวอร์แบบดั้งเดิมอุปกรณ์หนึ่งเครื่อง (เซิร์ฟเวอร์) จะโฆษณาบริการของตนและไคลเอ็นต์จะเรียกดู เนื่องจากเรามีความเท่าเทียมกันเราจึงไม่ต้องการระบุบทบาทสำหรับอุปกรณ์ของเรา เราจะมีอุปกรณ์ทุกชิ้นทั้งโฆษณาและเรียกดู
เราต้องเพิ่มวิธีการให้กับ MPCManager
ของเรา เพื่อสร้างอุปกรณ์ตามที่ค้นพบและติดตามในอาร์เรย์อุปกรณ์ของเรา วิธีการของเราจะใช้ MCPeerID
ค้นหาอุปกรณ์ที่มีอยู่พร้อม ID นั้นและส่งคืนหากพบ หากเรายังไม่มีอุปกรณ์ที่มีอยู่เราจะสร้างอุปกรณ์ใหม่และเพิ่มลงในอาร์เรย์อุปกรณ์ของเรา
func device(for id: MCPeerID) -> Device { for device in self.devices { if device.peerID == id { return device } } let device = Device(peerID: id) self.devices.append(device) return device }
หลังจากอุปกรณ์เริ่มโฆษณาแล้วอุปกรณ์ท่องเว็บอื่นสามารถพยายามแนบเข้ากับอุปกรณ์นั้น เราจะต้องเพิ่มวิธีการมอบสิทธิ์ใน MPCSession
ของเรา ชั้นเรียนเพื่อจัดการสายเรียกเข้าจากผู้ลงโฆษณาของเราในกรณีนี้:
extension MPCManager: MCNearbyServiceAdvertiserDelegate { func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) { let device = MPCManager.instance.device(for: peerID) device.connect() invitationHandler(true, device.session) } }
…วิธีการบนอุปกรณ์ของเราในการสร้าง MCSession:
func connect() { if self.session != nil { return } self.session = MCSession(peer: MPCManager.instance.localPeerID, securityIdentity: nil, encryptionPreference: .required) self.session?.delegate = self }
…และในที่สุดก็เป็นวิธีการเรียกใช้คำเชิญเมื่อเบราว์เซอร์ของเราพบผู้ลงโฆษณา:
extension MPCManager: MCNearbyServiceBrowserDelegate { func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { let device = MPCManager.instance.device(for: peerID) device.invite(with: self.browser) }
ตอนนี้เราไม่สนใจ withDiscoveryInfo
การโต้เถียง; เราสามารถใช้สิ่งนี้เพื่อกรองอุปกรณ์เฉพาะตามสิ่งที่มีให้ (นี่คือพจนานุกรมเดียวกับที่เราให้ไว้ในอาร์กิวเมนต์ discoveryInfo
ถึง MCNearbyServiceAdvertiser
ด้านบน)
ขนาดอุปกรณ์สำหรับการออกแบบเว็บแบบตอบสนอง
ตอนนี้เราได้รับการดูแลทำความสะอาดทั้งหมดแล้วเราสามารถเริ่มต้นธุรกิจอุปกรณ์เชื่อมต่อที่แท้จริงได้
ในวิธีการเริ่มต้นของ MPCSession เราตั้งค่าทั้งผู้ลงโฆษณาและตัวแทนของเรา เมื่อเราพร้อมที่จะเริ่มเชื่อมต่อเราจะต้องเริ่มการเชื่อมต่อทั้งคู่ ซึ่งสามารถทำได้ในเมธอด didFinishLaunching ของตัวแทนแอปหรือเมื่อใดก็ตามที่เหมาะสม นี่คือ start()
วิธีที่เราจะเพิ่มในชั้นเรียนของเรา:
func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() }
การโทรเหล่านี้หมายความว่าแอปของคุณจะเริ่มเผยแพร่การแสดงตนผ่าน WiFi โปรดทราบว่าคุณไม่จำเป็นต้องเชื่อมต่อกับเครือข่าย WiFi เพื่อให้สามารถใช้งานได้ (แต่คุณต้องเปิดใช้งาน)
เมื่ออุปกรณ์ตอบกลับคำเชิญและเริ่ม MCSession อุปกรณ์จะเริ่มรับการโทรกลับของผู้ร่วมประชุมจากเซสชัน เราจะเพิ่มตัวจัดการสำหรับสิ่งเหล่านั้นในอุปกรณ์ของเรา ส่วนใหญ่เราจะละเลยในขณะนี้:
extension Device: MCSessionDelegate { public func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { self.state = state NotificationCenter.default.post(name: Multipeer.Notifications.deviceDidChangeState, object: self) } public func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { } public func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { } }
ในขณะนี้เราให้ความสำคัญกับ session(_:peer:didChangeState:)
เป็นหลัก โทรกลับ. สิ่งนี้จะถูกเรียกเมื่อใดก็ตามที่อุปกรณ์เปลี่ยนไปสู่สถานะใหม่ (notConnected
, connecting
และ connected
) เราต้องการติดตามสิ่งนี้เพื่อที่เราจะได้สร้างรายการอุปกรณ์ที่เชื่อมต่อทั้งหมด:
extension MPCManager { var connectedDevices: [Device] { return self.devices.filter { การสมรู้ร่วมคิด: Nearby Device Networking with MultipeerConnectivity ใน iOS
ตามเนื้อผ้าอุปกรณ์เชื่อมต่อสำหรับการสื่อสารแบบเพียร์ทูเพียร์เป็นสิ่งที่ต้องยกระดับหนัก แอปพลิเคชันต้องค้นพบสิ่งที่อยู่รอบตัวเปิดการเชื่อมต่อทั้งสองด้านจากนั้นดูแลรักษาให้เป็นโครงสร้างพื้นฐานเครือข่ายการเชื่อมต่อระยะทาง ฯลฯ การเปลี่ยนแปลงทั้งหมด ตระหนักถึงความยากลำบากที่มีอยู่ในกิจกรรมเหล่านี้ใน iOS 7 และ macOS 10.10 Apple ได้เปิดตัว MultipeerConnectivity framework (ต่อจากนี้ไป MPC) ออกแบบมาเพื่อให้แอปทำงานเหล่านี้ได้โดยใช้ความพยายามค่อนข้างต่ำ
MPC ดูแลโครงสร้างพื้นฐานที่จำเป็นส่วนใหญ่ที่นี่:
ในบทความนี้เราจะกล่าวถึงการใช้งาน iOS เป็นหลัก แต่ส่วนใหญ่ถ้าไม่ใช่ทั้งหมดนี้จะใช้ได้กับ macOS และ tvOS
วงจรชีวิตหลายเซสชัน:
MCNearbyServiceAdvertiser.startAdvertisingForPeers()
MCNearbyServiceBrowser.startBrowsingForPeers()
MCNearbyServiceBrowserDelegate.browser(:foundPeer:withDiscoveryInfo:)
MCNearbyServiceBrowser.invitePeer(...)
MCNearbyServiceAdvertiserDelegate.didReceiveInvitationFromPeer(...)
invitationHandler
ใน didReceiveInvitation
Create the MCSession
MCSession.send(...)
MCSessionDelegate.session(_:didReceive:data:peerID)
MCSession.disconnect()
กลับมาดูภาพนี้เป็นครั้งคราว
มีแบบฝึกหัดและตัวอย่าง MultipeerConnectivity มากมายที่ตั้งใจจะเดิน นักพัฒนา iOS ผ่านการใช้งานแอปพลิเคชันที่ใช้ MPC อย่างไรก็ตามจากประสบการณ์ของฉันพวกเขามักจะไม่สมบูรณ์และมีแนวโน้มที่จะเข้าใจปัญหาที่อาจสะดุดที่สำคัญบางอย่างกับ MPC ในบทความนี้ฉันหวังว่าทั้งสองจะแนะนำผู้อ่านผ่านการใช้งานแอปดังกล่าวโดยพื้นฐานและระบุพื้นที่ที่ฉันพบว่าติดขัดได้ง่าย
กนง. ขึ้นอยู่กับจำนวนชั้นเรียน เรามาดูรายชื่อสิ่งที่พบบ่อยและสร้างความเข้าใจเกี่ยวกับกรอบงาน
MCSession
- เซสชันจัดการการสื่อสารทั้งหมดระหว่างเพื่อนที่เกี่ยวข้อง คุณสามารถส่งข้อความไฟล์และสตรีมผ่านเซสชันและผู้รับมอบสิทธิ์จะได้รับแจ้งเมื่อได้รับสิ่งเหล่านี้จากเพียร์ที่เชื่อมต่อMCPeerID
- รหัสเพียร์ช่วยให้คุณระบุอุปกรณ์เพียร์แต่ละเครื่องภายในเซสชัน มีชื่อเชื่อมโยงอยู่ แต่โปรดระวัง: รหัสเพียร์ที่มีชื่อเดียวกันจะไม่ถือว่าเหมือนกัน (ดูกฎพื้นฐานด้านล่าง)MCNearbyServiceAdvertiser
- ผู้โฆษณาอนุญาตให้คุณเผยแพร่ชื่อบริการของคุณไปยังอุปกรณ์ใกล้เคียง สิ่งนี้ช่วยให้พวกเขาเชื่อมต่อกับคุณMCNearbyServiceBrowser
- เบราว์เซอร์ช่วยให้คุณค้นหาอุปกรณ์โดยใช้ MCNearbyServiceAdvertiser
การใช้สองคลาสนี้ร่วมกันช่วยให้คุณค้นพบอุปกรณ์ที่อยู่ใกล้เคียงและสร้างการเชื่อมต่อแบบเพียร์ทูเพียร์ของคุณMCBrowserViewController
- สิ่งนี้มี UI พื้นฐานสำหรับการเรียกดูบริการอุปกรณ์ใกล้เคียง (จำหน่ายผ่าน MCNearbyServiceAdvertiser
) แม้ว่าจะเหมาะสำหรับการใช้งานบางกรณี แต่เราจะไม่ใช้สิ่งนี้เนื่องจากจากประสบการณ์ของฉันสิ่งที่ดีที่สุดอย่างหนึ่งของ MCP คือความราบรื่นมีสองสิ่งที่ควรคำนึงถึงเมื่อสร้างเครือข่าย MPC:
ก่อนที่เราจะสร้างเครือข่ายได้เราจำเป็นต้องทำความสะอาดเล็กน้อยจากนั้นตั้งค่าคลาสผู้ลงโฆษณาและเบราว์เซอร์เพื่อค้นหาอุปกรณ์อื่น ๆ ที่เราสามารถสื่อสารด้วยได้ เรากำลังจะสร้างซิงเกิลตันที่เราจะใช้เพื่อเก็บตัวแปรสถานะสองสามตัว (MCPeerID ในพื้นที่ของเราและอุปกรณ์ที่เชื่อมต่อใด ๆ ) จากนั้นเราจะสร้าง MCNearbyServiceAdvertiser
และ MCNearbyServiceBrowser
. ออบเจ็กต์สองชิ้นสุดท้ายนี้ต้องมีประเภทบริการซึ่งเป็นเพียงสตริงที่ระบุแอปพลิเคชันของคุณ ต้องมีอักขระน้อยกว่า 16 ตัวและควรไม่ซ้ำกันมากที่สุด (เช่น“ MyApp-MyCo” ไม่ใช่“ Multipeer”) เราสามารถระบุพจนานุกรม (ขนาดเล็ก) ให้กับผู้ลงโฆษณาของเราได้มากกว่าที่เบราว์เซอร์จะอ่านได้เพื่อให้ข้อมูลเพิ่มเติมเล็กน้อยเมื่อดูอุปกรณ์ที่อยู่ใกล้เคียง (อาจเป็นประเภทเกมหรือบทบาทของอุปกรณ์)
เนื่องจาก MPC อาศัย API ที่ระบบให้มาและสัมพันธ์กับวัตถุในโลกแห่งความเป็นจริง (อุปกรณ์อื่น ๆ รวมถึง 'เครือข่าย' ที่ใช้ร่วมกันระหว่างอุปกรณ์เหล่านี้จึงเหมาะสำหรับรูปแบบซิงเกิลตัน แม้ว่าจะมีการใช้งานมากเกินไป แต่เสื้อกล้ามก็เหมาะสำหรับทรัพยากรที่ใช้ร่วมกันเช่นนี้
คำจำกัดความของซิงเกิลตันของเรามีดังนี้
class MPCManager: NSObject { var advertiser: MCNearbyServiceAdvertiser! var browser: MCNearbyServiceBrowser! static let instance = MPCManager() let localPeerID: MCPeerID let serviceType = 'MPC-Testing' var devices: [Device] = [] override init() { if let data = UserDefaults.standard.data(forKey: 'peerID'), let id = NSKeyedUnarchiver.unarchiveObject(with: data) as? MCPeerID { self.localPeerID = id } else { let peerID = MCPeerID(displayName: UIDevice.current.name) let data = try? NSKeyedArchiver.archivedData(withRootObject: peerID) UserDefaults.standard.set(data, forKey: 'peerID') self.localPeerID = peerID } super.init() self.advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: self.serviceType) self.advertiser.delegate = self self.browser = MCNearbyServiceBrowser(peer: localPeerID, serviceType: self.serviceType) self.browser.delegate = self } }
โปรดทราบว่าเรากำลังจัดเก็บ MCPeerID
ของเรา ในค่าเริ่มต้นของผู้ใช้ (โดยใช้ NSKeyedArchiver
) และใช้ซ้ำ ดังที่ได้กล่าวไว้ข้างต้นนี่เป็นสิ่งสำคัญและความล้มเหลวในการแคชไม่ทางใดก็ทางหนึ่งอาจทำให้เกิดข้อบกพร่องที่คลุมเครืออยู่ไกลออกไป
นี่คือคลาสอุปกรณ์ของเราซึ่งเราจะใช้เพื่อติดตามว่ามีการค้นพบอุปกรณ์ใดบ้างและสถานะของอุปกรณ์นั้นเป็นอย่างไร:
class Device: NSObject { let peerID: MCPeerID var session: MCSession? var name: String var state = MCSessionState.notConnected init(peerID: MCPeerID) { self.name = peerID.displayName self.peerID = peerID super.init() } func invite() { browser.invitePeer(self.peerID, to: self.session!, withContext: nil, timeout: 10) } }
ตอนนี้เราได้สร้างคลาสเริ่มต้นแล้วก็ได้เวลาย้อนกลับมาคิดถึงการทำงานร่วมกันระหว่างเบราว์เซอร์และผู้ลงโฆษณา ใน MPC อุปกรณ์สามารถโฆษณาบริการที่นำเสนอและสามารถเรียกดูบริการที่สนใจในอุปกรณ์อื่น ๆ เนื่องจากเราให้ความสำคัญกับการสื่อสารระหว่างอุปกรณ์โดยใช้เพียงแอปของเราเราจึงทั้งโฆษณาและเรียกดูบริการเดียวกัน
ในการกำหนดค่าไคลเอ็นต์ / เซิร์ฟเวอร์แบบดั้งเดิมอุปกรณ์หนึ่งเครื่อง (เซิร์ฟเวอร์) จะโฆษณาบริการของตนและไคลเอ็นต์จะเรียกดู เนื่องจากเรามีความเท่าเทียมกันเราจึงไม่ต้องการระบุบทบาทสำหรับอุปกรณ์ของเรา เราจะมีอุปกรณ์ทุกชิ้นทั้งโฆษณาและเรียกดู
เราต้องเพิ่มวิธีการให้กับ MPCManager
ของเรา เพื่อสร้างอุปกรณ์ตามที่ค้นพบและติดตามในอาร์เรย์อุปกรณ์ของเรา วิธีการของเราจะใช้ MCPeerID
ค้นหาอุปกรณ์ที่มีอยู่พร้อม ID นั้นและส่งคืนหากพบ หากเรายังไม่มีอุปกรณ์ที่มีอยู่เราจะสร้างอุปกรณ์ใหม่และเพิ่มลงในอาร์เรย์อุปกรณ์ของเรา
func device(for id: MCPeerID) -> Device { for device in self.devices { if device.peerID == id { return device } } let device = Device(peerID: id) self.devices.append(device) return device }
หลังจากอุปกรณ์เริ่มโฆษณาแล้วอุปกรณ์ท่องเว็บอื่นสามารถพยายามแนบเข้ากับอุปกรณ์นั้น เราจะต้องเพิ่มวิธีการมอบสิทธิ์ใน MPCSession
ของเรา ชั้นเรียนเพื่อจัดการสายเรียกเข้าจากผู้ลงโฆษณาของเราในกรณีนี้:
extension MPCManager: MCNearbyServiceAdvertiserDelegate { func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) { let device = MPCManager.instance.device(for: peerID) device.connect() invitationHandler(true, device.session) } }
…วิธีการบนอุปกรณ์ของเราในการสร้าง MCSession:
func connect() { if self.session != nil { return } self.session = MCSession(peer: MPCManager.instance.localPeerID, securityIdentity: nil, encryptionPreference: .required) self.session?.delegate = self }
…และในที่สุดก็เป็นวิธีการเรียกใช้คำเชิญเมื่อเบราว์เซอร์ของเราพบผู้ลงโฆษณา:
extension MPCManager: MCNearbyServiceBrowserDelegate { func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { let device = MPCManager.instance.device(for: peerID) device.invite(with: self.browser) }
ตอนนี้เราไม่สนใจ withDiscoveryInfo
การโต้เถียง; เราสามารถใช้สิ่งนี้เพื่อกรองอุปกรณ์เฉพาะตามสิ่งที่มีให้ (นี่คือพจนานุกรมเดียวกับที่เราให้ไว้ในอาร์กิวเมนต์ discoveryInfo
ถึง MCNearbyServiceAdvertiser
ด้านบน)
ตอนนี้เราได้รับการดูแลทำความสะอาดทั้งหมดแล้วเราสามารถเริ่มต้นธุรกิจอุปกรณ์เชื่อมต่อที่แท้จริงได้
ในวิธีการเริ่มต้นของ MPCSession เราตั้งค่าทั้งผู้ลงโฆษณาและตัวแทนของเรา เมื่อเราพร้อมที่จะเริ่มเชื่อมต่อเราจะต้องเริ่มการเชื่อมต่อทั้งคู่ ซึ่งสามารถทำได้ในเมธอด didFinishLaunching ของตัวแทนแอปหรือเมื่อใดก็ตามที่เหมาะสม นี่คือ start()
วิธีที่เราจะเพิ่มในชั้นเรียนของเรา:
func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() }
การโทรเหล่านี้หมายความว่าแอปของคุณจะเริ่มเผยแพร่การแสดงตนผ่าน WiFi โปรดทราบว่าคุณไม่จำเป็นต้องเชื่อมต่อกับเครือข่าย WiFi เพื่อให้สามารถใช้งานได้ (แต่คุณต้องเปิดใช้งาน)
เมื่ออุปกรณ์ตอบกลับคำเชิญและเริ่ม MCSession อุปกรณ์จะเริ่มรับการโทรกลับของผู้ร่วมประชุมจากเซสชัน เราจะเพิ่มตัวจัดการสำหรับสิ่งเหล่านั้นในอุปกรณ์ของเรา ส่วนใหญ่เราจะละเลยในขณะนี้:
extension Device: MCSessionDelegate { public func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { self.state = state NotificationCenter.default.post(name: Multipeer.Notifications.deviceDidChangeState, object: self) } public func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { } public func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { } }
ในขณะนี้เราให้ความสำคัญกับ session(_:peer:didChangeState:)
เป็นหลัก โทรกลับ. สิ่งนี้จะถูกเรียกเมื่อใดก็ตามที่อุปกรณ์เปลี่ยนไปสู่สถานะใหม่ (notConnected
, connecting
และ connected
) เราต้องการติดตามสิ่งนี้เพื่อที่เราจะได้สร้างรายการอุปกรณ์ที่เชื่อมต่อทั้งหมด:
extension MPCManager { var connectedDevices: [Device] { return self.devices.filter { $0.state == .connected } } }
ตอนนี้เราได้เชื่อมต่ออุปกรณ์ทั้งหมดแล้วก็ได้เวลาเริ่มส่งข้อความไปมา MPC เสนอสามทางเลือกในเรื่องนี้:
เพื่อความเรียบง่ายเราจะดูตัวเลือกแรกเท่านั้น เราจะส่งข้อความธรรมดา ๆ กลับไปกลับมาและไม่ต้องกังวลมากเกินไปเกี่ยวกับความซับซ้อนของประเภทข้อความการจัดรูปแบบ ฯลฯ เราจะใช้โครงสร้าง Codable ในการห่อหุ้มข้อความของเราซึ่งจะมีลักษณะดังนี้:
struct Message: Codable { let body: String }
นอกจากนี้เราจะเพิ่มส่วนขยายให้กับอุปกรณ์เพื่อส่งสิ่งเหล่านี้:
extension Device { func send(text: String) throws { let message = Message(body: text) let payload = try JSONEncoder().encode(message) try self.session?.send(payload, toPeers: [self.peerID], with: .reliable) } } ~~~swift Finally, we'll need to modify our `Device.session(_:didReceive:fromPeer)` code to receive the message, parse it, and notify any interested objects about it:
static let messageReceivedNotification = Notification.Name (“ DeviceDidReceiveMessage”) เซสชัน func สาธารณะ (_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {if let message = try? JSONDecoder (). ถอดรหัส (Message.self, from: data) {NotificationCenter.default.post (name: Device.messageReceivedNotification, object: message, userInfo: [“ from”: self])}}
## Disconnections Now that we've got a connection created between multiple devices, we have to be able to both disconnect on demand and also handle system interruptions. One of the undocumented weaknesses of MPC is that it doesn't function in the background. We need to observe the `UIApplication.didEnterBackgroundNotification` notification, and make sure that we shut down all our sessions. Failure to do this will lead to undefined states in the sessions and devices and can cause lots of confusing, hard-to-track-down errors. There is a temptation to use a background task to keep your sessions around, in case the user jumps back into your app. However, this is a bad idea, as MPC will usually fail within the first second of being backgrounded. When your app returns to the foreground, you can rely on MPC's delegate methods to rebuild your connections. In our MPCSession's `start()` method, we'll want to observe this notification and add code to handle it and shut down all our sessions. ~~~swift func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: Notification.Name.UIApplicationDidEnterBackground, object: nil) } @objc func enteredBackground() { for device in self.devices { device.disconnect() } } func disconnect() { self.session?.disconnect() self.session = nil }
บทความนี้ครอบคลุมถึงสถาปัตยกรรมที่จำเป็นในการสร้างส่วนประกอบเครือข่ายของแอปพลิเคชันที่ใช้ MultipeerConnectivity ซอร์สโค้ดแบบเต็ม (มีอยู่ใน Github) มีตัวห่ออินเทอร์เฟซผู้ใช้ขั้นต่ำที่ช่วยให้คุณดูอุปกรณ์ที่เชื่อมต่อและส่งข้อความระหว่างกันได้
MPC นำเสนอการเชื่อมต่อที่ราบรื่นระหว่างอุปกรณ์ใกล้เคียงโดยไม่ต้องกังวลเกี่ยวกับเครือข่าย WiFi บลูทู ธ หรือยิมนาสติกไคลเอนต์ / เซิร์ฟเวอร์ที่ซับซ้อน ความสามารถในการจับคู่โทรศัพท์สองสามเครื่องอย่างรวดเร็วสำหรับเซสชันการเล่นเกมสั้น ๆ หรือเชื่อมต่ออุปกรณ์สองเครื่องเพื่อใช้งานร่วมกันนั้นทำได้ตามแบบฉบับของ Apple
ซอร์สโค้ดสำหรับโปรเจ็กต์นี้มีอยู่ใน Github ที่ https://github.com/bengottlieb/MultipeerExample .
ออกแบบ iOS ที่ใช้ AFNetworking? รูปแบบการออกแบบ Model-View-Controller (MVC) นั้นยอดเยี่ยมสำหรับ codebase ที่มีการบำรุงรักษา แต่บางครั้งคุณต้องใช้คลาสเดียวเพื่อจัดการระบบเครือข่ายของคุณเนื่องจากข้อกังวลเช่นรหัส DRY การบันทึกเครือข่ายแบบรวมศูนย์และโดยเฉพาะอย่างยิ่งการ จำกัด อัตรา อ่านทั้งหมดเกี่ยวกับการจัดการสิ่งนี้ด้วยคลาส Singleton ใน iOS Centralized and Decoupled Networking: AFNetworking Tutorial พร้อมคลาส Singleton
แอปในเครือข่ายที่สามารถเชื่อมต่อกับอินสแตนซ์อื่น ๆ (เพียร์) โดยไม่ต้องใช้เซิร์ฟเวอร์หรือตัวกลางอื่น ๆ
หากข้อมูลของคุณมีความละเอียดอ่อน (เช่นไฟล์ที่แชร์) คุณอาจต้องการป้องกันไม่ให้ผู้อื่นดักจับข้อมูลได้ อย่างไรก็ตามสำหรับแอปพลิเคชันแบบเพียร์ทูเพียร์จำนวนมากไม่จำเป็นต้องใช้ (คิดว่า: การเคลื่อนไหวของเกมหรือคำแนะนำง่ายๆ)
เมื่อเชื่อมต่ออุปกรณ์สองเครื่องจะสื่อสารกันผ่านข้อความที่ส่งไปมา
ในเครือข่ายไคลเอนต์เซิร์ฟเวอร์แบบดั้งเดิมโครงสร้างพื้นฐานประกอบด้วยเซิร์ฟเวอร์และอุปกรณ์เชื่อมต่อ (เช่นสถานีฐาน WiFi) ในรูปแบบเพียร์ทูเพียร์สามารถลดขนาดเป็นเครือข่ายที่ประกอบด้วยอุปกรณ์เท่านั้นและการเชื่อมต่อแบบเฉพาะกิจ (ไม่มีฮาร์ดแวร์ภายนอกมีเพียงวิทยุของอุปกรณ์เท่านั้น)
การสื่อสารแบบเพียร์ทูเพียร์เกี่ยวข้องกับอุปกรณ์บนเครือข่ายที่พูดคุยกันโดยตรงโดยไม่ต้องผ่านเซิร์ฟเวอร์ตัวกลาง
เครือข่ายเพียร์ทูเพียร์สามารถตั้งค่าได้ทุกที่ที่มีอุปกรณ์ที่มีความสามารถสองเครื่องขึ้นไปโดยไม่ต้องเกี่ยวข้องกับเซิร์ฟเวอร์ที่สามซึ่งอาจไม่สามารถเข้าถึงได้ ตัวอย่างเช่นเมื่อไม่มีการเชื่อมต่อกับอินเทอร์เน็ต แต่อุปกรณ์ต่างๆยังสามารถมองเห็นกันและกันได้
MPC ดูแลงานการตั้งค่าและการบำรุงรักษาที่สำคัญจำนวนมากเช่นการค้นหาเพื่อนและการสื่อสารระดับต่ำ ช่วยให้นักพัฒนาซอฟต์แวร์ประหยัดเวลาและความพยายามได้มากโดยการจัดการฟังก์ชันทั่วไปสำหรับพวกเขา
มีจุดอ่อนบางประการที่ไม่ได้รับการบันทึกไว้อย่างดีซึ่งหากไม่ได้รับการป้องกันอาจทำให้เกิดปัญหาในการเชื่อมต่อ นอกจากนี้ยังมีการสูญเสียความยืดหยุ่นโดยทั่วไปที่มาพร้อมกับกรอบวัตถุประสงค์ทั่วไป อย่างไรก็ตามสำหรับการใช้งานส่วนใหญ่ MPC นั้นเหมาะสมอย่างยิ่ง
ตอนนี้เราได้เชื่อมต่ออุปกรณ์ทั้งหมดแล้วก็ได้เวลาเริ่มส่งข้อความไปมา MPC เสนอสามทางเลือกในเรื่องนี้:
เพื่อความเรียบง่ายเราจะดูตัวเลือกแรกเท่านั้น เราจะส่งข้อความธรรมดา ๆ กลับไปกลับมาและไม่ต้องกังวลมากเกินไปเกี่ยวกับความซับซ้อนของประเภทข้อความการจัดรูปแบบ ฯลฯ เราจะใช้โครงสร้าง Codable ในการห่อหุ้มข้อความของเราซึ่งจะมีลักษณะดังนี้:
struct Message: Codable { let body: String }
นอกจากนี้เราจะเพิ่มส่วนขยายให้กับอุปกรณ์เพื่อส่งสิ่งเหล่านี้:
extension Device { func send(text: String) throws { let message = Message(body: text) let payload = try JSONEncoder().encode(message) try self.session?.send(payload, toPeers: [self.peerID], with: .reliable) } } ~~~swift Finally, we'll need to modify our `Device.session(_:didReceive:fromPeer)` code to receive the message, parse it, and notify any interested objects about it:
static let messageReceivedNotification = Notification.Name (“ DeviceDidReceiveMessage”) เซสชัน func สาธารณะ (_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {if let message = try? JSONDecoder (). ถอดรหัส (Message.self, from: data) {NotificationCenter.default.post (name: Device.messageReceivedNotification, object: message, userInfo: [“ from”: self])}}
## Disconnections Now that we've got a connection created between multiple devices, we have to be able to both disconnect on demand and also handle system interruptions. One of the undocumented weaknesses of MPC is that it doesn't function in the background. We need to observe the `UIApplication.didEnterBackgroundNotification` notification, and make sure that we shut down all our sessions. Failure to do this will lead to undefined states in the sessions and devices and can cause lots of confusing, hard-to-track-down errors. There is a temptation to use a background task to keep your sessions around, in case the user jumps back into your app. However, this is a bad idea, as MPC will usually fail within the first second of being backgrounded. When your app returns to the foreground, you can rely on MPC's delegate methods to rebuild your connections. In our MPCSession's `start()` method, we'll want to observe this notification and add code to handle it and shut down all our sessions. ~~~swift func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: Notification.Name.UIApplicationDidEnterBackground, object: nil) } @objc func enteredBackground() { for device in self.devices { device.disconnect() } } func disconnect() { self.session?.disconnect() self.session = nil }
บทความนี้ครอบคลุมถึงสถาปัตยกรรมที่จำเป็นในการสร้างส่วนประกอบเครือข่ายของแอปพลิเคชันที่ใช้ MultipeerConnectivity ซอร์สโค้ดแบบเต็ม (มีอยู่ใน Github) มีตัวห่ออินเทอร์เฟซผู้ใช้ขั้นต่ำที่ช่วยให้คุณดูอุปกรณ์ที่เชื่อมต่อและส่งข้อความระหว่างกันได้
MPC นำเสนอการเชื่อมต่อที่ราบรื่นระหว่างอุปกรณ์ใกล้เคียงโดยไม่ต้องกังวลเกี่ยวกับเครือข่าย WiFi บลูทู ธ หรือยิมนาสติกไคลเอนต์ / เซิร์ฟเวอร์ที่ซับซ้อน ความสามารถในการจับคู่โทรศัพท์สองสามเครื่องอย่างรวดเร็วสำหรับเซสชันการเล่นเกมสั้น ๆ หรือเชื่อมต่ออุปกรณ์สองเครื่องเพื่อใช้งานร่วมกันนั้นทำได้ตามแบบฉบับของ Apple
ซอร์สโค้ดสำหรับโปรเจ็กต์นี้มีอยู่ใน Github ที่ https://github.com/bengottlieb/MultipeerExample .
ออกแบบ iOS ที่ใช้ AFNetworking? รูปแบบการออกแบบ Model-View-Controller (MVC) นั้นยอดเยี่ยมสำหรับ codebase ที่มีการบำรุงรักษา แต่บางครั้งคุณต้องใช้คลาสเดียวเพื่อจัดการระบบเครือข่ายของคุณเนื่องจากข้อกังวลเช่นรหัส DRY การบันทึกเครือข่ายแบบรวมศูนย์และโดยเฉพาะอย่างยิ่งการ จำกัด อัตรา อ่านทั้งหมดเกี่ยวกับการจัดการสิ่งนี้ด้วยคลาส Singleton ใน iOS Centralized and Decoupled Networking: AFNetworking Tutorial พร้อมคลาส Singleton
แอปในเครือข่ายที่สามารถเชื่อมต่อกับอินสแตนซ์อื่น ๆ (เพียร์) โดยไม่ต้องใช้เซิร์ฟเวอร์หรือตัวกลางอื่น ๆ
หากข้อมูลของคุณมีความละเอียดอ่อน (เช่นไฟล์ที่แชร์) คุณอาจต้องการป้องกันไม่ให้ผู้อื่นดักจับข้อมูลได้ อย่างไรก็ตามสำหรับแอปพลิเคชันแบบเพียร์ทูเพียร์จำนวนมากไม่จำเป็นต้องใช้ (คิดว่า: การเคลื่อนไหวของเกมหรือคำแนะนำง่ายๆ)
เมื่อเชื่อมต่ออุปกรณ์สองเครื่องจะสื่อสารกันผ่านข้อความที่ส่งไปมา
ในเครือข่ายไคลเอนต์เซิร์ฟเวอร์แบบดั้งเดิมโครงสร้างพื้นฐานประกอบด้วยเซิร์ฟเวอร์และอุปกรณ์เชื่อมต่อ (เช่นสถานีฐาน WiFi) ในรูปแบบเพียร์ทูเพียร์สามารถลดขนาดเป็นเครือข่ายที่ประกอบด้วยอุปกรณ์เท่านั้นและการเชื่อมต่อแบบเฉพาะกิจ (ไม่มีฮาร์ดแวร์ภายนอกมีเพียงวิทยุของอุปกรณ์เท่านั้น)
การสื่อสารแบบเพียร์ทูเพียร์เกี่ยวข้องกับอุปกรณ์บนเครือข่ายที่พูดคุยกันโดยตรงโดยไม่ต้องผ่านเซิร์ฟเวอร์ตัวกลาง
bootstrap ในการออกแบบเว็บคืออะไร
เครือข่ายเพียร์ทูเพียร์สามารถตั้งค่าได้ทุกที่ที่มีอุปกรณ์ที่มีความสามารถสองเครื่องขึ้นไปโดยไม่ต้องเกี่ยวข้องกับเซิร์ฟเวอร์ที่สามซึ่งอาจไม่สามารถเข้าถึงได้ ตัวอย่างเช่นเมื่อไม่มีการเชื่อมต่อกับอินเทอร์เน็ต แต่อุปกรณ์ต่างๆยังสามารถมองเห็นกันและกันได้
MPC ดูแลงานการตั้งค่าและการบำรุงรักษาที่สำคัญจำนวนมากเช่นการค้นหาเพื่อนและการสื่อสารระดับต่ำ ช่วยให้นักพัฒนาซอฟต์แวร์ประหยัดเวลาและความพยายามได้มากโดยการจัดการฟังก์ชันทั่วไปสำหรับพวกเขา
มีจุดอ่อนบางประการที่ไม่ได้รับการบันทึกไว้อย่างดีซึ่งหากไม่ได้รับการป้องกันอาจทำให้เกิดปัญหาในการเชื่อมต่อ นอกจากนี้ยังมีการสูญเสียความยืดหยุ่นโดยทั่วไปที่มาพร้อมกับกรอบวัตถุประสงค์ทั่วไป อย่างไรก็ตามสำหรับการใช้งานส่วนใหญ่ MPC นั้นเหมาะสมอย่างยิ่ง