Fundamentals 19 min read

Introduction to Protobuf and Its Practical Application in Frontend and Backend Development

This article introduces Google Protocol Buffers, explains its binary encoding principles such as Tag‑Length‑Value, Varint and ZigZag, compares its efficiency with JSON/XML, and provides a step‑by‑step demo of building a Protobuf‑based WebSocket communication system using Node.js, protobufjs and a simple HTTP server.

Xueersi Online School Tech Team
Xueersi Online School Tech Team
Xueersi Online School Tech Team
Introduction to Protobuf and Its Practical Application in Frontend and Backend Development

Google Protocol Buffers (Protobuf) is a lightweight, high‑efficiency structured data format that is platform‑independent, language‑independent and extensible, suitable for communication protocols and data storage.

Protobuf uses a binary protocol, which offers higher conversion speed, time efficiency and space efficiency than JSON, making it attractive for micro‑service communication and binary data exchange between native apps and servers.

Protobuf Characteristics

Cross‑platform, cross‑language, highly extensible.

Binary format, small size, fast serialization, high transmission efficiency.

Built‑in compiler, low maintenance cost, strong encryption, backward compatible.

Encoding Structure

Each binary message consists of fields encoded as Tag - [Length] - Value . The Tag combines a field number and a wire type (3‑bit), defining six effective wire types.

Varint Encoding

Tag, int32, int64, uint32, uint64, bool and enum fields use Varint, a compact representation where each byte’s most‑significant bit indicates continuation. Numbers < 128 use one byte; larger numbers use more bytes (e.g., 300 → <ac 02> , 666666 → <aa d8 28> ).

ZigZag Encoding

For signed integers (sint32, sint64), Protobuf applies ZigZag to map negative numbers to unsigned values before Varint encoding, improving efficiency for small‑magnitude negatives.

Project Setup

Create client , proto and server directories. In proto/tal/xesv5.proto define the message:

syntax = "proto3";
package xesv5;
message Person {
    int32 id = 1;
    string name = 2;
    int32 age = 3;
    int32 empno = 4;
}

Install dependencies:

npm install protobufjs nodejs-websocket

Server Code (proto_test.js)

var protobufjs = require("protobufjs");
var root = protobufjs.loadSync("../proto/tal/xesv5.proto");
var personIn = root.lookupType("xesv5.Person");
var obj = personIn.fromObject({id:222, name:"陈健", age:"33", empno:666666});
var buf = personIn.encode(obj).finish();
console.log(buf);
var message = personIn.decode(buf);
console.log(message);
console.log(message.empno);

Running the script prints the binary buffer and the decoded Person object.

WebSocket Server (main.js)

var ws = require("nodejs-websocket");
var protobufjs = require("protobufjs");
var root = protobufjs.loadSync("../proto/tal/xesv5.proto");
var personIn = root.lookupType("xesv5.Person");
var server = ws.createServer(function(conn){
    console.log("连接成功");
    conn.on("binary", function(inStream){
        var data;
        inStream.on("readable", function(){ data = inStream.read(); });
        inStream.on("end", function(){
            console.log("接收到 " + data.length + " 字节的二进制数据");
            var cmd = data.readUInt16BE(0);
            console.log("接收数据的cmd:", cmd);
            var bytes = Buffer.from(data,1);
            var personOne = personIn.decode(bytes);
            console.log("接收数据:", personOne);
            var personTwoObj = personIn.fromObject({id:222, name:"陈健", age:33, empno:666666});
            var repbytes = personIn.encode(personTwoObj).finish();
            var sendBuffer = Buffer.alloc(2);
            sendBuffer.writeInt16BE(100);
            var totalBuffer = Buffer.concat([sendBuffer, repbytes], repbytes.length + sendBuffer.length);
            conn.sendBinary(totalBuffer);
        });
    });
    conn.on("close", function(){ console.log("关闭连接"); });
    conn.on("error", function(code, reason){ console.log(code, reason); console.log("异常关闭"); });
}).listen(8001);
console.log("创建websocket完毕");

Client HTML (main.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Protobuf测试页面</title>
</head>
<body>
    <div id="output"></div>
    <script src="./js/protobuf.min.js"></script>
    <script type="text/javascript">
    var Person = null, buffer = null, wsUri = "ws://localhost:8001", output;
    function init(){
        output = document.getElementById("output");
        protobuf.load("proto/tal/xesv5.proto", function(err, root){
            if (err) throw err;
            Person = root.lookupType("xesv5.Person");
            var message = Person.create({id:111, name:'陈健', age:34, empno:555555});
            buffer = Person.encode(message).finish();
            console.log(buffer);
            createWebSocket();
        });
    }
    function createWebSocket(){
        websocket = new WebSocket(wsUri);
        websocket.onopen = onOpen;
        websocket.onclose = onClose;
        websocket.onmessage = onMessage;
        websocket.onerror = onError;
    }
    function onOpen(){
        writeToScreen("连接开始");
        writeToScreen("发送数据:" + buffer);
        websocket.send(buffer);
    }
    function onClose(){ writeToScreen("连接关闭"); }
    function onMessage(evt){
        var reader = new FileReader();
        reader.readAsArrayBuffer(evt.data);
        reader.onload = function(e){
            var buf = new Uint8Array(reader.result);
            var message = Person.decode(buf);
            writeToScreen('接收数据:' + JSON.stringify(message));
        };
    }
    function onError(evt){ writeToScreen('错误:' + evt.data); }
    function writeToScreen(message){
        var pre = document.createElement("p");
        pre.innerHTML = message;
        output.appendChild(pre);
    }
    window.addEventListener("load", init, false);
    </script>
</body>
</html>

Simple HTTP Server (server.js)

var url = require("url"), fs = require("fs"), http = require("http"), path = require("path");
http.createServer(function(req, res){
    var pathname = __dirname + url.parse(req.url).pathname;
    if (path.extname(pathname) == "") pathname += "/";
    if (pathname.charAt(pathname.length-1) == "/") pathname += "index.html";
    fs.exists(pathname, function(exists){
        if (exists){
            switch(path.extname(pathname)){
                case ".html": res.writeHead(200,{"Content-Type":"text/html"}); break;
                case ".js":   res.writeHead(200,{"Content-Type":"text/javascript"}); break;
                case ".css":  res.writeHead(200,{"Content-Type":"text/css"}); break;
                case ".png":  res.writeHead(200,{"Content-Type":"image/png"}); break;
                default:        res.writeHead(200,{"Content-Type":"application/octet-stream"});
            }
            fs.readFile(pathname,function(err,data){ res.end(data); });
        } else {
            res.writeHead(404,{"Content-Type":"text/html"});
            res.end("
404 Not Found
");
        }
    });
}).listen(3000,"127.0.0.1");
console.log("Server running at http://127.0.0.1:3000/");

Running the HTTP server and opening http://localhost:3000/main.html establishes a WebSocket connection, sends a Protobuf‑encoded binary message, and displays the decoded response on the page.

Conclusion

Protobuf provides a compact, fast, and language‑agnostic binary serialization mechanism that can significantly improve network efficiency compared with JSON or XML. After mastering its encoding rules and tooling, developers can build high‑performance front‑end and back‑end communication pipelines, though binary data reduces human readability and requires proper debugging tools.

backendfrontendNode.jsProtobufWebSocketBinary ProtocolData Serialization
Xueersi Online School Tech Team
Written by

Xueersi Online School Tech Team

The Xueersi Online School Tech Team, dedicated to innovating and promoting internet education technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.