클라이언트와 통신시, 통신 프로토콜을 정의할 필요가 있다. skynet 은 프로토콜에대한 어떠한 규정도 없으며, 자유롭게 선택할 수 있다.
sproto 는 skynet이 제공하는 프로토콜이며, 사용을 추천하는것은 아니다. 그저 선택사항 일뿐이다. sproto 는 독립적인 프로젝트로 존재한다. 동시에 skynet 의 레포지토리에 복사본으로 존재한다.
sproto 는 google protobuffers 와 비슷한것 이지만, 설계는 훨씬 간단하며, lua 에서 사용하기 좋다. 동시에 간단한 RPC 솔루션도 제공한다.
sprto의 인코딩 프로토콜에 대하여, sproto의 readme 에 상세히 설명했다, 아래는 RPC 부분에 대한 소개이다.
RPC
먼저 패키지의 main 포맷을 정의할 필요가 있다. 이는 필수적으로 type 필드가 필요하며, RPC 가 어떤 메시지인지를 묘사한다. 또한 session 필드를 통해 응답메시지의 관계를 정의한다. 일반적으로 이 두개의 필드는 integer 로 정의된다.
.package { type 0 : integer session 1 : integer }
sproto의 RPC framework 를 사용하면, 모든 메시지는 이 메시지 헤더로 시작하며, 진짜 메시지내용은 그뒤에 붙어있다; 같이 연결한후 sproto 의 0-pack 방식으로 인코딩한다. 주의, 이데이터 페키지는 길이를 포함하고 있지않다, 진짜 네트워크로 출력할때, 길이 정보를 추가하여, 패키지분리를 편리하도록 한다. 물론. skynet의 gate 모듈을 사용한다면, 두개의 바이트로 길이를 표시하고 내용을 추가하는 방식으로 패키지를 분리한다.
역주: 헤깔린다. 그러려니하자
sproto rpc 의 메시지 처리기의 구성, 사용은 다음과같다:
local host = sproto:host(packagename) -- packagename 기본값은 "package", 위의 내용의 . package 형식。다른이름을 붙일수 있다.
이 명령의 호출은 하나의 Host 객체를 반환하며, 메시지 수신처리에 사용된다.
host:dispatch(msgcontent)
메시지처리에 사용된다. 여기의 msgcontent 또한 하나의 문자열이거나, userdata(포인터) + 길이이다. msgcontent 은 상술한 sproto의 0-pack 방식의 패키지포맷이여야 한다.
dispatch 호출의 응답은 두개의 종류가 있으며. 첫번째 반환값에 의해 결정된다:
REQUEST : 첫번째 반환 값이 “REQUEST”일때, 이는 원격요청임을 의미한다. 만약 요청패키지에 session 필드가 없다면, 이요청은 응답이 필요없음을 의미한다. 이럴때, 2번째와 3번째응답값은 메시지종류이름 (즉 sproto정의에 사용한 어떤 종류. 종류의 시작명), 그리고 메시지내용 (일반적으로 TAble) 로 구분된다; 만약 요구패키지에 session 필드가 있다면, 4번째 응답값이 있으며: 그것은 응답패키지를 생성하는 함수이다.
RESPONSE: 첫번째 반환 값이 “RESPONSE” 일때, 2번째와 3번째반환값은 session 과 메시지 내용으로 구분된다, 메시지내용은 일반적으로 table 이지만, 내용이 없을수도 있다.(그저 응답 확인)
local sender = host:attach(sp) -- 여기의 sp는 발송되어 나가는 메시지 프로토콜의 정의이다
attach 는 발송함수를 구성하고, 외부요청을 패키지로 인코딩하여 dispatch 에 의해 올바르게 디코딩되도록한다
이 sender 함수는 세개의 파라메터(name, args, session)을 받는다 name 은 메시지의 문자열이름, args 는 한장의 메시지내용저장용 table 그리고 session 은 서버가 제공한 유일한 구별번호이며, 이번호로 상대방에게 올바르게 응답한다. 물론 프로토콜이 응답이 필요하지 않다면 session을 주지 않아도 된다. 같은이유로, args 역시 공백일수 있다.
Sproto Loader
skynet 은 multi LuaVM 을 채용하기 떄문에. 만약 모든 VM 에서 모두 동일한 sproto프로토콜 정의를 로드한다면 명백히 낭비일 것이다. 그래서 skynet은 sprotoloader 라는 모듈을 제공하여 이를 공유한다.
그실현원리는 C 모듈에서 16개의 전역 slot 을 제공하며 이는 sprotoloader.register 나 sprotoloader.save 를 통해 초기화할때, 필요한 프로토콜을 로딩할수 있으며, 동시에 slot 에 저장할 수 있다. 일반적으로 두개의 슬롯만이 필요하다. 하나는 클라이언트에서 서버로 도착하는 프로토콜 그룹, 다른하나는 서버에서 클라이언트로 도달하는 프로토콜의 그룹. 구분slot 은 1 과 2이다.
이리하여, 모든 VM 내에서, 모두 sprotoloader.load 를통해 프로토콜을 vm 내로 로딩할수 있다.
주의: 이런 API 는 스레드 안전하지않다. 그러니 필수로 초기화 완료후 load 작업이 이루어지도록 스스로 보장해야한다. (load 자체는 스레드 안전하다)
구체적사용예제는, examples 아래의 agent.lua 그리고 examples 아래dml client.lua 를 참고하자. RPC 는 어떻게 동작하는지 이해하자