No description
Find a file
toki 7f8771b89e refactor: implement HeartbeatMixin for heartbeat handling
- Dart: Replace ResponseChecker with HeartbeatMixin
- Dart: Remove heartbeat-related fields from ProtobufClient/Server
- Dart: Add HeartbeatMixin for cleaner heartbeat logic
- Go: Add timeout test in communicator_test.go
- Go: Add heartbeat_test.go with heartbeat functionality tests
- Go: Add TLS test in tls_test.go
- Go: Add message type mismatch tests
- Go: Improve tcp_test.go and ws_test.go with comprehensive tests
- Dart: Add comprehensive socket_test.dart with multiple test scenarios
- Add untracked documentation files
2026-04-11 16:33:40 +09:00
.claude feat: add WebSocket/WSS support and Go implementation 2026-04-11 08:33:46 +09:00
.vscode Update launch.json 2026-04-05 20:55:10 +09:00
dart refactor: implement HeartbeatMixin for heartbeat handling 2026-04-11 16:33:40 +09:00
go refactor: implement HeartbeatMixin for heartbeat handling 2026-04-11 16:33:40 +09:00
skills/add-crosstest-language Refactor project structure and add Close() method 2026-04-11 09:54:03 +09:00
.codex Update protocol and add WebSocket protobuf client/server implementations 2026-04-05 20:44:37 +09:00
.gitignore docs: add Python and Rust to planned implementations 2026-04-05 13:46:20 +09:00
CODE_REVIEW_REVIEW_TEST.md refactor: implement HeartbeatMixin for heartbeat handling 2026-04-11 16:33:40 +09:00
IMPROVEMENT_PLAN_REVIEW_TEST.md refactor: implement HeartbeatMixin for heartbeat handling 2026-04-11 16:33:40 +09:00
PORTING_GUIDE.md Add PORTING_GUIDE.md 2026-04-11 15:01:54 +09:00
PROTOCOL.md feat: add WebSocket/WSS support and Go implementation 2026-04-11 08:33:46 +09:00
README.md feat: add WebSocket/WSS support and Go implementation 2026-04-11 08:33:46 +09:00

Toki Socket

Binary socket protocol library for bidirectional, heterogeneous communication across languages and platforms.

Built on Protocol Buffers with TCP length-prefixed framing, WebSocket binary frames, type-based message routing, request-response correlation, and built-in heartbeat.


Design Principles

  • Keep the core transport layer thin and stable
  • Provide only the minimum common foundation for cross-language communication
  • Standardize framing, serialization, routing, request-response correlation, and heartbeat
  • Do not embed application semantics into the protocol core
  • Domain-specific concerns such as auth, session, agent workflow, chat features, and game logic belong in upper-layer implementations

Protocol

See PROTOCOL.md for the full wire format specification.

[4-byte big-endian length] [PacketBase protobuf bytes]

For WebSocket/WSS transports, each binary frame contains one PacketBase protobuf payload without the TCP length header.


Implementations

Language Status Path Use case
Dart Available dart/ Flutter, Dart server
C# Planned csharp/ Unity, .NET
Kotlin Planned kotlin/ Android
Swift Planned swift/ iOS, macOS
Go Available go/ Server, tooling, scripting
Python Planned python/ Server, tooling, scripting
Rust Planned rust/ High-performance server, embedded

Quick Start (Dart)

import 'package:toki_socket/toki_socket.dart';

// 1. Define your message in message_common.proto, generate with protoc

// 2. Implement a client
class MyClient extends ProtobufClient {
  MyClient(Socket socket) : super(socket, 30, 10, {
    MyMessage.getDefault().info_.qualifiedMessageName: MyMessage.fromBuffer,
  });
}

// 3. Implement a server
class MyServer extends ProtobufServer {
  MyServer() : super('0.0.0.0', 9090, (socket) => MyClient(socket));

  @override
  void onClientConnected(ProtobufClient client) {
    client.addListener<MyMessage>((msg) => print('Received: ${msg}'));
  }
}

// 4. Start
final server = MyServer();
await server.start();

// 5. Connect and send
final socket = await Socket.connect('localhost', 9090);
final client = MyClient(socket);
await client.send(MyMessage()..text = 'hello');

Adding Message Types

Edit dart/lib/src/packets/message_common.proto and regenerate:

cd dart
dart pub global activate protoc_plugin
protoc --dart_out=lib/src/packets lib/src/packets/message_common.proto

For Go, keep go/packets/message_common.proto in sync with the canonical Dart proto. The Go copy includes option go_package, then regenerate from the Go module root:

cd go
protoc --go_out=. --go_opt=paths=source_relative packets/message_common.proto

Quick Start (Go)

package main

import (
	"context"
	"net"
	"time"

	"google.golang.org/protobuf/proto"

	toki "toki-labs.com/toki_socket/go"
	"toki-labs.com/toki_socket/go/packets"
)

func parserMap() toki.ParserMap {
	return toki.ParserMap{
		toki.TypeNameOf(&packets.TestData{}): func(b []byte) (proto.Message, error) {
			m := &packets.TestData{}
			return m, proto.Unmarshal(b, m)
		},
	}
}

func main() {
	ctx := context.Background()

	server := toki.NewTcpServer("127.0.0.1", 9090, func(conn net.Conn) *toki.TcpClient {
		return toki.NewTcpClient(conn, 30, 10, parserMap())
	})
	server.OnClientConnected = func(client *toki.TcpClient) {
		toki.AddRequestListenerTyped[*packets.TestData, *packets.TestData](
			&client.Communicator,
			func(req *packets.TestData) (*packets.TestData, error) {
				return &packets.TestData{Index: req.GetIndex(), Message: "echo: " + req.GetMessage()}, nil
			},
		)
	}
	if err := server.Start(ctx); err != nil {
		panic(err)
	}
	defer server.Stop()

	client, err := toki.DialTcp(ctx, "127.0.0.1", 9090, 30, 10, parserMap())
	if err != nil {
		panic(err)
	}
	defer client.Close()

	res, err := toki.SendRequestTyped[*packets.TestData, *packets.TestData](
		&client.Communicator,
		&packets.TestData{Index: 1, Message: "hello"},
		2*time.Second,
	)
	if err != nil {
		panic(err)
	}
	println(res.GetMessage())
}

Go also provides:

  • TCP: NewTcpServer, DialTcp, NewTcpServerTLS, DialTcpTLS
  • WebSocket: NewWsServer, DialWs, NewWsServerTLS, DialWss
  • Shared helpers: Send, SendRequest, AddListenerTyped, AddRequestListenerTyped, Broadcast

Running Tests

cd dart
dart pub get
dart test
cd go
go test ./...