إطار عمل الاتصال المتعدد - يبقى النظير المفقود في الجلسة
-
21-12-2019 - |
سؤال
أتساءل عما إذا كان إطار عمل Multipeer Connectivity هذا جاهزًا للاستخدام في العالم الحقيقي، نظرًا لجميع الأخطاء التي واجهها المجتمع.أعتقد أنني قمت بإعداده بشكل صحيح، ولكن جميع نماذج المشاريع الأخرى التي جربتها واجهت مشكلات مماثلة.
قد تكون المشكلة التي أواجهها مرتبطة ببعض المشكلات المتأصلة في Bonjour أو شيء من هذا القبيل، لا أستطيع معرفة ذلك، ولكن المشكلة في الأساس هي كما يلي:
- عندي نشط
MCSession
مع عدد من أقرانه. - الآن، إذا كان الجهاز في جلسة عمل، وبعد ذلك القوة تنتهي, ، أن "النظير" يظل متصلاً لفترة غير محددة من الوقت.
- لا يوجد شيء يمكنني القيام به لإجبار هذا المستخدم على الخروج، على الرغم من أن
browser:lostPeer:
يتم استدعاء الطريقة لهذا الأقران ولم يعد يظهر في المتصفح على أنه "قريب". - ال
session:peer:didChangeState:
لا يتم استدعاء الأسلوب لهذا النظير. - عندما يعود ذلك النظير الذي تركته القوة إلى التطبيق، يتم "العثور عليه" مرة أخرى بواسطة ملف
browser:foundPeer:withDiscoveryInfo:
ولكنها لا تزال موجودة أيضًا فيsession.connectedPeers
NSArray.من الواضح أنهم لا يتلقون أي بيانات أو تحديثات حول الجلسة ولا يزالون غير متصلين فعليًا. - الشيء الوحيد الذي يبدو أنه يعمل على تسجيل ذلك النظير الأصلي باسم
MCSessionStateNotConnected
إلى الجلسة عن طريق إعادة توصيل هذا النظير بالجلسة الأصلية.ثم هناك مكالمة مكررة لsession:peer:didChangeState:
حيث يوجد المثيل الجديد لمعرف النظيرMCSessionStateConnected
وبعد فترة وجيزة من استدعاء المثيل القديم لمعرف النظير باستخدامMCSessionStateNotConnected
.
يوضح نموذج تطبيق الدردشة هذه المشكلة جيدًا: https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Introduction/Intro.html
نظرًا لعدم وجود أي طريقة لفرض إزالة أحد الأقران يدويًا من الجلسة، فماذا علي أن أفعل؟هل يجب أن أحاول إعادة بناء الجلسة بطريقة ما؟
يبدو هذا الإطار وكأنه في حالة من الفوضى بعض الشيء، لكنني أحاول الاحتفاظ بالحكم!
المحلول
كان الحل الوحيد لهذا النوع من المشكلات هو وجود علاقة 1-1 بين الجلسات والأقران.إنه يعقد إرسال عمليات البث، لكنه يسمح على الأقل بقطع الاتصال على مستوى النظير والتنظيف من خلال قطع الاتصال/إزالة الجلسة نفسها.
تحديث
لتوضيح إجابتي الأصلية، لكي تتمكن من إرسال البيانات إلى الأقران المتصلين، من الضروري الاحتفاظ بمرجع للجلسة التي تم إنشاؤها لكل نظير.لقد كنت أستخدم قاموسًا قابلاً للتغيير لهذا الغرض.
بمجرد إرسال/قبول الدعوة بجلسة جديدة، استخدم MCSession
طريقة التفويض لتحديث القاموس:
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
if (state==MCSessionStateConnected){
_myPeerSessions[peerID.displayName] = session;
}
else if (state==MCSessionStateNotConnected){
//This is where the session can be disconnected without
//affecting other peers
[session disconnect];
[_myPeerSessions removeObjectForKey:peerID.displayName];
}
}
يمكن الوصول إلى جميع الأقران باستخدام طريقة تُرجع جميع قيم القاموس، وبالتالي جميع القيم connectedPeers
(في هذه الحالة واحد) لكل منهما MCSession
:
- (NSArray *)allConnectedPeers {
return [[_myPeerSessions allValues] valueForKey:@"connectedPeers"];
}
يمكن إرسال البيانات إلى نظير معين أو عبر البث بطريقة مثل هذه:
- (void)sendData:(NSData *)data toPeerIDs:(NSArray *)remotePeers reliable:(BOOL)reliable error:(NSError *__autoreleasing *)error {
MCSessionSendDataMode mode = (reliable) ? MCSessionSendDataReliable : MCSessionSendDataUnreliable;
for (MCPeerID *peer in remotePeers){
NSError __autoreleasing *currentError = nil;
MCSession *session = _myPeerSessions[peer.displayName];
[session sendData:data toPeers:session.connectedPeers withMode:mode error:currentError];
if (currentError && !error)
*error = *currentError;
}
}
نصائح أخرى
هل حاولت قطع اتصال الجلسة قبل إغلاق التطبيق؟يجب أن يؤدي هذا إلى إزالة النظير من الجلسة بشكل صحيح وتنظيف أي موارد مخصصة للنظير.
على وجه التحديد أعني شيئا من هذا القبيل [self.peer disconnect]
في applicationWillTerminate:
لقد كنت أواجه مشاكل مماثلة.يبدو أنه إذا قمت بتشغيل تطبيقي على أحد أجهزة iOS، وقمت بالاتصال بجهاز آخر، ثم قمت بإنهاء التطبيق وإعادة تشغيله (على سبيل المثال، عند إعادة التشغيل من Xcode)، فأنا في موقف حيث أتلقى رسالة "متصل" ثم "غير متصل" الرسالة بعد قليل.كان هذا يطردني.لكن بالنظر بعناية أكبر، أستطيع أن أرى أن الرسالة "غير متصل" مخصصة في الواقع لمعرف نظير مختلف عن الذي تم الاتصال به.
أعتقد أن المشكلة هنا هي أن معظم العينات التي رأيتها تهتم فقط باسم العرض الخاص بمعرف النظير، وتهمل حقيقة أنه يمكنك الحصول على معرفات نظير متعددة لنفس الجهاز/اسم العرض.
أقوم الآن بالتحقق من اسم العرض أولاً ثم التحقق من أن معرف النظير هو نفسه عن طريق مقارنة المؤشرات.
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
MyPlayer *player = _players[peerID.displayName];
if ((state == MCSessionStateNotConnected) &&
(peerID != player.peerID)) {
NSLog(@"remnant connection drop");
return; // note that I don't care if player is nil, since I don't want to
// add a dictionary object for a Not Connecting peer.
}
if (player == nil) {
player = [MyPlayer init];
player.peerID = peerID;
_players[peerID.displayName] = player;
}
player.state = state;
...
لم أتمكن من الحصول على الإجابة المقبولة للعمل على الإطلاق، لذا ما فعلته بدلاً من ذلك هو وجود مؤقت يعمل على إعادة تعيين الاتصال عندما يبلغ المتصفح عن عدم الاتصال وعدم وجود أقران آخرين متصلين.
-(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{
//DebugLog(@"session didChangeState: %ld",state);
if(resetTimer != nil){
[resetTimer invalidate];
resetTimer = nil;
}
if(state == MCSessionStateNotConnected){
[session disconnect];
[peerSessions removeObjectForKey:peerID.displayName];
[self removeGuidyPeerWithPeerID:peerID];
//DebugLog(@"removing all guides from peer %@",peerID);
if([localSession connectedPeers].count == 0){
DebugLog(@"nothing found... maybe restart in 3 seconds");
dispatch_async(dispatch_get_main_queue(), ^{
resetTimer = [NSTimer
scheduledTimerWithTimeInterval:3.0
target:self selector:@selector(onResetTimer:)
userInfo:nil
repeats:NO];
}
);
}
}
...
}
يمكنك حذف النظير من MCBrowserViewController بالكود التالي في Swift 3:
self.mySession.cancelConnectPeer(self.myPeerID)