Изпращане на входящи мрежови съобщения до конкретни нишки въз основа на идентификатори на съобщения в iOS (Swift 2)

Задълбочавам се в кодирането на iOS със Swift, така че съм нов.

Моето приложение за iOS (всъщност игра) говори със сървър за игри, използвайки връзка с един сокет и персонализиран JSON протокол. Съобщенията са еднопосочни (известия) и заявка/отговор (могат да бъдат инициирани и от двете страни). Заявките/Отговорите са свързани заедно чрез използване на глобално уникални идентификатори на съобщения. Така че това е доста различно от обикновените HTTP/REST заявки.

Това, което може да се случи сега (всъщност проблемът, който трябва да реша) е например:

  • Сървърът изпраща заявка (с искане за конкретно действие... напр. играчите се обръщат) (идентификатор на съобщение S01)
  • Приложението изпраща в същото време заявка (за някакво различно действие... напр. списък с приятели) (идентификатор на съобщение C01)

В момента моята iOS игра използва клас SocketConnection, който се свързва със сървъра и може да изпраща и получава (JSON) известия/заявки/отговори. Липсва обаче частта за изпращане на отговор към нишката, която е инициирала заявката.

Прегледах документацията за iOS и купища блогове/уроци. Всички те изглежда покриват GCD и как да го използвате за паралелност (зареждане на работа в работници и т.н.)

Това, от което се нуждая обаче, е някакъв вид изчакване/уведомяване като в Java. Сървърът на играта е внедрен в Java и прави приблизително следното, когато инициира заявка за клиент:

  • вземете заявка и създайте уникален идентификатор на съобщение за нея
  • съхранявайте го в колекция за по-късна справка
  • изчакване (време за изчакване) при обект на заявка

Във фонов режим има специална (частна) нишка на приемник, която получава всички входящи съобщения:

докато (!прекратено) {

  • изчакайте следващото съобщение
  • ако съобщението е отговор, потърсете съответната заявка по идентификатора на съобщението
  • поставете отговора в заявката
  • notify() инициатора на заявката за обекта на заявката

}

Мислех за разделянето на връзките на 3 или повече, така че съобщението за преплитане на заявка/отговор изобщо да не се появи - но мисля, че тази идея не е добра поради a) обработката на множество връзки ще направи много по-трудно поддържането на играта стабилна и b ) в дългосрочен план трябва да е възможно да се играят няколко игрови сесии паралелно в едно и също копие на приложение за iOS.

В момента мисля, че семафорите и sleep() могат да работят, което би наподобявало модела, внедрен на сървъра. Но преди да се опитам да приложа това, бих искал да знам дали това е осъществимо. Семафорите изглеждат доста бързи, но ще ми трябват милиони семафори - по един за всеки уникален идентификатор на съобщение). И ако използвам семафори: как бих внедрил wait()? Използване на зает цикъл със заспиване и запитване на семафора? Мисля, че wait() просто ще използва wait() на семафора.

Благодаря предварително за всякакви отзиви или идеи.


person Alex    schedule 18.07.2015    source източник
comment
Защо да се занимавате с персонализиран кабелен протокол? Просто използвайте HTTP + отдалечени известия или уеб сокети и приключете с това, за да можете да съсредоточите повече усилия върху важните и интересни аспекти на създаването на игра.   -  person mattt    schedule 18.07.2015
comment
Бих се радвал да се съсредоточа повече върху самата игра :) Уеб сокетите обаче ще изискват и персонализиран протокол (почти това, което вече използвам) плюс те/са по-тежки от страна на сървъра (поне пролетта уеб сокети изискват J2EE контейнер) от това, което имам в момента. Освен това HTTP+REST плюс някакъв вид обратен канал за известия означава 2 различни стека съобщения, което плаче за проблеми според мен. За какви дистанционни известия се сетихте?   -  person Alex    schedule 19.07.2015


Отговори (1)


Това, от което се нуждая обаче, е някакъв вид изчакване/уведомяване като в Java.

Това работи зле в iOS. Може да генерира твърде много блокирани нишки, което може да блокира цялата система. Вместо това трябва да мислите от гледна точка на опашки, а не на нишки.

По принцип начинът, по който това се обработва в iOS, е да се включи манипулатор за завършване или делегат заедно със заявката. Така че вашата заявка ще изглежда така:

conn.sendRequest(request, completion: {
   // thing to do when it completes
})

Това не блокира. sendRequest ще постави completion в речник на идентификатори на заявки -> манипулатори на завършване. Когато отговорът се върне, вие търсите манипулатора за завършване и го изпълнявате. Например:

if let handler = self.handlers[identifier] {
    handler(response)
} else {
    // We got a response we weren't expecting... This may be an error, or we might ignore.
}

За повече подробности защо моделът async/await работи много зле в iOS, вижте Изграждане Отзивчиви и ефективни приложения с GCD. Вижте също Ръководство за паралелно програмиране, и по-специално раздела „Мигриране от нишки“.

person Rob Napier    schedule 18.07.2015
comment
Ще пренастроя мозъчните си клетки и ще разбера добре GCD и опашките :) Добре е да знам, че идеята за семафорите вероятно ще се провали рано или късно. Едно нещо, което може да е добро и лошо в същото време, може да е, че за използване на опашки повикващият трябва да дефинира контекста на извикването, така че да знае по-късно какво е било състоянието на приложението по време на изпращането на първоначалната заявка. Предполагам, че контекстът на извикването е най-добре да се предаде вътре в блока (блокът е това, което се поставя в опашката, нали?) за справка... - person Alex; 19.07.2015
comment
@Алекс правилно. Блокът може да поддържа състояние (той улавя променливите в обхвата, в който е създаден), така че самият блок има целия контекст на извикване, от който се нуждаете в много случаи. Можете също така да създавате модели производител/потребител, като използвате серийни опашки, които могат да пресъздадат голяма част от това, с което сте свикнали. Опашките са доста евтини (за разлика от нишките), така че не се страхувайте да създавате опашки, за да организирате работата си. - person Rob Napier; 19.07.2015