J'implémente la communication client-serveur par paquets auto-définis.
J'utilise Go net.conn
. Il peut composer des schémas tcp / unix, ce qui est très pratique. J'utilise protocol buffer
pour définir mes messages.
J'ai défini un Packet qui contient length
et buffer
func(api *API) Send(m *proto.Message) error { bytes, err := proto.Marshal(m) if err != nil { return err } buffer := api.packet[:length] copy(buffer, bytes) _, err := api.conn.Write(buffer) if err != nil { return err } return nil }
La fonction API est comme ceci:
Erreur func (api * API) Send (m * proto.Message)
Erreur de réception func (api * API) (p * Packet)
Prenez la fonction send
comme exemple, elle prend un message protobuf, le marshalez dans Packet
. Et écrivez-le dans le net.conn
.
Voici une version simplifiée de la fonction Send:
type Packet struct { length uint32 buffer []byte }
Je copiais octets
dans le buffer code>. Parce que l'API de tampon de protocole Go fournit uniquement
func Marshal (pb Message) ([] byte, error)
Dans le tampon de protocole C ++, il fournit
bool SerializeToArray (void * data, int size) const
, qui sérialise le message et le stocke dans le tableau d'octets donné.
Mais je ne trouve pas la même chose dans l'API du tampon de protocole Go.
Y a-t-il un moyen d'éviter la copie si je veux stocker directement le résultat sérialisé dans le tableau d'octets donné?
3 Réponses :
Ce que vous demandez n'est pas clair. Notez que la fonction proto Marshal () fait exactement ce que vous recherchez: elle sérialise le message en une tranche d'octets (ce que vous entendez probablement par tableau d'octets)
Voyez si l'un de ces éléments aide:
func(api *API) Send(m *proto.Message) error { buffer := api.packet[:length] buffer, err := proto.Marshal(m) if err != nil { return err } _, err := api.conn.Write(buffer) if err != nil { return err } return nil }
Ou
func(api *API) Send(m *proto.Message) error { p := Packet{} p.buffer, err := proto.Marshal(m) if err != nil { return err } _, err := api.conn.Write(p.buffer) if err != nil { return err } return nil }
Mon but est de mettre le tampon sérialisé dans le paquet
, au lieu de l'écrire directement dans conn
. car le paquet était déjà utilisé par d'autres composants pour mon projet. Je ne peux pas le refactoriser. proto.Marshal
renvoie [] octet
et a été copié dans mon paquet. Je cherche un moyen d'éviter la copie. Un exemple est l'API protobuf C ++, elle peut rassembler un message dans un tampon déjà alloué
. Mais go protobuf ne peut que le retourner.
Il semble que vous puissiez faire de Packet.buffer
un proto.Buffer
type Packet struct { length uint32 buffer proto.Buffer } ... var packet Packet packet.length = YouLength packet.buffer = proto.NewBuffer(make([]byte, YouLength)) //Then you can Marshall in Packet directly and it may be reused. err := packet.Marshal(message)
Vous recherchez la méthode MarshalTo de gogo / protobuf
, une autre implémentation de protobuf, compatible avec l'original.
Vous pouvez réutiliser le même tampon via plusieurs appels de marshal lorsque vous lui passez le tampon à remplir. Évidemment, le tampon doit être suffisamment grand.
func MarshalTo([]byte, m) error
Pourriez-vous clarifier quelques points: Pourquoi le paquet est-il stocké dans un champ de l'API et d'où vient cette longueur? Est-ce que tout ce que vous voulez vraiment faire est d'écrire un tableau d'octets composé de la longueur (4 octets), puis du message réel à la connexion?
Qu'en est-il d'accepter une réponse?