nng-ruby 0.1.0
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 +7 -0
- data/CHANGELOG.md +39 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +383 -0
- data/Rakefile +22 -0
- data/examples/pair.rb +48 -0
- data/examples/pubsub.rb +51 -0
- data/examples/reqrep.rb +54 -0
- data/lib/nng/errors.rb +48 -0
- data/lib/nng/ffi.rb +376 -0
- data/lib/nng/message.rb +151 -0
- data/lib/nng/protocols.rb +96 -0
- data/lib/nng/socket.rb +193 -0
- data/lib/nng/version.rb +5 -0
- data/lib/nng.rb +52 -0
- data/nng.gemspec +65 -0
- metadata +148 -0
data/lib/nng/ffi.rb
ADDED
@@ -0,0 +1,376 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module NNG
|
6
|
+
# FFI bindings for libnng
|
7
|
+
module FFI
|
8
|
+
extend ::FFI::Library
|
9
|
+
|
10
|
+
# Try to load libnng from various locations
|
11
|
+
@lib_paths = [
|
12
|
+
File.expand_path('../../ext/nng/libnng.so.1.8.0', __dir__),
|
13
|
+
'/usr/local/lib/libnng.so',
|
14
|
+
'/usr/lib/libnng.so',
|
15
|
+
'/usr/lib/x86_64-linux-gnu/libnng.so'
|
16
|
+
]
|
17
|
+
|
18
|
+
@loaded_lib_path = nil
|
19
|
+
@lib_paths.each do |path|
|
20
|
+
if File.exist?(path)
|
21
|
+
begin
|
22
|
+
ffi_lib path
|
23
|
+
@loaded_lib_path = path
|
24
|
+
break
|
25
|
+
rescue LoadError
|
26
|
+
next
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
raise LoadError, "Could not find libnng.so in any of: #{@lib_paths.join(', ')}" unless @loaded_lib_path
|
32
|
+
|
33
|
+
def self.loaded_lib_path
|
34
|
+
@loaded_lib_path
|
35
|
+
end
|
36
|
+
|
37
|
+
# ============================================================================
|
38
|
+
# Constants
|
39
|
+
# ============================================================================
|
40
|
+
|
41
|
+
NNG_MAJOR_VERSION = 1
|
42
|
+
NNG_MINOR_VERSION = 8
|
43
|
+
NNG_PATCH_VERSION = 0
|
44
|
+
|
45
|
+
# Maximum address length
|
46
|
+
NNG_MAXADDRLEN = 128
|
47
|
+
|
48
|
+
# Error codes
|
49
|
+
NNG_OK = 0
|
50
|
+
NNG_EINTR = 1
|
51
|
+
NNG_ENOMEM = 2
|
52
|
+
NNG_EINVAL = 3
|
53
|
+
NNG_EBUSY = 4
|
54
|
+
NNG_ETIMEDOUT = 5
|
55
|
+
NNG_ECONNREFUSED = 6
|
56
|
+
NNG_ECLOSED = 7
|
57
|
+
NNG_EAGAIN = 8
|
58
|
+
NNG_ENOTSUP = 9
|
59
|
+
NNG_EADDRINUSE = 10
|
60
|
+
NNG_ESTATE = 11
|
61
|
+
NNG_ENOENT = 12
|
62
|
+
NNG_EPROTO = 13
|
63
|
+
NNG_EUNREACHABLE = 14
|
64
|
+
NNG_EADDRINVAL = 15
|
65
|
+
NNG_EPERM = 16
|
66
|
+
NNG_EMSGSIZE = 17
|
67
|
+
NNG_ECONNABORTED = 18
|
68
|
+
NNG_ECONNRESET = 19
|
69
|
+
NNG_ECANCELED = 20
|
70
|
+
NNG_ENOFILES = 21
|
71
|
+
NNG_ENOSPC = 22
|
72
|
+
NNG_EEXIST = 23
|
73
|
+
NNG_EREADONLY = 24
|
74
|
+
NNG_EWRITEONLY = 25
|
75
|
+
NNG_ECRYPTO = 26
|
76
|
+
NNG_EPEERAUTH = 27
|
77
|
+
NNG_ENOARG = 28
|
78
|
+
NNG_EAMBIGUOUS = 29
|
79
|
+
NNG_EBADTYPE = 30
|
80
|
+
NNG_ECONNSHUT = 31
|
81
|
+
NNG_EINTERNAL = 1000
|
82
|
+
|
83
|
+
# Flags
|
84
|
+
NNG_FLAG_ALLOC = 1 # Allocate receive buffer
|
85
|
+
NNG_FLAG_NONBLOCK = 2 # Non-blocking mode
|
86
|
+
|
87
|
+
# Socket address families
|
88
|
+
NNG_AF_UNSPEC = 0
|
89
|
+
NNG_AF_INPROC = 1
|
90
|
+
NNG_AF_IPC = 2
|
91
|
+
NNG_AF_INET = 3
|
92
|
+
NNG_AF_INET6 = 4
|
93
|
+
NNG_AF_ZT = 5
|
94
|
+
NNG_AF_ABSTRACT = 6
|
95
|
+
|
96
|
+
# Pipe events
|
97
|
+
NNG_PIPE_EV_ADD_PRE = 0
|
98
|
+
NNG_PIPE_EV_ADD_POST = 1
|
99
|
+
NNG_PIPE_EV_REM_POST = 2
|
100
|
+
NNG_PIPE_EV_NUM = 3
|
101
|
+
|
102
|
+
# ============================================================================
|
103
|
+
# Type definitions
|
104
|
+
# ============================================================================
|
105
|
+
|
106
|
+
# nng_socket structure
|
107
|
+
class NngSocket < ::FFI::Struct
|
108
|
+
layout :id, :uint32
|
109
|
+
end
|
110
|
+
|
111
|
+
# nng_dialer structure
|
112
|
+
class NngDialer < ::FFI::Struct
|
113
|
+
layout :id, :uint32
|
114
|
+
end
|
115
|
+
|
116
|
+
# nng_listener structure
|
117
|
+
class NngListener < ::FFI::Struct
|
118
|
+
layout :id, :uint32
|
119
|
+
end
|
120
|
+
|
121
|
+
# nng_ctx structure
|
122
|
+
class NngCtx < ::FFI::Struct
|
123
|
+
layout :id, :uint32
|
124
|
+
end
|
125
|
+
|
126
|
+
# nng_pipe structure
|
127
|
+
class NngPipe < ::FFI::Struct
|
128
|
+
layout :id, :uint32
|
129
|
+
end
|
130
|
+
|
131
|
+
# nng_duration - time interval in milliseconds
|
132
|
+
typedef :int32, :nng_duration
|
133
|
+
|
134
|
+
# nng_time - absolute time in milliseconds
|
135
|
+
typedef :uint64, :nng_time
|
136
|
+
|
137
|
+
# Opaque types
|
138
|
+
typedef :pointer, :nng_msg
|
139
|
+
typedef :pointer, :nng_aio
|
140
|
+
typedef :pointer, :nng_stat
|
141
|
+
|
142
|
+
# Socket address structures
|
143
|
+
class NngSockaddrInproc < ::FFI::Struct
|
144
|
+
layout :sa_family, :uint16,
|
145
|
+
:sa_name, [:char, NNG_MAXADDRLEN]
|
146
|
+
end
|
147
|
+
|
148
|
+
class NngSockaddrPath < ::FFI::Struct
|
149
|
+
layout :sa_family, :uint16,
|
150
|
+
:sa_path, [:char, NNG_MAXADDRLEN]
|
151
|
+
end
|
152
|
+
|
153
|
+
class NngSockaddrIn < ::FFI::Struct
|
154
|
+
layout :sa_family, :uint16,
|
155
|
+
:sa_port, :uint16,
|
156
|
+
:sa_addr, :uint32
|
157
|
+
end
|
158
|
+
|
159
|
+
class NngSockaddrIn6 < ::FFI::Struct
|
160
|
+
layout :sa_family, :uint16,
|
161
|
+
:sa_port, :uint16,
|
162
|
+
:sa_addr, [:uint8, 16],
|
163
|
+
:sa_scope, :uint32
|
164
|
+
end
|
165
|
+
|
166
|
+
class NngSockaddrStorage < ::FFI::Struct
|
167
|
+
layout :sa_family, :uint16,
|
168
|
+
:sa_pad, [:uint64, 16]
|
169
|
+
end
|
170
|
+
|
171
|
+
class NngSockaddr < ::FFI::Union
|
172
|
+
layout :s_family, :uint16,
|
173
|
+
:s_inproc, NngSockaddrInproc,
|
174
|
+
:s_ipc, NngSockaddrPath,
|
175
|
+
:s_in, NngSockaddrIn,
|
176
|
+
:s_in6, NngSockaddrIn6,
|
177
|
+
:s_storage, NngSockaddrStorage
|
178
|
+
end
|
179
|
+
|
180
|
+
# ============================================================================
|
181
|
+
# Core functions
|
182
|
+
# ============================================================================
|
183
|
+
|
184
|
+
# Library finalization
|
185
|
+
attach_function :nng_fini, [], :void
|
186
|
+
|
187
|
+
# Error handling
|
188
|
+
attach_function :nng_strerror, [:int], :string
|
189
|
+
|
190
|
+
# Socket functions
|
191
|
+
attach_function :nng_close, [NngSocket.by_value], :int
|
192
|
+
attach_function :nng_socket_id, [NngSocket.by_value], :int
|
193
|
+
|
194
|
+
# Socket options - set
|
195
|
+
attach_function :nng_setopt, [NngSocket.by_value, :string, :pointer, :size_t], :int
|
196
|
+
attach_function :nng_setopt_bool, [NngSocket.by_value, :string, :bool], :int
|
197
|
+
attach_function :nng_setopt_int, [NngSocket.by_value, :string, :int], :int
|
198
|
+
attach_function :nng_setopt_size, [NngSocket.by_value, :string, :size_t], :int
|
199
|
+
attach_function :nng_setopt_uint64, [NngSocket.by_value, :string, :uint64], :int
|
200
|
+
attach_function :nng_setopt_string, [NngSocket.by_value, :string, :string], :int
|
201
|
+
attach_function :nng_setopt_ptr, [NngSocket.by_value, :string, :pointer], :int
|
202
|
+
attach_function :nng_setopt_ms, [NngSocket.by_value, :string, :nng_duration], :int
|
203
|
+
|
204
|
+
# Socket options - get
|
205
|
+
attach_function :nng_getopt, [NngSocket.by_value, :string, :pointer, :pointer], :int
|
206
|
+
attach_function :nng_getopt_bool, [NngSocket.by_value, :string, :pointer], :int
|
207
|
+
attach_function :nng_getopt_int, [NngSocket.by_value, :string, :pointer], :int
|
208
|
+
attach_function :nng_getopt_size, [NngSocket.by_value, :string, :pointer], :int
|
209
|
+
attach_function :nng_getopt_uint64, [NngSocket.by_value, :string, :pointer], :int
|
210
|
+
attach_function :nng_getopt_string, [NngSocket.by_value, :string, :pointer], :int
|
211
|
+
attach_function :nng_getopt_ptr, [NngSocket.by_value, :string, :pointer], :int
|
212
|
+
attach_function :nng_getopt_ms, [NngSocket.by_value, :string, :pointer], :int
|
213
|
+
|
214
|
+
# Connection management
|
215
|
+
attach_function :nng_listen, [NngSocket.by_value, :string, :pointer, :int], :int
|
216
|
+
attach_function :nng_dial, [NngSocket.by_value, :string, :pointer, :int], :int
|
217
|
+
|
218
|
+
# Dialer functions
|
219
|
+
attach_function :nng_dialer_create, [:pointer, NngSocket.by_value, :string], :int
|
220
|
+
attach_function :nng_dialer_start, [NngDialer.by_value, :int], :int
|
221
|
+
attach_function :nng_dialer_close, [NngDialer.by_value], :int
|
222
|
+
attach_function :nng_dialer_id, [NngDialer.by_value], :int
|
223
|
+
|
224
|
+
# Listener functions
|
225
|
+
attach_function :nng_listener_create, [:pointer, NngSocket.by_value, :string], :int
|
226
|
+
attach_function :nng_listener_start, [NngListener.by_value, :int], :int
|
227
|
+
attach_function :nng_listener_close, [NngListener.by_value], :int
|
228
|
+
attach_function :nng_listener_id, [NngListener.by_value], :int
|
229
|
+
|
230
|
+
# Send and receive (synchronous)
|
231
|
+
attach_function :nng_send, [NngSocket.by_value, :pointer, :size_t, :int], :int
|
232
|
+
attach_function :nng_recv, [NngSocket.by_value, :pointer, :pointer, :int], :int
|
233
|
+
|
234
|
+
# Message-based send/receive
|
235
|
+
attach_function :nng_sendmsg, [NngSocket.by_value, :nng_msg, :int], :int
|
236
|
+
attach_function :nng_recvmsg, [NngSocket.by_value, :pointer, :int], :int
|
237
|
+
|
238
|
+
# Memory management
|
239
|
+
attach_function :nng_free, [:pointer, :size_t], :void
|
240
|
+
attach_function :nng_strfree, [:pointer], :void
|
241
|
+
|
242
|
+
# Message functions
|
243
|
+
attach_function :nng_msg_alloc, [:pointer, :size_t], :int
|
244
|
+
attach_function :nng_msg_free, [:nng_msg], :void
|
245
|
+
attach_function :nng_msg_realloc, [:nng_msg, :size_t], :int
|
246
|
+
attach_function :nng_msg_header, [:nng_msg], :pointer
|
247
|
+
attach_function :nng_msg_header_len, [:nng_msg], :size_t
|
248
|
+
attach_function :nng_msg_body, [:nng_msg], :pointer
|
249
|
+
attach_function :nng_msg_len, [:nng_msg], :size_t
|
250
|
+
attach_function :nng_msg_append, [:nng_msg, :pointer, :size_t], :int
|
251
|
+
attach_function :nng_msg_insert, [:nng_msg, :pointer, :size_t], :int
|
252
|
+
attach_function :nng_msg_trim, [:nng_msg, :size_t], :int
|
253
|
+
attach_function :nng_msg_chop, [:nng_msg, :size_t], :int
|
254
|
+
attach_function :nng_msg_header_append, [:nng_msg, :pointer, :size_t], :int
|
255
|
+
attach_function :nng_msg_header_insert, [:nng_msg, :pointer, :size_t], :int
|
256
|
+
attach_function :nng_msg_header_trim, [:nng_msg, :size_t], :int
|
257
|
+
attach_function :nng_msg_header_chop, [:nng_msg, :size_t], :int
|
258
|
+
attach_function :nng_msg_clear, [:nng_msg], :void
|
259
|
+
attach_function :nng_msg_header_clear, [:nng_msg], :void
|
260
|
+
attach_function :nng_msg_dup, [:pointer, :nng_msg], :int
|
261
|
+
attach_function :nng_msg_get_pipe, [:nng_msg], NngPipe.by_value
|
262
|
+
attach_function :nng_msg_set_pipe, [:nng_msg, NngPipe.by_value], :void
|
263
|
+
|
264
|
+
# Context functions
|
265
|
+
attach_function :nng_ctx_open, [:pointer, NngSocket.by_value], :int
|
266
|
+
attach_function :nng_ctx_close, [NngCtx.by_value], :int
|
267
|
+
attach_function :nng_ctx_id, [NngCtx.by_value], :int
|
268
|
+
attach_function :nng_ctx_send, [NngCtx.by_value, :nng_aio], :void
|
269
|
+
attach_function :nng_ctx_recv, [NngCtx.by_value, :nng_aio], :void
|
270
|
+
attach_function :nng_ctx_sendmsg, [NngCtx.by_value, :nng_msg, :int], :int
|
271
|
+
attach_function :nng_ctx_recvmsg, [NngCtx.by_value, :pointer, :int], :int
|
272
|
+
|
273
|
+
# Pipe functions
|
274
|
+
attach_function :nng_pipe_id, [NngPipe.by_value], :int
|
275
|
+
attach_function :nng_pipe_socket, [NngPipe.by_value], NngSocket.by_value
|
276
|
+
attach_function :nng_pipe_dialer, [NngPipe.by_value], NngDialer.by_value
|
277
|
+
attach_function :nng_pipe_listener, [NngPipe.by_value], NngListener.by_value
|
278
|
+
attach_function :nng_pipe_close, [NngPipe.by_value], :int
|
279
|
+
|
280
|
+
# Asynchronous I/O functions
|
281
|
+
attach_function :nng_aio_alloc, [:pointer, :pointer, :pointer], :int
|
282
|
+
attach_function :nng_aio_free, [:nng_aio], :void
|
283
|
+
attach_function :nng_aio_stop, [:nng_aio], :void
|
284
|
+
attach_function :nng_aio_result, [:nng_aio], :int
|
285
|
+
attach_function :nng_aio_count, [:nng_aio], :size_t
|
286
|
+
attach_function :nng_aio_cancel, [:nng_aio], :void
|
287
|
+
attach_function :nng_aio_abort, [:nng_aio, :int], :void
|
288
|
+
attach_function :nng_aio_wait, [:nng_aio], :void
|
289
|
+
attach_function :nng_aio_set_msg, [:nng_aio, :nng_msg], :void
|
290
|
+
attach_function :nng_aio_get_msg, [:nng_aio], :nng_msg
|
291
|
+
attach_function :nng_aio_set_input, [:nng_aio, :uint, :pointer], :int
|
292
|
+
attach_function :nng_aio_set_output, [:nng_aio, :uint, :pointer], :int
|
293
|
+
attach_function :nng_aio_set_timeout, [:nng_aio, :nng_duration], :void
|
294
|
+
attach_function :nng_aio_set_iov, [:nng_aio, :uint, :pointer], :int
|
295
|
+
attach_function :nng_aio_begin, [:nng_aio], :bool
|
296
|
+
attach_function :nng_aio_finish, [:nng_aio, :int], :void
|
297
|
+
attach_function :nng_aio_defer, [:nng_aio, :pointer, :pointer], :void
|
298
|
+
|
299
|
+
# Asynchronous send/receive
|
300
|
+
attach_function :nng_send_aio, [NngSocket.by_value, :nng_aio], :void
|
301
|
+
attach_function :nng_recv_aio, [NngSocket.by_value, :nng_aio], :void
|
302
|
+
|
303
|
+
# Statistics functions
|
304
|
+
attach_function :nng_stats_get, [:pointer], :int
|
305
|
+
attach_function :nng_stats_free, [:nng_stat], :void
|
306
|
+
attach_function :nng_stats_dump, [:nng_stat], :void
|
307
|
+
attach_function :nng_stat_next, [:nng_stat], :nng_stat
|
308
|
+
attach_function :nng_stat_child, [:nng_stat], :nng_stat
|
309
|
+
attach_function :nng_stat_name, [:nng_stat], :string
|
310
|
+
attach_function :nng_stat_type, [:nng_stat], :int
|
311
|
+
attach_function :nng_stat_unit, [:nng_stat], :int
|
312
|
+
attach_function :nng_stat_value, [:nng_stat], :uint64
|
313
|
+
attach_function :nng_stat_desc, [:nng_stat], :string
|
314
|
+
|
315
|
+
# Device functions (forwarder/reflector)
|
316
|
+
attach_function :nng_device, [NngSocket.by_value, NngSocket.by_value], :int
|
317
|
+
|
318
|
+
# Utility functions
|
319
|
+
attach_function :nng_sleep_aio, [:nng_duration, :nng_aio], :void
|
320
|
+
attach_function :nng_msleep, [:nng_duration], :void
|
321
|
+
|
322
|
+
# URL parsing
|
323
|
+
typedef :pointer, :nng_url
|
324
|
+
attach_function :nng_url_parse, [:pointer, :string], :int
|
325
|
+
attach_function :nng_url_free, [:nng_url], :void
|
326
|
+
attach_function :nng_url_clone, [:pointer, :nng_url], :int
|
327
|
+
|
328
|
+
# ============================================================================
|
329
|
+
# Protocol-specific functions (will be attached when protocol is loaded)
|
330
|
+
# ============================================================================
|
331
|
+
|
332
|
+
# These will be attached by specific protocol modules:
|
333
|
+
# - nng_pair0_open, nng_pair1_open
|
334
|
+
# - nng_push0_open, nng_pull0_open
|
335
|
+
# - nng_pub0_open, nng_sub0_open
|
336
|
+
# - nng_req0_open, nng_rep0_open
|
337
|
+
# - nng_surveyor0_open, nng_respondent0_open
|
338
|
+
# - nng_bus0_open
|
339
|
+
|
340
|
+
# ============================================================================
|
341
|
+
# Helper methods
|
342
|
+
# ============================================================================
|
343
|
+
|
344
|
+
# Check error code and raise exception if not OK
|
345
|
+
def self.check_error(ret, operation = "NNG operation")
|
346
|
+
return if ret == NNG_OK
|
347
|
+
error_msg = nng_strerror(ret)
|
348
|
+
raise NNG::Error, "#{operation} failed: #{error_msg} (code: #{ret})"
|
349
|
+
end
|
350
|
+
|
351
|
+
# Create initialized socket
|
352
|
+
def self.socket_initializer
|
353
|
+
NngSocket.new.tap { |s| s[:id] = 0 }
|
354
|
+
end
|
355
|
+
|
356
|
+
# Create initialized dialer
|
357
|
+
def self.dialer_initializer
|
358
|
+
NngDialer.new.tap { |d| d[:id] = 0 }
|
359
|
+
end
|
360
|
+
|
361
|
+
# Create initialized listener
|
362
|
+
def self.listener_initializer
|
363
|
+
NngListener.new.tap { |l| l[:id] = 0 }
|
364
|
+
end
|
365
|
+
|
366
|
+
# Create initialized context
|
367
|
+
def self.ctx_initializer
|
368
|
+
NngCtx.new.tap { |c| c[:id] = 0 }
|
369
|
+
end
|
370
|
+
|
371
|
+
# Create initialized pipe
|
372
|
+
def self.pipe_initializer
|
373
|
+
NngPipe.new.tap { |p| p[:id] = 0 }
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
data/lib/nng/message.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NNG
|
4
|
+
# NNG message wrapper
|
5
|
+
class Message
|
6
|
+
attr_reader :msg_ptr
|
7
|
+
|
8
|
+
# Create a new message
|
9
|
+
# @param size [Integer] initial size
|
10
|
+
def initialize(size: 0)
|
11
|
+
@msg_ptr = ::FFI::MemoryPointer.new(:pointer)
|
12
|
+
ret = FFI.nng_msg_alloc(@msg_ptr, size)
|
13
|
+
FFI.check_error(ret, "Allocate message")
|
14
|
+
@msg = @msg_ptr.read_pointer
|
15
|
+
@freed = false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Append data to message body
|
19
|
+
# @param data [String] data to append
|
20
|
+
# @return [self]
|
21
|
+
def append(data)
|
22
|
+
check_freed
|
23
|
+
data_str = data.to_s
|
24
|
+
data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
|
25
|
+
data_ptr.put_bytes(0, data_str)
|
26
|
+
|
27
|
+
ret = FFI.nng_msg_append(@msg, data_ptr, data_str.bytesize)
|
28
|
+
FFI.check_error(ret, "Append to message")
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Insert data at the beginning of message body
|
33
|
+
# @param data [String] data to insert
|
34
|
+
# @return [self]
|
35
|
+
def insert(data)
|
36
|
+
check_freed
|
37
|
+
data_str = data.to_s
|
38
|
+
data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
|
39
|
+
data_ptr.put_bytes(0, data_str)
|
40
|
+
|
41
|
+
ret = FFI.nng_msg_insert(@msg, data_ptr, data_str.bytesize)
|
42
|
+
FFI.check_error(ret, "Insert to message")
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get message body
|
47
|
+
# @return [String] message body
|
48
|
+
def body
|
49
|
+
check_freed
|
50
|
+
body_ptr = FFI.nng_msg_body(@msg)
|
51
|
+
length = FFI.nng_msg_len(@msg)
|
52
|
+
body_ptr.read_bytes(length)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get message body length
|
56
|
+
# @return [Integer] length in bytes
|
57
|
+
def length
|
58
|
+
check_freed
|
59
|
+
FFI.nng_msg_len(@msg)
|
60
|
+
end
|
61
|
+
alias size length
|
62
|
+
|
63
|
+
# Get message header
|
64
|
+
# @return [String] message header
|
65
|
+
def header
|
66
|
+
check_freed
|
67
|
+
header_ptr = FFI.nng_msg_header(@msg)
|
68
|
+
length = FFI.nng_msg_header_len(@msg)
|
69
|
+
header_ptr.read_bytes(length)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get message header length
|
73
|
+
# @return [Integer] length in bytes
|
74
|
+
def header_length
|
75
|
+
check_freed
|
76
|
+
FFI.nng_msg_header_len(@msg)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Append data to message header
|
80
|
+
# @param data [String] data to append
|
81
|
+
# @return [self]
|
82
|
+
def header_append(data)
|
83
|
+
check_freed
|
84
|
+
data_str = data.to_s
|
85
|
+
data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
|
86
|
+
data_ptr.put_bytes(0, data_str)
|
87
|
+
|
88
|
+
ret = FFI.nng_msg_header_append(@msg, data_ptr, data_str.bytesize)
|
89
|
+
FFI.check_error(ret, "Append to message header")
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
# Clear message body
|
94
|
+
# @return [self]
|
95
|
+
def clear
|
96
|
+
check_freed
|
97
|
+
FFI.nng_msg_clear(@msg)
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
# Clear message header
|
102
|
+
# @return [self]
|
103
|
+
def header_clear
|
104
|
+
check_freed
|
105
|
+
FFI.nng_msg_header_clear(@msg)
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
# Duplicate message
|
110
|
+
# @return [Message] duplicated message
|
111
|
+
def dup
|
112
|
+
check_freed
|
113
|
+
dup_ptr = ::FFI::MemoryPointer.new(:pointer)
|
114
|
+
ret = FFI.nng_msg_dup(dup_ptr, @msg)
|
115
|
+
FFI.check_error(ret, "Duplicate message")
|
116
|
+
|
117
|
+
new_msg = allocate
|
118
|
+
new_msg.instance_variable_set(:@msg, dup_ptr.read_pointer)
|
119
|
+
new_msg.instance_variable_set(:@msg_ptr, dup_ptr)
|
120
|
+
new_msg.instance_variable_set(:@freed, false)
|
121
|
+
new_msg
|
122
|
+
end
|
123
|
+
|
124
|
+
# Free the message
|
125
|
+
# @return [nil]
|
126
|
+
def free
|
127
|
+
return if @freed
|
128
|
+
FFI.nng_msg_free(@msg)
|
129
|
+
@freed = true
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# Check if message is freed
|
134
|
+
# @return [Boolean]
|
135
|
+
def freed?
|
136
|
+
@freed
|
137
|
+
end
|
138
|
+
|
139
|
+
# Get the internal message pointer (for use with send/recv)
|
140
|
+
# @return [FFI::Pointer]
|
141
|
+
def to_ptr
|
142
|
+
@msg
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def check_freed
|
148
|
+
raise StateError, "Message has been freed" if @freed
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NNG
|
4
|
+
# Protocol-specific socket opening functions
|
5
|
+
module Protocols
|
6
|
+
# Dynamically load protocol functions from libnng
|
7
|
+
def self.attach_protocol_functions
|
8
|
+
extend ::FFI::Library
|
9
|
+
|
10
|
+
# Use the same library as FFI module
|
11
|
+
ffi_lib NNG::FFI.loaded_lib_path
|
12
|
+
|
13
|
+
# Pair protocols
|
14
|
+
attach_function :nng_pair0_open, [FFI::NngSocket.by_ref], :int
|
15
|
+
attach_function :nng_pair0_open_raw, [FFI::NngSocket.by_ref], :int
|
16
|
+
attach_function :nng_pair1_open, [FFI::NngSocket.by_ref], :int
|
17
|
+
attach_function :nng_pair1_open_raw, [FFI::NngSocket.by_ref], :int
|
18
|
+
attach_function :nng_pair1_open_poly, [FFI::NngSocket.by_ref], :int
|
19
|
+
|
20
|
+
# Push/Pull protocols
|
21
|
+
attach_function :nng_push0_open, [FFI::NngSocket.by_ref], :int
|
22
|
+
attach_function :nng_push0_open_raw, [FFI::NngSocket.by_ref], :int
|
23
|
+
attach_function :nng_pull0_open, [FFI::NngSocket.by_ref], :int
|
24
|
+
attach_function :nng_pull0_open_raw, [FFI::NngSocket.by_ref], :int
|
25
|
+
|
26
|
+
# Pub/Sub protocols
|
27
|
+
attach_function :nng_pub0_open, [FFI::NngSocket.by_ref], :int
|
28
|
+
attach_function :nng_pub0_open_raw, [FFI::NngSocket.by_ref], :int
|
29
|
+
attach_function :nng_sub0_open, [FFI::NngSocket.by_ref], :int
|
30
|
+
attach_function :nng_sub0_open_raw, [FFI::NngSocket.by_ref], :int
|
31
|
+
|
32
|
+
# Req/Rep protocols
|
33
|
+
attach_function :nng_req0_open, [FFI::NngSocket.by_ref], :int
|
34
|
+
attach_function :nng_req0_open_raw, [FFI::NngSocket.by_ref], :int
|
35
|
+
attach_function :nng_rep0_open, [FFI::NngSocket.by_ref], :int
|
36
|
+
attach_function :nng_rep0_open_raw, [FFI::NngSocket.by_ref], :int
|
37
|
+
|
38
|
+
# Surveyor/Respondent protocols
|
39
|
+
attach_function :nng_surveyor0_open, [FFI::NngSocket.by_ref], :int
|
40
|
+
attach_function :nng_surveyor0_open_raw, [FFI::NngSocket.by_ref], :int
|
41
|
+
attach_function :nng_respondent0_open, [FFI::NngSocket.by_ref], :int
|
42
|
+
attach_function :nng_respondent0_open_raw, [FFI::NngSocket.by_ref], :int
|
43
|
+
|
44
|
+
# Bus protocol
|
45
|
+
attach_function :nng_bus0_open, [FFI::NngSocket.by_ref], :int
|
46
|
+
attach_function :nng_bus0_open_raw, [FFI::NngSocket.by_ref], :int
|
47
|
+
rescue ::FFI::NotFoundError => e
|
48
|
+
# Some protocols might not be available in all builds
|
49
|
+
warn "Warning: Some NNG protocol functions not available: #{e.message}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Protocol name to function mapping
|
53
|
+
PROTOCOL_FUNCTIONS = {
|
54
|
+
pair0: :nng_pair0_open,
|
55
|
+
pair1: :nng_pair1_open,
|
56
|
+
pair: :nng_pair1_open, # alias for pair1
|
57
|
+
push0: :nng_push0_open,
|
58
|
+
push: :nng_push0_open,
|
59
|
+
pull0: :nng_pull0_open,
|
60
|
+
pull: :nng_pull0_open,
|
61
|
+
pub0: :nng_pub0_open,
|
62
|
+
pub: :nng_pub0_open,
|
63
|
+
sub0: :nng_sub0_open,
|
64
|
+
sub: :nng_sub0_open,
|
65
|
+
req0: :nng_req0_open,
|
66
|
+
req: :nng_req0_open,
|
67
|
+
rep0: :nng_rep0_open,
|
68
|
+
rep: :nng_rep0_open,
|
69
|
+
surveyor0: :nng_surveyor0_open,
|
70
|
+
surveyor: :nng_surveyor0_open,
|
71
|
+
respondent0: :nng_respondent0_open,
|
72
|
+
respondent: :nng_respondent0_open,
|
73
|
+
bus0: :nng_bus0_open,
|
74
|
+
bus: :nng_bus0_open
|
75
|
+
}.freeze
|
76
|
+
|
77
|
+
# Open a socket for the given protocol
|
78
|
+
# @param protocol [Symbol] protocol name (:pair0, :pair1, :push, :pull, etc.)
|
79
|
+
# @param raw [Boolean] open in raw mode
|
80
|
+
# @return [FFI::NngSocket] opened socket
|
81
|
+
def self.open_socket(protocol, raw: false)
|
82
|
+
func_name = PROTOCOL_FUNCTIONS[protocol]
|
83
|
+
raise ArgumentError, "Unknown protocol: #{protocol}" unless func_name
|
84
|
+
|
85
|
+
func_name = "#{func_name}_raw".to_sym if raw
|
86
|
+
|
87
|
+
socket = FFI.socket_initializer
|
88
|
+
ret = send(func_name, socket)
|
89
|
+
FFI.check_error(ret, "Opening #{protocol} socket")
|
90
|
+
socket
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Auto-load protocol functions
|
96
|
+
NNG::Protocols.attach_protocol_functions
|