sping 1.0.4 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f7679b3fb31d07d034182114e576f3e272ef8223954f592bb30a98378b98fd5
4
- data.tar.gz: b8d21d6a58ffb3c57481cc421d61f8fc25da32646fb8d5ae160829017d01fb14
3
+ metadata.gz: 6dda8939b3290683a544ac5ba7192ca3ceab50489b3dbd62a0dd04696f6a7786
4
+ data.tar.gz: de774cc1260933cdcd386e39cfd040e0af8510ba9e73e30f615e2f8da27c074f
5
5
  SHA512:
6
- metadata.gz: a6a2c6897ead8f7371b2f1a83719c1a8f2c07d44957a9c302bc548855d25671858435fe0d6e8162e14737e065f6bbcede37defd2ea21e23561d19e7f8d5ef63f
7
- data.tar.gz: 2618e70abea52acc38364f7fb431dafa12a4d48ad8f07665f93ac76e316784e6a2f8f5d2b463a55053da88b10f46dc23adf16846abb1a4b1d4677eb9eef09e7f
6
+ metadata.gz: e100242edcfe0e85965c6520dc9e95bdcab2c1aea58e5df338eb1dba9efb2dca1b6de0c453a333aa83553b9187d62b731f6fbd4dd81510ee600572b7f0d8e3a5
7
+ data.tar.gz: 0d01578a50563b1e7f1de1219423e00f66ecffec49ccf25b74e0052df858dc317c71bea80dcec2e17762d66b4b7b97322d8a389484dc72ae158157de605098b0
data/README.md CHANGED
@@ -1,3 +1,18 @@
1
1
  # sping
2
2
 
3
- This is a reimplementation of [sping](https://github.com/benjojo/sping/) in Ruby.
3
+ Program for measuring asymmetric latencies.
4
+
5
+ This is a reimplementation in Ruby of the reference implementation of the sping protocol in Go. The program provides both the client and server part to measure asymmetric latencies between two peers.
6
+
7
+ ## Protocol
8
+
9
+ The protocol consists of three phases:
10
+ 1. A TCP handshake
11
+ 2. A UDP handshake
12
+ 2. Sending the pings
13
+
14
+ The TCP handshake is performed first. Here the peers negotiate a session ID. To do this, the client sends a request to the server and then invites it to a session. If the server accepts the invitation, it sends a session ID to the client. This is used to uniquely identify the connection between the peers. For example, a peer may change its port or IP address. The session ID is used so that the peers can continue to identify each other. The session is activated at the end of the TCP handshake.
15
+
16
+ A UDP handshake is then performed. In this, the client sends a UDP packet, which is encoded with [MessagePack](https://msgpack.org/), to the server. The server sends the packet back to the client for confirmation. One of the purposes of this is to punch a hole in a firewall so that it allows UDP packets between the peers. After a successful UDP handshake, the session is double activated. Only then does the sending of ping packets begin.
17
+
18
+ Finally, the peers send pings to each other (in this case UDP packets containing time messages). These time messages contain, among other things, the time at which the message was sent, but also the last 32 time messages received from the peer. This information can be used to determine both the asymmetric latency and the asymmetric packet loss.
data/lib/errors.rb CHANGED
@@ -1,20 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
  # sharable_constant_value: literal
3
3
 
4
+ # SPing / Splitted Ping is a protocol for measuring asymmetric latencies.
4
5
  module SPing
6
+ # Generic SPing error.
5
7
  class SPingError < StandardError; end
6
8
 
9
+ # Error that occurred while managing the sessions.
7
10
  class SessionManagerError < SPingError; end
11
+
12
+ # Error that is thrown when there are not enough session IDs available to establish a
13
+ # connection to a peer or to accept new ones.
14
+ # This indicates either a programmer error or an overload of the server.
8
15
  class OutOfSessions < SessionManagerError; end
9
16
 
17
+ # Error that occurs in connection with a session.
10
18
  class SessionError < SPingError; end
19
+
20
+ # Error during the TCP handshake
11
21
  class TCPHandshakeError < SessionError; end
12
22
 
23
+ # Error due to the peer (i.e. not self-inflicted).
13
24
  class PeerError < SPingError; end
25
+
26
+ # Package was not coded correctly.
14
27
  class InvalidPacketError < PeerError; end
28
+
29
+ # The package was coded correctly, but the content is invalid.
15
30
  class InvalidMessageError < InvalidPacketError; end
31
+
32
+ # A packet has been received which cannot be clearly assigned to SPing
33
+ # and therefore cannot be processed further.
16
34
  class UnknownPacketError < InvalidPacketError; end
35
+
36
+ # The peer has signaled an error.
17
37
  class SignalizedError < PeerError; end
18
- class UnknownVersionError < PeerError; end
38
+
39
+ # The peer uses a version of sping that is not supported.
40
+ class UnsupportedVersionError < PeerError; end
41
+
42
+ # The package could not be assigned to a current session.
19
43
  class UnknownSessionError < PeerError; end
20
44
  end
data/lib/last_acks.rb CHANGED
@@ -1,16 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
  # sharable_constant_value: literal
3
3
 
4
+ # SPing / Splitted Ping is a protocol for measuring asymmetric latencies.
4
5
  module SPing
6
+ # A kind of circular buffer, which consists of 32 acks at any given time. If no acks are available,
7
+ # empty ones are created. If a new ack is added (which would result in 33 acks), one is removed.
5
8
  class LastAcks
6
- attr_reader :size
7
-
8
- def initialize(size = 32)
9
- @size = size
9
+ # Creates a new circular buffer for acks
10
+ def initialize
11
+ # Array in which the acks are stored.
10
12
  @acks = []
13
+ # Mutex, which ensures that the array is not accessed simultaneously.
11
14
  @acks_mutex = Mutex.new
12
15
 
13
- @size.times do |index|
16
+ # Initiallize the circular buffer with 32 empty acks.
17
+ 32.times do |index|
14
18
  @acks[index] = {
15
19
  'R' => 0,
16
20
  'U' => Time.at(0),
@@ -19,17 +23,20 @@ module SPing
19
23
  end
20
24
  end
21
25
 
26
+ # Returns a copy of the last 32 acks.
27
+ # @return [Array] Last 32 Acks
22
28
  def acks
23
29
  @acks_mutex.synchronize do
24
30
  return @acks.dup
25
31
  end
26
32
  end
27
33
 
34
+ # Adds a new ack and deletes the oldest one
35
+ # @param ack [Hash] New ack to be added
28
36
  def add_ack(ack)
29
37
  @acks_mutex.synchronize do
30
38
  @acks << ack
31
-
32
- @acks.shift if @acks.length > @size
39
+ @acks.shift
33
40
  end
34
41
  end
35
42
  end
data/lib/session.rb CHANGED
@@ -1,68 +1,122 @@
1
1
  # frozen_string_literal: true
2
2
  # sharable_constant_value: literal
3
3
 
4
+ # SPing / Splitted Ping is a protocol for measuring asymmetric latencies.
4
5
  module SPing
6
+ # Inclusion of all possible SPing-specific errors.
5
7
  require_relative 'errors'
6
8
 
9
+ # Models or represents a session with a peer.
7
10
  class Session
8
- attr_reader :created, :last_rx, :madebyme
9
- attr_accessor :session_id, :tcp_handshake_complete, :udp_handshake_complete
11
+ # Time when the session was created. This is necessary so that the session GC knows when a
12
+ # non-initialized session can be deleted.
13
+ # @return [Time]
14
+ attr_reader :created
15
+ # Time when the last ping packet was received by the peer. This is important so that the session
16
+ # GC knows when a session is considered inactive and can be deleted.
17
+ # @return [Time]
18
+ attr_reader :last_rx
19
+ # Indicates whether the session has been initialized. true if we have initiated it.
20
+ # false if the peer has initiated it.
21
+ # @return [TrueClass, FalseClass]
22
+ attr_reader :madebyme
23
+ # Indicates whether a TCP handshake has been carried out (successfully).
24
+ # @return [TrueClass, FalseClass]
25
+ attr_reader :tcp_handshake_complete
26
+ # Indicates whether a UDP handshake has been carried out (successfully).
27
+ # @return [TrueClass, FalseClass]
28
+ attr_reader :udp_handshake_complete
29
+ # The session ID in the range from 0 to 2**32 - 1.
30
+ # @return [Integer]
31
+ attr_reader :session_id
10
32
 
11
33
  require 'socket'
12
34
  require 'timeout'
13
35
  require 'msgpack'
14
36
  require_relative 'last_acks'
15
37
 
38
+ # Creates a new session
39
+ # @param host [#to_s] Host of the peer
40
+ # @param port [#to_i] Port of the peer
41
+ # @param socket [UDPSocket] UDP socket via which the pings and the UDP handshake are to be sent.
42
+ # @param madebyme [TrueClass, FalseClass] Indicates whether we have initiated the session.
16
43
  def initialize(host, port, socket, madebyme)
17
- @host = host
18
- @port = port
44
+ # Assign the passed parameters to instance variables.
45
+ @host = host.to_s
46
+ @port = port.to_i
19
47
  @socket = socket
20
- @created = Time.now
21
48
  @madebyme = madebyme
22
49
 
50
+ # Initialize assignment of other instance variables
51
+ @created = Time.now
52
+ @double_activated = nil
53
+
23
54
  @tcp_handshake_complete = false
24
55
  @udp_handshake_complete = false
25
56
 
26
57
  @last_acks = SPing::LastAcks.new
27
58
  end
28
59
 
60
+ # Initiates a TCP handshake.
61
+ # @param socket [TCPSocket] Socket via which the handshake is to be sent
62
+ # (and responses are to be expected).
63
+ # @param session_id [Integer] Session ID which is to be sent to the peer
64
+ # and which is to be used for the current session.
29
65
  def do_tcp_handshake1(socket, session_id)
66
+ # Send the banner
30
67
  socket.write "sping-0.3-https://codeberg.org/mark22k/sping\r\n"
68
+ # and make sure that it is no longer in the send buffer.
31
69
  socket.flush
32
70
 
71
+ # See if the peer invites us to create a session with them.
33
72
  invite = socket.readpartial 6
73
+ unless invite.chomp == 'INVITE'
74
+ socket.write "I_DONT_UNDERSTAND\r\n"
75
+ raise TCPHandshakeError, 'Peer didn\'t invite us.'
76
+ end
34
77
 
35
- raise TCPHandshakeError, 'Peer didn\'t invite us.' unless invite.chomp == 'INVITE'
36
-
78
+ # Send the session ID
37
79
  @session_id = session_id
38
-
39
80
  socket.write "#{session_id}\r\n"
81
+ # and make sure that it is no longer in the send buffer.
40
82
  socket.flush
41
83
 
84
+ # This means that the TCP handshake is successful. The socket can be closed
85
+ # and the corresponding instance variable can be set.
42
86
  socket.close
43
87
 
44
88
  @tcp_handshake_complete = true
45
89
  end
46
90
 
91
+ # Receives a TCP handshake from a peer.
92
+ # The peer specified when the session is created is consulted for this purpose.
47
93
  def do_tcp_handshake2
94
+ # Establish a connection and receive the banner.
48
95
  socket = TCPSocket.new @host, @port
96
+
97
+ # If the banner is too large, close the socket and throw an error.
98
+ # The handshake was not successful.
49
99
  banner = socket.readpartial 9001
50
100
  if banner.length > 9000
51
101
  socket.close
52
102
  raise TCPHandshakeError, 'Host banner too big'
53
103
  end
54
104
 
105
+ # If the banner does not match the SPing service, close the socket and
106
+ # throw an error. The handshake was not successful.
55
107
  unless banner.start_with? 'sping-0.3-'
56
108
  socket.close
57
- raise TCPHandshakeError, 'Host banner not sping'
109
+ raise TCPHandshakeError, 'Host banner not sping or unsupported version of sping.'
58
110
  end
59
111
 
60
112
  @remote_version = banner.chomp
61
113
  $logger.info "Peer uses the following program version: #{@remote_version.dump}"
62
114
 
115
+ # Invite the peer to start a session with us.
63
116
  socket.write "INVITE\r\n"
64
117
  socket.flush
65
118
 
119
+ # If the session ID is too long or none was received, close the socket and throw an error.
66
120
  invite_buf = socket.readpartial 32
67
121
  if invite_buf.length > 31 || invite_buf.empty?
68
122
  socket.close
@@ -76,15 +130,21 @@ module SPing
76
130
  @tcp_handshake_complete = true
77
131
  end
78
132
 
133
+ # Sets the endpoint consisting of the host and port of the remote end. This is done
134
+ # each time a new packet is received and ensures that the current endpoint is always available.
135
+ # @param host [#to_s]
136
+ # @param port [#to_i]
79
137
  def set_endpoint(host, port)
80
- @host = host
81
- @port = port
138
+ @host = host.to_s
139
+ @port = port.to_i
82
140
  end
83
141
 
84
- def start_udp_handshake_sender(send_interval = 5)
142
+ # Starts a thread which sends the UDP handshake at regular intervals.
143
+ # @param send_interval [#to_i] The interval at which the UDP handshakes are to be sent.
144
+ def start_udp_handshake_sender(send_interval = 1)
85
145
  raise 'UDP handshake sender is already running.' if @udp_handshake_sender
86
146
 
87
- @udp_handshake_sender = Thread.new(send_interval) do |th_send_interval|
147
+ @udp_handshake_sender = Thread.new(send_interval.to_i) do |th_send_interval|
88
148
  loop do
89
149
  send_udp_handshake
90
150
  sleep th_send_interval
@@ -92,6 +152,7 @@ module SPing
92
152
  end
93
153
  end
94
154
 
155
+ # Send a single UDP handshake
95
156
  def send_udp_handshake
96
157
  packet = {
97
158
  'Y' => 'h'.ord,
@@ -104,6 +165,7 @@ module SPing
104
165
  @socket.send packet, 0, @host, @port
105
166
  end
106
167
 
168
+ # Stop the UDP handshake sender. UDP handshakes are then no longer sent at any interval.
107
169
  def stop_udp_handshake_sender
108
170
  raise 'UDP handshake sender is not running and therefore cannot be terminated.' unless @udp_handshake_sender
109
171
 
@@ -111,11 +173,14 @@ module SPing
111
173
  @udp_handshake_sender = nil
112
174
  end
113
175
 
176
+ # Informs the session that a UDP handshake has been received.
114
177
  def udp_handshake_recived
115
178
  stop_udp_handshake_sender if @madebyme
116
179
  @udp_handshake_complete = true
180
+ @double_activated = Time.now
117
181
  end
118
182
 
183
+ # Starts a thread which sends ping or time messages to the peer at one-second intervals.
119
184
  def start_pinger
120
185
  raise 'Pinger is already running.' if @pinger
121
186
 
@@ -127,6 +192,7 @@ module SPing
127
192
  end
128
193
  end
129
194
 
195
+ # Stops the thread, which sends ping or time messages to the peer at regular intervals.
130
196
  def stop_pinger
131
197
  raise 'Pinger sender is not running and therefore cannot be terminated.' unless @pinger
132
198
 
@@ -134,6 +200,7 @@ module SPing
134
200
  @pinger = nil
135
201
  end
136
202
 
203
+ # Sends a ping message to the peer.
137
204
  def ping
138
205
  current_id = (Time.now.to_i % 255) + 1
139
206
  data = {
@@ -150,11 +217,16 @@ module SPing
150
217
  @socket.send data, 0, @host, @port
151
218
  end
152
219
 
220
+ # Stops all threads associated with the session. This means that the session to the peer is as good as dead.
153
221
  def stop
154
222
  @pinger&.kill
155
223
  @udp_handshake_sender&.kill
156
224
  end
157
225
 
226
+ # Handler that receives and processes a receiving ping packet or time message. The current statistics are output.
227
+ # @param packet [Hash]
228
+ # @param rxtime [Time]
229
+ # @param peeraddr [#to_s]
158
230
  def handle_ping(packet, rxtime, peeraddr)
159
231
  if !(packet.keys - %w[M Y E I T A S]).empty? ||
160
232
  # M, Y are already checked in handle_packet from session manager
@@ -211,11 +283,17 @@ module SPing
211
283
  rx_loss = 0
212
284
  exchanges = 0
213
285
 
214
- if remote_last_acks.length == 32
286
+ last_local_acks = @last_acks.acks
287
+
288
+ # A calculation can only take place when enough data is available, i.e. when:
289
+ # - We have sent 32 time messages (this is the case after 32 seconds)
290
+ # - The peer has sent 32 time messages (this is the case after 32 seconds)
291
+ # - If there is a time message (this is the case when we are in this function)
292
+ if @double_activated && (Time.now - @double_activated).to_i > 32
215
293
  # We have enough data
216
294
  exchanges = 32
217
295
  remote_acks = remote_last_acks.map { |ack| ack['R'] }
218
- local_acks = @last_acks.acks.map { |ack| ack['R'] }
296
+ local_acks = last_local_acks.map { |ack| ack['R'] }
219
297
 
220
298
  tip_id = (Time.now.to_i % 255) + 1
221
299
  starting = tip_id - 32
@@ -1,17 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
  # sharable_constant_value: literal
3
3
 
4
+ # SPing / Splitted Ping is a protocol for measuring asymmetric latencies.
4
5
  module SPing
6
+ # Inclusion of all possible SPing-specific errors.
5
7
  require_relative 'errors'
6
8
 
9
+ # Container, which contains a collection of sessions and manages them.
7
10
  class SessionManager
8
11
  require 'socket'
9
12
  require 'timeout'
10
13
  require_relative 'session'
11
14
 
15
+ # Creates a new Session Manager, which can (and will) manage a number of sessions.
16
+ # @param host [#to_s] Host to which messages are to be bound and from which messages are sent accordingly.
17
+ # @param port [#to_i] Port
12
18
  def initialize(host = '::', port = 6924)
13
- @host = host
14
- @port = port
19
+ @host = host.to_s
20
+ @port = port.to_i
15
21
 
16
22
  @sessions = {}
17
23
  @sessions_mutex = Mutex.new
@@ -20,9 +26,12 @@ module SPing
20
26
  @socket.bind @host, @port
21
27
  end
22
28
 
29
+ # Initiates a new session with a peer.
30
+ # @param host [#to_s]
31
+ # @param port [#to_s, #to_i]
23
32
  def new_session(host, port = 6924)
24
33
  $logger.info "Add new session for host #{host} port #{port}."
25
- session = SPing::Session.new host, port, @socket, true
34
+ session = SPing::Session.new host.to_s, port.to_i, @socket, true
26
35
 
27
36
  counter = 0
28
37
  loop do
@@ -48,6 +57,8 @@ module SPing
48
57
  $logger.error "Out of session: #{e.message}"
49
58
  end
50
59
 
60
+ # Stops all threads connected to a session ID and removes the session from the Session Manager administration.
61
+ # @param session_id [Integer] Session ID of the session to be removed.
51
62
  def del_session(session_id)
52
63
  $logger.debug "Delete session with session id #{session_id}"
53
64
 
@@ -57,18 +68,21 @@ module SPing
57
68
  end
58
69
  end
59
70
 
71
+ # Stops all sessions or the threads that are connected to them.
60
72
  def stop_sessions
61
73
  @sessions.each do |_session_id, session|
62
74
  session.stop
63
75
  end
64
76
  end
65
77
 
78
+ # Waits and blocks until the Session Manager is no longer running.
66
79
  def join
67
80
  @runner&.join
68
81
  @gc_thread&.join
69
82
  @server_thread&.join
70
83
  end
71
84
 
85
+ # Starts the Session Manager.
72
86
  def run
73
87
  raise 'Client is already running.' if @runner
74
88
  raise 'GC is already running.' if @gc
@@ -88,6 +102,7 @@ module SPing
88
102
  end
89
103
  end
90
104
 
105
+ # Starts the server functionality of the Session Manager.
91
106
  def run_server
92
107
  raise 'Server is already running.' if @server_thread
93
108
  raise 'Server already exist.' if @server
@@ -105,6 +120,7 @@ module SPing
105
120
  return @server_thread
106
121
  end
107
122
 
123
+ # Stops the server functionality of the Session Manager.
108
124
  def stop_server
109
125
  raise 'Server thread is not running.' unless @server_thread
110
126
 
@@ -117,6 +133,7 @@ module SPing
117
133
  @server = nil
118
134
  end
119
135
 
136
+ # Stops the Session Manager. The server functionality must be stopped separately.
120
137
  def stop
121
138
  raise 'Session manager is not running.' unless @runner
122
139
 
@@ -129,6 +146,12 @@ module SPing
129
146
  @gc_thread = nil
130
147
  end
131
148
 
149
+ private
150
+
151
+ # Generates a new session ID that was not yet in use at the time of the check.
152
+ # If none could be generated within half a minute - for example because all
153
+ # session IDs have already been assigned - an error is thrown.
154
+ # @return [Integer]
132
155
  def generate_session_id
133
156
  Timeout.timeout(30, OutOfSessions, 'No session ID could be generated which has not yet been used.') do
134
157
  loop do
@@ -138,19 +161,23 @@ module SPing
138
161
  end
139
162
  end
140
163
 
141
- private
142
-
164
+ # Removes obsolete sessions. This includes sessions that are older than one minute but
165
+ # have not been activated a double time. And sessions that have not
166
+ # received any packets for half a minute.
143
167
  def do_gc
144
168
  $logger.debug 'Remove outdated sessions.'
145
169
  # rubocop:disable Style/HashEachMethods
146
- # You cannot run through a map and edit it at the same time. Since this is necessary due to the threading,
170
+ # You cannot run through a map and edit it at the same time. Since this is
171
+ # necessary due to the threading,
147
172
  # a snapshot of the key variable is run through.
148
173
  @sessions.keys.each do |session_id|
149
174
  # rubocop:enable Style/HashEachMethods
150
175
 
151
176
  session = @sessions[session_id]
152
- # It is possible that sessions no longer exist here, as we may have session IDs that have already been deleted.
153
- # However, we can ignore this aspect here, as we are the only function that deletes sessions.
177
+ # It is possible that sessions no longer exist here, as we may have
178
+ # session IDs that have already been deleted.
179
+ # However, we can ignore this aspect here, as we are the only
180
+ # function that deletes sessions.
154
181
  if !(session.tcp_handshake_complete && session.udp_handshake_complete)
155
182
  # Handshake incomplete
156
183
  if (Time.now.to_i - session.created.to_i) > 60
@@ -159,13 +186,16 @@ module SPing
159
186
  del_session session_id
160
187
  end
161
188
  elsif (Time.now.to_i - session.last_rx.to_i) > 30
162
- # 30 seconds have elapsed since the last ping from the peer was received. The peer is probably dead.
189
+ # 30 seconds have elapsed since the last ping from the peer was received.
190
+ # The peer is probably dead.
163
191
  $logger.debug "Session id #{session_id} without activity for over thirty seconds."
164
192
  del_session session_id
165
193
  end
166
194
  end
167
195
  end
168
196
 
197
+ # Receives a TCP handshake for a session and then has it
198
+ # managed by the Session Manager.
169
199
  def request_session(session)
170
200
  session.do_tcp_handshake2
171
201
 
@@ -179,6 +209,9 @@ module SPing
179
209
  return nil
180
210
  end
181
211
 
212
+ # Handler for a received packet. The function catches errors and outputs them.
213
+ # It also forwards the packet to the corresponding handler according to the
214
+ # session ID and type of packet.
182
215
  def handle_packet(buf)
183
216
  rxtime = Time.now
184
217
 
@@ -217,6 +250,7 @@ module SPing
217
250
  $logger.error "Perr error: #{e.message}"
218
251
  end
219
252
 
253
+ # Handler for a receiving UDP handshake.
220
254
  def handle_udp_handshake(packet, peeraddr, peerport)
221
255
  if !(packet.keys - %w[M Y V S]).empty? ||
222
256
  # M, Y are already checked in handle_packet from session manager
@@ -227,7 +261,7 @@ module SPing
227
261
  session_id = packet['S']
228
262
  session = @sessions[session_id]
229
263
  raise UnknownSessionError, 'UDP handshake received for uninitiated session.' unless session
230
- raise UnknownVersionError, "UDP handshake uses an unsupported version: #{packet['V']}" if packet['V'] != 3
264
+ raise UnsupportedVersionError, "UDP handshake uses an unsupported version: #{packet['V']}" if packet['V'] != 3
231
265
 
232
266
  session.set_endpoint peeraddr, peerport
233
267
 
@@ -249,6 +283,7 @@ module SPing
249
283
  session.start_pinger
250
284
  end
251
285
 
286
+ # Handler for a receiving ping or a receiving time message.
252
287
  def handle_ping(packet, rxtime, peeraddr, peerport)
253
288
  session_id = packet['S']
254
289
  session = @sessions[session_id]
@@ -260,6 +295,7 @@ module SPing
260
295
  session.handle_ping packet, rxtime, peeraddr
261
296
  end
262
297
 
298
+ # Handler to establish a session for the request of a peer.
263
299
  def handle_client(client)
264
300
  host = client.peeraddr[3]
265
301
  port = client.peeraddr[1]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sping
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marek Küthe
@@ -30,8 +30,9 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.7.2
33
- description: This is a reimplementation of [sping](https://github.com/benjojo/sping/)
34
- in Ruby.
33
+ description: This is a reimplementation in Ruby of the reference implementation of
34
+ the sping protocol in Go. The program provides both the client and server part to
35
+ measure asymmetric latencies between two peers.
35
36
  email: m.k@mk16.de
36
37
  executables:
37
38
  - sping
@@ -72,5 +73,5 @@ requirements: []
72
73
  rubygems_version: 3.4.22
73
74
  signing_key:
74
75
  specification_version: 4
75
- summary: Reimplementation of sping in Ruby.
76
+ summary: Program for measuring asymmetric latencies.
76
77
  test_files: []