nng-ruby 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +871 -54
- data/examples/protobuf_advanced.rb +322 -0
- data/examples/protobuf_demo.rb +340 -0
- data/examples/protobuf_example.rb +374 -0
- data/examples/protobuf_simple.rb +191 -0
- data/examples/protobuf_thread.rb +236 -0
- data/ext/nng/extconf.rb +71 -0
- data/lib/nng/ffi.rb +78 -9
- data/lib/nng/socket.rb +5 -2
- data/lib/nng/version.rb +1 -1
- data/nng.gemspec +6 -4
- metadata +26 -5
@@ -0,0 +1,322 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# NNG + Protocol Buffers 高级示例
|
5
|
+
# 使用 .proto 文件和复杂的消息嵌套
|
6
|
+
|
7
|
+
require 'nng'
|
8
|
+
require 'google/protobuf'
|
9
|
+
|
10
|
+
puts "=" * 70
|
11
|
+
puts "NNG + Protocol Buffers 高级示例"
|
12
|
+
puts "使用 .proto 文件和消息嵌套"
|
13
|
+
puts "=" * 70
|
14
|
+
puts
|
15
|
+
|
16
|
+
# ============================================================================
|
17
|
+
# 1. 从 .proto 文件生成 Ruby 代码 (编译时)
|
18
|
+
# ============================================================================
|
19
|
+
|
20
|
+
puts "说明: 使用 .proto 文件"
|
21
|
+
puts "-" * 70
|
22
|
+
puts
|
23
|
+
puts "实际项目中应该使用 protoc 编译 .proto 文件:"
|
24
|
+
puts " $ protoc --ruby_out=. proto/message.proto"
|
25
|
+
puts
|
26
|
+
puts "本示例直接在 Ruby 中定义消息 (等效于编译后的代码)"
|
27
|
+
puts
|
28
|
+
|
29
|
+
# 定义消息 (等效于从 message.proto 编译生成)
|
30
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
31
|
+
add_file("message.proto", syntax: :proto3) do
|
32
|
+
# RPC 请求
|
33
|
+
add_message "RpcRequest" do
|
34
|
+
optional :func_code, :int32, 1
|
35
|
+
optional :data, :bytes, 2
|
36
|
+
optional :timestamp, :int64, 3
|
37
|
+
optional :request_id, :string, 4
|
38
|
+
end
|
39
|
+
|
40
|
+
# RPC 响应
|
41
|
+
add_message "RpcResponse" do
|
42
|
+
optional :status, :int32, 1
|
43
|
+
optional :data, :bytes, 2
|
44
|
+
optional :error_msg, :string, 3
|
45
|
+
optional :request_id, :string, 4
|
46
|
+
end
|
47
|
+
|
48
|
+
# 用户
|
49
|
+
add_message "User" do
|
50
|
+
optional :id, :int32, 1
|
51
|
+
optional :name, :string, 2
|
52
|
+
optional :email, :string, 3
|
53
|
+
repeated :tags, :string, 4
|
54
|
+
end
|
55
|
+
|
56
|
+
# 联系人
|
57
|
+
add_message "Contact" do
|
58
|
+
optional :wxid, :string, 1
|
59
|
+
optional :name, :string, 2
|
60
|
+
optional :remark, :string, 3
|
61
|
+
optional :contact_type, :int32, 4
|
62
|
+
end
|
63
|
+
|
64
|
+
# 联系人列表
|
65
|
+
add_message "ContactList" do
|
66
|
+
repeated :contacts, :message, 1, "Contact"
|
67
|
+
end
|
68
|
+
|
69
|
+
# 文本消息
|
70
|
+
add_message "TextMessage" do
|
71
|
+
optional :receiver, :string, 1
|
72
|
+
optional :content, :string, 2
|
73
|
+
optional :aters, :string, 3
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# 获取消息类
|
79
|
+
RpcRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("RpcRequest").msgclass
|
80
|
+
RpcResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("RpcResponse").msgclass
|
81
|
+
User = Google::Protobuf::DescriptorPool.generated_pool.lookup("User").msgclass
|
82
|
+
Contact = Google::Protobuf::DescriptorPool.generated_pool.lookup("Contact").msgclass
|
83
|
+
ContactList = Google::Protobuf::DescriptorPool.generated_pool.lookup("ContactList").msgclass
|
84
|
+
TextMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("TextMessage").msgclass
|
85
|
+
|
86
|
+
puts "✅ Protobuf 消息类加载完成"
|
87
|
+
puts
|
88
|
+
|
89
|
+
# ============================================================================
|
90
|
+
# 2. 示例 1: 嵌套消息 - 获取联系人列表
|
91
|
+
# ============================================================================
|
92
|
+
|
93
|
+
puts "示例 1: 嵌套消息 - 获取联系人列表"
|
94
|
+
puts "=" * 70
|
95
|
+
puts
|
96
|
+
|
97
|
+
url1 = "tcp://127.0.0.1:15560"
|
98
|
+
|
99
|
+
# 服务器进程
|
100
|
+
server1_pid = fork do
|
101
|
+
server = NNG::Socket.new(:pair1)
|
102
|
+
server.listen(url1)
|
103
|
+
puts "[服务器] 监听: #{url1}"
|
104
|
+
|
105
|
+
# 接收 RPC 请求
|
106
|
+
request_data = server.recv
|
107
|
+
rpc_request = RpcRequest.decode(request_data)
|
108
|
+
|
109
|
+
puts "[服务器] 收到 RPC 调用:"
|
110
|
+
puts "[服务器] Function: 0x#{rpc_request.func_code.to_s(16)}"
|
111
|
+
puts "[服务器] Request ID: #{rpc_request.request_id}"
|
112
|
+
puts "[服务器] Timestamp: #{Time.at(rpc_request.timestamp)}"
|
113
|
+
|
114
|
+
# 构建联系人列表
|
115
|
+
contacts = ContactList.new(
|
116
|
+
contacts: [
|
117
|
+
Contact.new(wxid: "wxid_001", name: "张三", remark: "老同学", contact_type: 1),
|
118
|
+
Contact.new(wxid: "wxid_002", name: "李四", remark: "同事", contact_type: 1),
|
119
|
+
Contact.new(wxid: "chatroom_001", name: "技术交流群", remark: "", contact_type: 2)
|
120
|
+
]
|
121
|
+
)
|
122
|
+
|
123
|
+
puts "[服务器] 构建响应: #{contacts.contacts.size} 个联系人"
|
124
|
+
|
125
|
+
# 编码嵌套消息
|
126
|
+
contacts_data = ContactList.encode(contacts)
|
127
|
+
|
128
|
+
# 构建 RPC 响应
|
129
|
+
rpc_response = RpcResponse.new(
|
130
|
+
status: 0,
|
131
|
+
data: contacts_data,
|
132
|
+
error_msg: "",
|
133
|
+
request_id: rpc_request.request_id
|
134
|
+
)
|
135
|
+
|
136
|
+
# 发送响应
|
137
|
+
server.send(RpcResponse.encode(rpc_response))
|
138
|
+
puts "[服务器] 响应已发送"
|
139
|
+
|
140
|
+
server.close
|
141
|
+
end
|
142
|
+
|
143
|
+
sleep 0.5
|
144
|
+
|
145
|
+
# 客户端
|
146
|
+
client1 = NNG::Socket.new(:pair1)
|
147
|
+
client1.dial(url1)
|
148
|
+
puts "[客户端] 连接: #{url1}"
|
149
|
+
|
150
|
+
# 构建 RPC 请求
|
151
|
+
rpc_request = RpcRequest.new(
|
152
|
+
func_code: 0x12, # FUNC_GET_CONTACTS
|
153
|
+
data: "",
|
154
|
+
timestamp: Time.now.to_i,
|
155
|
+
request_id: "req_#{Time.now.to_i}_001"
|
156
|
+
)
|
157
|
+
|
158
|
+
puts "[客户端] 发送 RPC 请求:"
|
159
|
+
puts "[客户端] Function: 0x#{rpc_request.func_code.to_s(16)}"
|
160
|
+
puts "[客户端] Request ID: #{rpc_request.request_id}"
|
161
|
+
|
162
|
+
# 发送请求
|
163
|
+
client1.send(RpcRequest.encode(rpc_request))
|
164
|
+
|
165
|
+
# 接收响应
|
166
|
+
response_data = client1.recv
|
167
|
+
rpc_response = RpcResponse.decode(response_data)
|
168
|
+
|
169
|
+
puts "[客户端] 收到 RPC 响应:"
|
170
|
+
puts "[客户端] Status: #{rpc_response.status}"
|
171
|
+
puts "[客户端] Request ID: #{rpc_response.request_id}"
|
172
|
+
|
173
|
+
if rpc_response.status == 0
|
174
|
+
# 解析嵌套的联系人列表
|
175
|
+
contacts = ContactList.decode(rpc_response.data)
|
176
|
+
puts "[客户端] 解析联系人列表 (#{contacts.contacts.size} 个):"
|
177
|
+
|
178
|
+
contacts.contacts.each_with_index do |contact, i|
|
179
|
+
puts "[客户端] #{i + 1}. #{contact.name} (#{contact.wxid})"
|
180
|
+
puts "[客户端] 备注: #{contact.remark}" unless contact.remark.empty?
|
181
|
+
puts "[客户端] 类型: #{contact.contact_type == 1 ? '好友' : '群聊'}"
|
182
|
+
end
|
183
|
+
else
|
184
|
+
puts "[客户端] 错误: #{rpc_response.error_msg}"
|
185
|
+
end
|
186
|
+
|
187
|
+
client1.close
|
188
|
+
Process.wait(server1_pid)
|
189
|
+
|
190
|
+
puts
|
191
|
+
puts
|
192
|
+
|
193
|
+
# ============================================================================
|
194
|
+
# 3. 示例 2: 发送文本消息
|
195
|
+
# ============================================================================
|
196
|
+
|
197
|
+
puts "示例 2: 发送文本消息"
|
198
|
+
puts "=" * 70
|
199
|
+
puts
|
200
|
+
|
201
|
+
url2 = "tcp://127.0.0.1:15561"
|
202
|
+
|
203
|
+
# 服务器进程
|
204
|
+
server2_pid = fork do
|
205
|
+
server = NNG::Socket.new(:pair1)
|
206
|
+
server.listen(url2)
|
207
|
+
puts "[服务器] 监听: #{url2}"
|
208
|
+
|
209
|
+
# 接收 RPC 请求
|
210
|
+
request_data = server.recv
|
211
|
+
rpc_request = RpcRequest.decode(request_data)
|
212
|
+
|
213
|
+
puts "[服务器] 收到 RPC 调用:"
|
214
|
+
puts "[服务器] Function: 0x#{rpc_request.func_code.to_s(16)}"
|
215
|
+
|
216
|
+
# 解析文本消息参数
|
217
|
+
text_msg = TextMessage.decode(rpc_request.data)
|
218
|
+
puts "[服务器] 解析文本消息:"
|
219
|
+
puts "[服务器] 接收者: #{text_msg.receiver}"
|
220
|
+
puts "[服务器] 内容: #{text_msg.content}"
|
221
|
+
puts "[服务器] @的人: #{text_msg.aters}" unless text_msg.aters.empty?
|
222
|
+
|
223
|
+
# 模拟发送消息
|
224
|
+
puts "[服务器] 发送消息..."
|
225
|
+
sleep 0.1
|
226
|
+
puts "[服务器] 消息发送成功"
|
227
|
+
|
228
|
+
# 构建响应
|
229
|
+
rpc_response = RpcResponse.new(
|
230
|
+
status: 0,
|
231
|
+
data: [1].pack('C'), # 返回 1 表示成功
|
232
|
+
error_msg: "",
|
233
|
+
request_id: rpc_request.request_id
|
234
|
+
)
|
235
|
+
|
236
|
+
server.send(RpcResponse.encode(rpc_response))
|
237
|
+
server.close
|
238
|
+
end
|
239
|
+
|
240
|
+
sleep 0.5
|
241
|
+
|
242
|
+
# 客户端
|
243
|
+
client2 = NNG::Socket.new(:pair1)
|
244
|
+
client2.dial(url2)
|
245
|
+
puts "[客户端] 连接: #{url2}"
|
246
|
+
|
247
|
+
# 构建文本消息
|
248
|
+
text_msg = TextMessage.new(
|
249
|
+
receiver: "wxid_001",
|
250
|
+
content: "Hello from NNG + Protobuf!",
|
251
|
+
aters: ""
|
252
|
+
)
|
253
|
+
|
254
|
+
# 编码为字节
|
255
|
+
text_msg_data = TextMessage.encode(text_msg)
|
256
|
+
|
257
|
+
# 构建 RPC 请求
|
258
|
+
rpc_request = RpcRequest.new(
|
259
|
+
func_code: 0x20, # FUNC_SEND_TXT
|
260
|
+
data: text_msg_data,
|
261
|
+
timestamp: Time.now.to_i,
|
262
|
+
request_id: "req_#{Time.now.to_i}_002"
|
263
|
+
)
|
264
|
+
|
265
|
+
puts "[客户端] 发送文本消息:"
|
266
|
+
puts "[客户端] 接收者: #{text_msg.receiver}"
|
267
|
+
puts "[客户端] 内容: #{text_msg.content}"
|
268
|
+
|
269
|
+
# 发送请求
|
270
|
+
client2.send(RpcRequest.encode(rpc_request))
|
271
|
+
|
272
|
+
# 接收响应
|
273
|
+
response_data = client2.recv
|
274
|
+
rpc_response = RpcResponse.decode(response_data)
|
275
|
+
|
276
|
+
puts "[客户端] 收到响应:"
|
277
|
+
if rpc_response.status == 0
|
278
|
+
puts "[客户端] ✅ 消息发送成功"
|
279
|
+
else
|
280
|
+
puts "[客户端] ❌ 发送失败: #{rpc_response.error_msg}"
|
281
|
+
end
|
282
|
+
|
283
|
+
client2.close
|
284
|
+
Process.wait(server2_pid)
|
285
|
+
|
286
|
+
puts
|
287
|
+
puts
|
288
|
+
|
289
|
+
# ============================================================================
|
290
|
+
# 总结
|
291
|
+
# ============================================================================
|
292
|
+
|
293
|
+
puts "=" * 70
|
294
|
+
puts "✅ 示例完成"
|
295
|
+
puts "=" * 70
|
296
|
+
puts
|
297
|
+
puts "关键技术点:"
|
298
|
+
puts
|
299
|
+
puts "1. 消息嵌套:"
|
300
|
+
puts " RpcRequest { data: ContactList { contacts: [Contact, ...] } }"
|
301
|
+
puts
|
302
|
+
puts "2. 序列化链:"
|
303
|
+
puts " 内层: ContactList.encode(contacts) → bytes"
|
304
|
+
puts " 外层: RpcRequest.encode(request) → bytes"
|
305
|
+
puts
|
306
|
+
puts "3. 反序列化链:"
|
307
|
+
puts " 外层: RpcRequest.decode(bytes) → request"
|
308
|
+
puts " 内层: ContactList.decode(request.data) → contacts"
|
309
|
+
puts
|
310
|
+
puts "4. 实际应用场景:"
|
311
|
+
puts " • RPC 框架 (如本例)"
|
312
|
+
puts " • 微服务通信"
|
313
|
+
puts " • 消息队列"
|
314
|
+
puts " • 分布式系统数据交换"
|
315
|
+
puts
|
316
|
+
puts "5. 优势:"
|
317
|
+
puts " ✅ 强类型约束"
|
318
|
+
puts " ✅ 自动编解码"
|
319
|
+
puts " ✅ 跨语言兼容"
|
320
|
+
puts " ✅ 高效序列化"
|
321
|
+
puts " ✅ 版本向后兼容"
|
322
|
+
puts
|
@@ -0,0 +1,340 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# NNG + Protocol Buffers 集成演示
|
5
|
+
# 展示如何将 Protobuf 消息通过 NNG 发送和接收
|
6
|
+
|
7
|
+
require 'google/protobuf'
|
8
|
+
|
9
|
+
puts "=" * 70
|
10
|
+
puts "NNG + Protocol Buffers 集成演示"
|
11
|
+
puts "=" * 70
|
12
|
+
puts
|
13
|
+
|
14
|
+
# ============================================================================
|
15
|
+
# 1. 定义 Protobuf 消息结构
|
16
|
+
# ============================================================================
|
17
|
+
|
18
|
+
puts "步骤 1: 定义 Protobuf 消息结构"
|
19
|
+
puts "-" * 70
|
20
|
+
puts
|
21
|
+
|
22
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
23
|
+
add_file("wcf_rpc.proto", syntax: :proto3) do
|
24
|
+
# RPC 请求消息
|
25
|
+
add_message "RpcRequest" do
|
26
|
+
optional :func_code, :int32, 1 # 功能码
|
27
|
+
optional :data, :bytes, 2 # 业务数据
|
28
|
+
optional :request_id, :string, 3 # 请求 ID
|
29
|
+
end
|
30
|
+
|
31
|
+
# RPC 响应消息
|
32
|
+
add_message "RpcResponse" do
|
33
|
+
optional :status, :int32, 1 # 状态码
|
34
|
+
optional :data, :bytes, 2 # 响应数据
|
35
|
+
optional :error_msg, :string, 3 # 错误消息
|
36
|
+
end
|
37
|
+
|
38
|
+
# 联系人信息
|
39
|
+
add_message "Contact" do
|
40
|
+
optional :wxid, :string, 1
|
41
|
+
optional :name, :string, 2
|
42
|
+
optional :remark, :string, 3
|
43
|
+
end
|
44
|
+
|
45
|
+
# 联系人列表
|
46
|
+
add_message "ContactList" do
|
47
|
+
repeated :contacts, :message, 1, "Contact"
|
48
|
+
end
|
49
|
+
|
50
|
+
# 文本消息
|
51
|
+
add_message "TextMessage" do
|
52
|
+
optional :receiver, :string, 1
|
53
|
+
optional :content, :string, 2
|
54
|
+
optional :aters, :string, 3
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# 获取消息类
|
60
|
+
RpcRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("RpcRequest").msgclass
|
61
|
+
RpcResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("RpcResponse").msgclass
|
62
|
+
Contact = Google::Protobuf::DescriptorPool.generated_pool.lookup("Contact").msgclass
|
63
|
+
ContactList = Google::Protobuf::DescriptorPool.generated_pool.lookup("ContactList").msgclass
|
64
|
+
TextMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("TextMessage").msgclass
|
65
|
+
|
66
|
+
puts "✅ 消息定义完成:"
|
67
|
+
puts " - RpcRequest (func_code, data, request_id)"
|
68
|
+
puts " - RpcResponse (status, data, error_msg)"
|
69
|
+
puts " - Contact (wxid, name, remark)"
|
70
|
+
puts " - ContactList (contacts[])"
|
71
|
+
puts " - TextMessage (receiver, content, aters)"
|
72
|
+
puts
|
73
|
+
|
74
|
+
# ============================================================================
|
75
|
+
# 2. 示例 1: 简单的请求-响应
|
76
|
+
# ============================================================================
|
77
|
+
|
78
|
+
puts "示例 1: 简单的 RPC 请求-响应"
|
79
|
+
puts "=" * 70
|
80
|
+
puts
|
81
|
+
|
82
|
+
# 客户端: 构建请求
|
83
|
+
request = RpcRequest.new(
|
84
|
+
func_code: 0x01, # FUNC_IS_LOGIN
|
85
|
+
data: "",
|
86
|
+
request_id: "req_001"
|
87
|
+
)
|
88
|
+
|
89
|
+
puts "📤 客户端构建请求:"
|
90
|
+
puts " Function Code: 0x#{request.func_code.to_s(16)}"
|
91
|
+
puts " Request ID: #{request.request_id}"
|
92
|
+
puts
|
93
|
+
|
94
|
+
# 序列化为二进制
|
95
|
+
request_binary = RpcRequest.encode(request)
|
96
|
+
puts "📦 序列化结果:"
|
97
|
+
puts " 大小: #{request_binary.bytesize} bytes"
|
98
|
+
puts " 十六进制: #{request_binary.unpack('H*').first[0, 40]}..."
|
99
|
+
puts
|
100
|
+
|
101
|
+
# 模拟通过 NNG 发送 (实际代码: socket.send(request_binary))
|
102
|
+
puts "🌐 通过 NNG 发送: socket.send(request_binary)"
|
103
|
+
puts
|
104
|
+
|
105
|
+
# 服务器: 接收并反序列化
|
106
|
+
# 模拟从 NNG 接收 (实际代码: response_binary = socket.recv)
|
107
|
+
puts "📥 服务器接收并解析:"
|
108
|
+
received_request = RpcRequest.decode(request_binary)
|
109
|
+
puts " Function Code: 0x#{received_request.func_code.to_s(16)}"
|
110
|
+
puts " Request ID: #{received_request.request_id}"
|
111
|
+
puts
|
112
|
+
|
113
|
+
# 服务器: 构建响应
|
114
|
+
response = RpcResponse.new(
|
115
|
+
status: 0,
|
116
|
+
data: [1].pack('C'), # 返回 1 表示已登录
|
117
|
+
error_msg: ""
|
118
|
+
)
|
119
|
+
|
120
|
+
puts "📤 服务器构建响应:"
|
121
|
+
puts " Status: #{response.status} (成功)"
|
122
|
+
puts
|
123
|
+
|
124
|
+
# 序列化响应
|
125
|
+
response_binary = RpcResponse.encode(response)
|
126
|
+
puts "📦 序列化响应: #{response_binary.bytesize} bytes"
|
127
|
+
puts
|
128
|
+
|
129
|
+
# 模拟通过 NNG 发送响应
|
130
|
+
puts "🌐 通过 NNG 发送响应: socket.send(response_binary)"
|
131
|
+
puts
|
132
|
+
|
133
|
+
# 客户端: 接收并解析响应
|
134
|
+
received_response = RpcResponse.decode(response_binary)
|
135
|
+
puts "📥 客户端接收响应:"
|
136
|
+
puts " Status: #{received_response.status}"
|
137
|
+
puts " Is Login: #{received_response.data.unpack('C').first == 1}"
|
138
|
+
puts
|
139
|
+
puts
|
140
|
+
|
141
|
+
# ============================================================================
|
142
|
+
# 3. 示例 2: 嵌套消息 - 获取联系人列表
|
143
|
+
# ============================================================================
|
144
|
+
|
145
|
+
puts "示例 2: 嵌套消息 - 获取联系人列表"
|
146
|
+
puts "=" * 70
|
147
|
+
puts
|
148
|
+
|
149
|
+
# 客户端: 发送获取联系人请求
|
150
|
+
get_contacts_request = RpcRequest.new(
|
151
|
+
func_code: 0x12, # FUNC_GET_CONTACTS
|
152
|
+
data: "",
|
153
|
+
request_id: "req_002"
|
154
|
+
)
|
155
|
+
|
156
|
+
request_binary = RpcRequest.encode(get_contacts_request)
|
157
|
+
puts "📤 客户端发送请求: FUNC_GET_CONTACTS (#{request_binary.bytesize} bytes)"
|
158
|
+
puts
|
159
|
+
|
160
|
+
# 服务器: 构建联系人列表
|
161
|
+
contacts = ContactList.new(
|
162
|
+
contacts: [
|
163
|
+
Contact.new(wxid: "wxid_001", name: "张三", remark: "老同学"),
|
164
|
+
Contact.new(wxid: "wxid_002", name: "李四", remark: "同事"),
|
165
|
+
Contact.new(wxid: "wxid_003", name: "王五", remark: ""),
|
166
|
+
Contact.new(wxid: "chatroom_001", name: "技术交流群", remark: "")
|
167
|
+
]
|
168
|
+
)
|
169
|
+
|
170
|
+
puts "🏗️ 服务器构建联系人列表:"
|
171
|
+
contacts.contacts.each_with_index do |contact, i|
|
172
|
+
puts " #{i + 1}. #{contact.name} (#{contact.wxid})"
|
173
|
+
puts " 备注: #{contact.remark}" unless contact.remark.empty?
|
174
|
+
end
|
175
|
+
puts
|
176
|
+
|
177
|
+
# 序列化联系人列表 (内层消息)
|
178
|
+
contacts_binary = ContactList.encode(contacts)
|
179
|
+
puts "📦 序列化联系人列表: #{contacts_binary.bytesize} bytes"
|
180
|
+
puts
|
181
|
+
|
182
|
+
# 构建 RPC 响应 (外层消息,包含内层数据)
|
183
|
+
contacts_response = RpcResponse.new(
|
184
|
+
status: 0,
|
185
|
+
data: contacts_binary, # 嵌套的联系人列表
|
186
|
+
error_msg: ""
|
187
|
+
)
|
188
|
+
|
189
|
+
response_binary = RpcResponse.encode(contacts_response)
|
190
|
+
puts "📦 序列化 RPC 响应: #{response_binary.bytesize} bytes"
|
191
|
+
puts " (包含嵌套的联系人列表)"
|
192
|
+
puts
|
193
|
+
|
194
|
+
# 客户端: 接收并解析
|
195
|
+
puts "📥 客户端接收并解析:"
|
196
|
+
|
197
|
+
# 第一层: 解析 RPC 响应
|
198
|
+
rpc_resp = RpcResponse.decode(response_binary)
|
199
|
+
puts " RPC Status: #{rpc_resp.status}"
|
200
|
+
|
201
|
+
# 第二层: 解析嵌套的联系人列表
|
202
|
+
contact_list = ContactList.decode(rpc_resp.data)
|
203
|
+
puts " 联系人数量: #{contact_list.contacts.size}"
|
204
|
+
puts
|
205
|
+
|
206
|
+
puts "📋 解析结果:"
|
207
|
+
contact_list.contacts.each_with_index do |contact, i|
|
208
|
+
puts " #{i + 1}. #{contact.name}"
|
209
|
+
puts " WXID: #{contact.wxid}"
|
210
|
+
puts " 备注: #{contact.remark}" unless contact.remark.empty?
|
211
|
+
end
|
212
|
+
puts
|
213
|
+
puts
|
214
|
+
|
215
|
+
# ============================================================================
|
216
|
+
# 4. 示例 3: 发送文本消息
|
217
|
+
# ============================================================================
|
218
|
+
|
219
|
+
puts "示例 3: 发送文本消息"
|
220
|
+
puts "=" * 70
|
221
|
+
puts
|
222
|
+
|
223
|
+
# 客户端: 构建文本消息
|
224
|
+
text_msg = TextMessage.new(
|
225
|
+
receiver: "wxid_001",
|
226
|
+
content: "Hello from NNG + Protobuf!",
|
227
|
+
aters: ""
|
228
|
+
)
|
229
|
+
|
230
|
+
puts "📝 客户端构建文本消息:"
|
231
|
+
puts " 接收者: #{text_msg.receiver}"
|
232
|
+
puts " 内容: #{text_msg.content}"
|
233
|
+
puts
|
234
|
+
|
235
|
+
# 序列化文本消息
|
236
|
+
text_msg_binary = TextMessage.encode(text_msg)
|
237
|
+
puts "📦 序列化文本消息: #{text_msg_binary.bytesize} bytes"
|
238
|
+
puts
|
239
|
+
|
240
|
+
# 构建 RPC 请求 (包含文本消息)
|
241
|
+
send_text_request = RpcRequest.new(
|
242
|
+
func_code: 0x20, # FUNC_SEND_TXT
|
243
|
+
data: text_msg_binary,
|
244
|
+
request_id: "req_003"
|
245
|
+
)
|
246
|
+
|
247
|
+
request_binary = RpcRequest.encode(send_text_request)
|
248
|
+
puts "📦 序列化 RPC 请求: #{request_binary.bytesize} bytes"
|
249
|
+
puts "🌐 通过 NNG 发送: socket.send(request_binary)"
|
250
|
+
puts
|
251
|
+
|
252
|
+
# 服务器: 接收并解析
|
253
|
+
puts "📥 服务器接收并解析:"
|
254
|
+
recv_request = RpcRequest.decode(request_binary)
|
255
|
+
puts " Function Code: 0x#{recv_request.func_code.to_s(16)}"
|
256
|
+
|
257
|
+
# 解析嵌套的文本消息
|
258
|
+
recv_text_msg = TextMessage.decode(recv_request.data)
|
259
|
+
puts " 接收者: #{recv_text_msg.receiver}"
|
260
|
+
puts " 内容: #{recv_text_msg.content}"
|
261
|
+
puts
|
262
|
+
|
263
|
+
# 服务器: 发送消息并响应
|
264
|
+
puts "📨 服务器发送消息..."
|
265
|
+
send_response = RpcResponse.new(
|
266
|
+
status: 0,
|
267
|
+
data: [1].pack('C'), # 1 = 发送成功
|
268
|
+
error_msg: ""
|
269
|
+
)
|
270
|
+
|
271
|
+
response_binary = RpcResponse.encode(send_response)
|
272
|
+
puts "📤 服务器响应: 发送成功"
|
273
|
+
puts
|
274
|
+
|
275
|
+
# 客户端: 接收响应
|
276
|
+
final_response = RpcResponse.decode(response_binary)
|
277
|
+
puts "📥 客户端收到响应:"
|
278
|
+
if final_response.status == 0
|
279
|
+
puts " ✅ 消息发送成功"
|
280
|
+
else
|
281
|
+
puts " ❌ 发送失败: #{final_response.error_msg}"
|
282
|
+
end
|
283
|
+
puts
|
284
|
+
puts
|
285
|
+
|
286
|
+
# ============================================================================
|
287
|
+
# 5. 总结
|
288
|
+
# ============================================================================
|
289
|
+
|
290
|
+
puts "=" * 70
|
291
|
+
puts "✅ 演示完成"
|
292
|
+
puts "=" * 70
|
293
|
+
puts
|
294
|
+
puts "实际使用 NNG 的代码示例:"
|
295
|
+
puts
|
296
|
+
puts " require 'nng'"
|
297
|
+
puts " require 'google/protobuf'"
|
298
|
+
puts
|
299
|
+
puts " # 客户端"
|
300
|
+
puts " client = NNG::Socket.new(:pair1)"
|
301
|
+
puts " client.dial('tcp://127.0.0.1:10086')"
|
302
|
+
puts
|
303
|
+
puts " # 构建并发送 Protobuf 请求"
|
304
|
+
puts " request = RpcRequest.new(func_code: 0x12, data: '', request_id: 'req_001')"
|
305
|
+
puts " request_binary = RpcRequest.encode(request)"
|
306
|
+
puts " client.send(request_binary)"
|
307
|
+
puts
|
308
|
+
puts " # 接收并解析 Protobuf 响应"
|
309
|
+
puts " response_binary = client.recv"
|
310
|
+
puts " response = RpcResponse.decode(response_binary)"
|
311
|
+
puts
|
312
|
+
puts " # 解析嵌套的业务数据"
|
313
|
+
puts " contacts = ContactList.decode(response.data)"
|
314
|
+
puts
|
315
|
+
puts " client.close"
|
316
|
+
puts
|
317
|
+
puts "关键技术点:"
|
318
|
+
puts
|
319
|
+
puts "1. 消息嵌套:"
|
320
|
+
puts " RpcRequest/Response.data 字段 (bytes 类型) 可存储任意 Protobuf 消息"
|
321
|
+
puts
|
322
|
+
puts "2. 序列化链:"
|
323
|
+
puts " 业务消息 → encode → bytes → RPC 消息 → encode → bytes → NNG 发送"
|
324
|
+
puts
|
325
|
+
puts "3. 反序列化链:"
|
326
|
+
puts " NNG 接收 → bytes → decode → RPC 消息 → bytes → decode → 业务消息"
|
327
|
+
puts
|
328
|
+
puts "4. 优势:"
|
329
|
+
puts " ✅ 类型安全 - 编译时检查"
|
330
|
+
puts " ✅ 高效编码 - 比 JSON 小 50%+"
|
331
|
+
puts " ✅ 跨语言 - 与 Python/Java/Go/C++ 互通"
|
332
|
+
puts " ✅ 版本兼容 - 可安全添加/删除字段"
|
333
|
+
puts " ✅ 自动验证 - 自动检查必填字段"
|
334
|
+
puts
|
335
|
+
puts "5. 实际应用场景:"
|
336
|
+
puts " • WeChatFerry RPC 通信"
|
337
|
+
puts " • 微服务间调用"
|
338
|
+
puts " • 分布式系统消息传递"
|
339
|
+
puts " • 客户端-服务器协议"
|
340
|
+
puts
|