Есть вопросы? Добро пожаловать в группу: 828958110.
Первое использование
Прежде чем представить antnet, давайте сначала попробуем его в действии и посмотрим, как он создаёт эхо-сервер.
package main
import (
"antnet"
)
func main() {
antnet.StartServer("tcp://:6666", antnet.MsgTypeCmd, &antnet.EchoMsgHandler{}, nil)
antnet.WaitForSystemExit()
}
С помощью этого кода мы создали самый простой эхо-сервер. Теперь откройте командную строку, выполните команду telnet 127.0.0.1 6666, введите строку и нажмите Enter. Вы получите ответное сообщение, которое будет идентично отправленному вами.
Дизайн
В antnet код, связанный с определёнными функциями, организован в блоки, что позволяет быстро находить нужный код. Например, файлы с префиксом parser содержат код для парсера, а файлы с префиксом msgque — код для очереди сообщений. Кроме того, код в antnet тщательно продуман. Это касается, например, момента закрытия TCP-соединения и других аспектов. Я опубликую более подробную информацию на Zhihu:
Почему выбирают antnet?
Antnet отличается от многих популярных фреймворков тем, что он использует особенности языка Go вместо того, чтобы пытаться имитировать другие языки программирования. В качестве примера можно привести глобальные сообщения в antnet:
Глобальные сообщения вызывают много вопросов, потому что подход antnet к ним отличается от подхода других фреймворков (см. часть 1 дизайна TCP). Многие считают, что глобальные сообщения должны обрабатываться в одном потоке, который перебирает все очереди сообщений и отправляет их с помощью цикла for. Это связано с тем, что такой подход используется во фреймворках на C++. Однако это может привести к проблемам с производительностью, так как поток будет часто блокироваться. Antnet решает эту проблему, позволяя каждой очереди обрабатывать свои собственные глобальные сообщения. Это позволяет использовать преимущества многоядерных процессоров и обеспечивает быструю отправку глобальных сообщений.
Многие разработчики Go пришли из мира C++, и это влияет на то, как они создают фреймворки на Go. Меня тоже интересует, как использовать особенности Go, а не просто переносить идеи из C++ на новый язык.
Кроме того, antnet учитывает множество сетевых деталей, которые часто игнорируются другими фреймворками из-за недостаточного понимания работы сетей. Я даже требую от кандидатов на работу написать полный граф состояний для TCP, хотя это требование обычно смягчается при реальном собеседовании.
Вот почему antnet отличается от большинства популярных фреймворков, и я надеюсь, что вы задумаетесь об этом, если столкнётесь с необычными подходами в коде antnet.
Зависимости
Производственная среда
Antnet обслуживает миллионы игроков по всему миру, и вот некоторые примеры коммерческих игр, использующих его:
Хотя antnet является базовым фреймворком, он не реализует RUDP, поскольку требования к RUDP могут различаться в зависимости от типа игры. Вместо этого я предпочитаю настраивать RUDP для каждого конкретного проекта.
При использовании параметра race для тестирования конкуренции могут возникать предупреждения, так как я допускаю одновременное чтение и запись для некоторых переменных. Однако эти переменные используются после тщательного анализа и тестирования, и их использование не должно вызывать проблем с конкуренцией. Проблемы с конкуренцией могут возникнуть в следующих случаях:
Мобильные приложения
Antnet также поддерживает разработку H5-приложений, и он является единственным игровым продуктом H5, доступным для мобильных устройств. Вы можете попробовать его, поиграв в «美食大战老鼠2» или «街机三国3» на своём мобильном устройстве или отсканировав QR-код.
|---------------------------------------------------|
Приложение (пользовательская логика) |
---|
Обработка (IMsgHandler) |
--------------------------------------------------- |
Парсинг (IMsgParser, IParser, IParserFactory) |
--------------------------------------------------- |
Сеть (IMsgQue, Message) |
--------------------------------------------------- |
Тестовый фреймворк
Wsserver — это тестовый фреймворк для веб-сокетов, демонстрирующий, как использовать antnet. Он содержит менее 60 строк логического кода и реализует чат-систему на основе веб-сокетов и protobuf. Union — официальный фреймворк antnet, но он пока не доступен для открытого использования по коммерческим причинам.
Архитектура |
---|
Приложение (пользовательская логика) |
--------------------------------------------------- |
Обработка (IMsgHandler) |
--------------------------------------------------- |
Парсинг (IMsgParser,IParser,IParserFactory) |
--------------------------------------------------- |
Сеть (IMsgQue,Message) |
--------------------------------------------------- |
Заголовок сообщения
Для сетевого сервера необходимо определить заголовок сообщения, который в antnet имеет длину 12 байт.
type MessageHead struct {
Len uint32 //длина данных
Error uint16 //код ошибки
Cmd uint8 //команда
Act uint8 //действие
Index uint16 //номер
Flags uint16 //флаги
}
Код ошибки используется для быстрого возврата ошибки, когда серверу не нужно отправлять данные. Команды и действия определяют назначение сообщения, а индекс служит уникальным идентификатором для каждого запроса. Вместе команда и действие образуют тег сообщения, который сервер должен вернуть без изменений. Флаги указывают на дополнительные параметры сообщения, такие как сжатие или шифрование.
Сообщение
type Message struct {
Head *MessageHead //заголовок, может быть нулевым
Data []byte //данные сообщения
IMsgParser //парсер
User interface{} //пользовательские данные
}
Сообщение представляет собой абстракцию взаимодействия, каждое сообщение имеет свой собственный парсер. Оно состоит из заголовка (может быть нулевым) и данных.
Antnet обрабатывает эти типы сообщений особым образом, обеспечивая высокую производительность. Функция Send используется для отправки глобальных сообщений, а функция SendGroup — для групповых сообщений. Один очередь сообщений может иметь несколько групповых идентификаторов, например, для комнат и клубов. Анализ
В запросе представлен текст на языке Go, который описывает архитектуру и принципы работы системы antnet. В тексте рассматриваются различные типы анализаторов (парсеров), их назначение и способы использования. Также описываются обработчики сообщений и процесс запуска сервера с использованием библиотеки antnet.
Перевод
Разбор
antnet в настоящее время имеет шесть типов анализаторов:
```go
type ParserType int
const (
ParserTypePB ParserType = iota //protobuf тип, используемый для взаимодействия с клиентом
ParserTypeCmd //тип cmd, похожий на команды telnet, используется для прямого взаимодействия с программой
ParserTypeJson //тип json, может использоваться для взаимодействия между клиентом и сервером
ParserTypeMsgpack //тип msgpack, может использоваться для взаимодействия между клиентом и сервером
ParserTypeCustom //пользовательский тип
ParserTypeRaw //без разбора)
)
Все эти шесть типов анализаторов могут быть созданы с помощью antnet.Parser.
Каждый анализатор требует определения поля Type и поля ErrType. Поле Type указывает тип анализатора сообщений, а поле ErrType определяет поведение по умолчанию при сбое анализа сообщения. В настоящее время существует четыре способа обработки ошибок:
type ParserType int
const (
ParseErrTypeSendRemind ParseErrType = iota //сообщение об ошибке отправляется клиенту
ParseErrTypeContinue //сообщение об ошибке пропускается
ParseErrTypeAlways //сообщение обрабатывается независимо от ошибки
ParseErrTypeClose //сообщение об ошибке приводит к закрытию соединения)
По умолчанию анализатор имеет тип pb, и обработка ошибок заключается в отправке уведомления об ошибке клиенту.
Например, если у нас есть требование получить уровень игрока на основе его идентификатора, мы можем создать анализатор типа cmd, что позволит нам напрямую подключаться к серверу через telnet и запрашивать информацию. Для этого мы используем следующий код:
pf := &antnet.Parser{Type: antnet.ParserTypeCmd}
Приведённый выше код определяет анализатор, основанный на типе cmd.
После определения анализатора необходимо зарегистрировать сообщения, которые он должен анализировать. Анализаторы поддерживают два режима:
Функции регистрации определяются следующим образом:
Register(cmd uint8, act uint8, c2s interface{}, s2c interface{})
RegisterMsg(c2s interface{}, s2c interface{})
Анализатор командной строки
Анализатор командной строки используется для анализа ввода командных строк, подобно telnet. После запуска сервера я хочу иметь простой интерфейс обмена данными, предпочтительно основанный на telnet. Этот анализатор готов к приёму неполного ввода, пока вы в конечном итоге не введёте всё полностью, или к приёму неправильного ввода до тех пор, пока вы не введёте правильный ввод.
Анализатор командной строки в настоящее время поддерживает два тега:
Для регистрации анализатора командной строки требуется структура данных, например, в приведённом выше примере, где требуется получить уровень игрока, определение выглядит следующим образом:
type GetGamerLevel struct {
Get string `match:"k"`
Gamer int
Level int `match:"k"`
}
Три поля объясняются следующим образом:
Определив структуру данных, мы должны зарегистрировать её в анализаторе, используя следующий код:
pf.RegisterMsg(&GetGamerLevel{}, nil)
Таким образом, мы регистрируем сообщение в анализаторе.
Протокольный анализатор
Протокольный анализатор используется для анализа данных типа pb.
Пользовательский анализатор
Если существующие анализаторы не удовлетворяют требованиям, можно создать собственный анализатор, реализовав IParserFactory.
Обработчик
Обработчики используются для обработки сообщений. Обработчик должен реализовывать интерфейс IMsgHandler:
type IMsgHandler interface {
OnNewMsgQue(msgque IMsgQue) bool //новое сообщение в очереди
OnDelMsgQue(msgque IMsgQue) //очередь сообщений закрыта
OnProcessMsg(msgque IMsq Que, msg *Message) bool //функция обработки по умолчанию
OnConnectComplete(msgque IMsgQue, ok bool) bool //соединение успешно установлено
GetHandlerFunc(msgque IMsgQue, msg *Message) HandlerFunc //получение функции обработки на основе сообщения)
}
Конечно, обычно нам не нужно полностью реализовывать вышеуказанный интерфейс. Достаточно добавить antnet.DefMsgHandler в наш обработчик.
Antnet.DefMsgHandler также определяет функции Register и RegisterMsg для различения различных входных данных. Если вы не зарегистрировали никаких функций обработки сообщений, система автоматически вызовет функцию OnProcessMsg, если она определена.
В приведённом примере получения уровня игрока мы определяем обработчик следующим образом:
type Handler struct {
antnet.DefMsgHandler
}
Определив обработчик, мы создаём обработчик и регистрируем функции обработки, которые хотим использовать:
h := &Handler{}
h.RegisterMsg(&GetGamerLevel{}, func(msgque antnet.IMsgQue, msg *antnet.Message) bool {
c2s := msg.C2S().(*GetGamerLevel)
c2s.Level = 8
msgque.SendStringLn(msg.C2SString())
return true
})
Так мы создали обработчик и зарегистрировали функцию обработки.
Время вызова обработчика
Antnet создаёт два goroutine для каждого TCP-соединения: один для чтения, другой для записи. Обработка происходит в goroutine, отвечающем за чтение. Почему выбрана такая конструкция? Нужно ли обрабатывать следующее сообщение сразу после того, как предыдущее сообщение не было обработано?
Запуск службы
Чтобы запустить сетевой сервер с использованием antnet.StartServer, необходимо указать обработчик и анализатор.
В примере получения уровня игрока запуск службы выглядит следующим образом:
antnet.StartServer("tcp://:6666", antnet.MsgTypeCmd, h, pf)
После запуска службы мы ожидаем сигнала Ctrl+C для завершения работы службы, используя функцию WaitForSystemExit.
Полный пример:
package main
import "antnet"
type GetGamerLevel struct {
Get string `match:"k"`
Gamer int
Level int `match:"k"`
}
type Handler struct {
antnet.DefMsgHandler
}
func test(msgque antnet.IMsgQue, msg *antnet.Message) bool {
c2s := msg.C2S().(*GetGamerLevel)
c2s.Level = 8
msgque.SendStringLn(msg.C2SString())
return true
}
func main() {
pf := &antnet.Parser{Type: antnet.ParserTypeCmd}
pf.RegisterMsg(&GetGamerLevel{}, nil)
h
``` В данном тексте описывается создание основанного на командах сетевого приложения.
В примере кода показано, как можно создать и запустить сервер с помощью функции antnet.StartServer. Затем ожидается завершение работы программы с помощью вызова функции antnet.WaitForSystemExit.
Далее в тексте рассказывается о глобальных переменных и функциях, предоставляемых библиотекой antnet для удобства использования. Также описывается система логирования, основанная на константах LogLevelAllOn, LogLevelDebug, LogLevelInfo, LogLevelWarn, LogLevelError, LogLevelFatal и LogLevelAllOff.
Кроме того, в тексте упоминается об обёртке над Redis, предоставляемой antnet, а также о встроенной системе таймеров и о модели данных, основанной на протобуферах.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )