@@ -146,37 +146,32 @@ class ProtoSession : public NetUtil::Asio::SSLSession, public FBE::proto::FinalC
146146
147147bool ProtoServer::hasConnected (const std::string &ip)
148148{
149- // std::cout << "check hasConnected: " << ip << std::endl ;
149+ bool found = false ;
150150
151151 // Use consistent lock ordering to avoid deadlock: always acquire mapping_lock before session_lock
152- std::shared_lock<std::shared_mutex> mapping_locker (_ipmapping_lock);
153- std::shared_lock<std::shared_mutex> session_locker (_sessionids_lock);
154-
155- // print all session ids
156- // for (auto &session : _session_ids) {
157- // std::cout << "session ip: " << session.first << " id: " << session.second << std::endl;
158- // }
159-
160- // First try to find by direct IP match
161- auto direct_it = _session_ids.find (ip);
162- if (direct_it != _session_ids.end ()) {
163- std::cout << " hasConnected: " << ip << " true (direct match)" << std::endl;
164- return true ;
165- }
152+ {
153+ std::shared_lock<std::shared_mutex> mapping_locker (_ipmapping_lock);
154+ std::shared_lock<std::shared_mutex> session_locker (_sessionids_lock);
166155
167- // If not found, try to find via IP mapping (for NAT/Router scenarios)
168- // Check if this IP is a real IP that maps to a remote endpoint IP
169- auto real_to_remote = _real_to_remote_ip.find (ip);
170- if (real_to_remote != _real_to_remote_ip.end ()) {
171- auto session_it = _session_ids.find (real_to_remote->second );
172- if (session_it != _session_ids.end ()) {
173- std::cout << " hasConnected: " << ip << " true (via IP mapping: " << real_to_remote->second << " )" << std::endl;
174- return true ;
156+ found = _session_ids.find (ip) != _session_ids.end ();
157+
158+ // If not found, try via IP mapping (for NAT/Router scenarios)
159+ if (!found) {
160+ auto real_to_remote = _real_to_remote_ip.find (ip);
161+ if (real_to_remote != _real_to_remote_ip.end ())
162+ found = _session_ids.find (real_to_remote->second ) != _session_ids.end ();
175163 }
176164 }
177165
178- std::cout << " hasConnected: " << ip << " false" << std::endl;
179- return false ;
166+ if (!found) {
167+ std::cout << " hasConnected: " << ip << " false" << std::endl;
168+ return false ;
169+ }
170+
171+ // A registered session may still be completing its TLS handshake; onSend
172+ // reports that as a send failure, which lets the caller retry.
173+ std::cout << " hasConnected: " << ip << " true" << std::endl;
174+ return true ;
180175}
181176
182177bool ProtoServer::startHeartbeat ()
@@ -315,8 +310,9 @@ void ProtoServer::onConnected(std::shared_ptr<NetUtil::Asio::SSLSession>& sessio
315310
316311 {
317312 std::unique_lock<std::shared_mutex> locker (_sessionids_lock);
318- // std::cout << "Server onConnected insert session ip: " << addr << " id: " << session->id() << std::endl;
319- _session_ids.insert (std::make_pair (addr, session->id ()));
313+ // Keep every session: a reconnect can register a new session before
314+ // the old one is torn down. Routing always picks the newest one.
315+ _session_ids.emplace (addr, session->id ());
320316 }
321317
322318 _callbacks->onStateChanged (RPC_CONNECTED , addr);
@@ -329,36 +325,47 @@ void ProtoServer::onDisconnected(std::shared_ptr<NetUtil::Asio::SSLSession>& ses
329325 auto search_uuid = session->id ();
330326 std::string addr = " " ;
331327 std::string real_ip = " " ;
332-
328+ bool replaced = false ;
329+
333330 // Use consistent lock ordering: mapping_lock before session_lock to avoid deadlock
334331 {
335332 std::unique_lock<std::shared_mutex> mapping_locker (_ipmapping_lock);
336333 std::unique_lock<std::shared_mutex> session_locker (_sessionids_lock);
337-
334+
338335 // Find and remove session
339336 auto it = std::find_if (_session_ids.begin (), _session_ids.end (), [search_uuid](const std::pair<std::string, BaseKit::UUID >& pair) {
340337 return pair.second == search_uuid;
341338 });
342339
343340 if (it != _session_ids.end ()) {
344- // std::cout << "find connected by uuid, ip:" << it->first << std::endl;
345341 addr = it->first ;
346342 _session_ids.erase (it);
347-
348- // Clean up IP mapping for this remote endpoint
349- auto remote_to_real = _remote_to_real_ip.find (addr);
350- if (remote_to_real != _remote_to_real_ip.end ()) {
351- real_ip = remote_to_real->second ;
352- _real_to_remote_ip.erase (real_ip);
353- _remote_to_real_ip.erase (addr);
354- std::cout << " Cleaned up IP mapping for disconnected session: " << real_ip << " -> " << addr << std::endl;
343+
344+ // A newer session for the same peer may already be connected.
345+ // Keep its routing state and do not report the peer as gone.
346+ replaced = _session_ids.find (addr) != _session_ids.end ();
347+
348+ if (!replaced) {
349+ // Clean up IP mapping for this remote endpoint
350+ auto remote_to_real = _remote_to_real_ip.find (addr);
351+ if (remote_to_real != _remote_to_real_ip.end ()) {
352+ real_ip = remote_to_real->second ;
353+ _real_to_remote_ip.erase (real_ip);
354+ _remote_to_real_ip.erase (addr);
355+ std::cout << " Cleaned up IP mapping for disconnected session: " << real_ip << " -> " << addr << std::endl;
356+ }
355357 }
356358 } else {
357359 std::cout << " did not find connected id:" << search_uuid << std::endl;
358360 return ;
359361 }
360362 }
361363
364+ if (replaced) {
365+ std::cout << " Session superseded, keep connection state for: " << addr << std::endl;
366+ return ;
367+ }
368+
362369 _callbacks->onStateChanged (RPC_DISCONNECTED , addr);
363370}
364371
@@ -372,40 +379,51 @@ size_t ProtoServer::onSend(const void *data, size_t size)
372379 return size;
373380 }
374381
375- std::shared_ptr<NetUtil::Asio::SSLSession> session = nullptr ;
376382 std::string target_ip = _active_target;
383+ _active_target.clear ();
384+
385+ BaseKit::UUID sessionId;
386+ bool found = false ;
377387
378- // First try direct session lookup
388+ // Use consistent lock ordering: mapping_lock before session_lock. Prefer
389+ // the newest session when a reconnecting peer has both an old and a new
390+ // one registered.
379391 {
392+ std::shared_lock<std::shared_mutex> mapping_locker (_ipmapping_lock);
380393 std::shared_lock<std::shared_mutex> session_locker (_sessionids_lock);
381- auto session_it = _session_ids.find (target_ip);
382- if (session_it != _session_ids.end ()) {
383- session = FindSession (session_it->second );
384- }
385- }
386394
387- // If not found, try via IP mapping (for NAT/Router scenarios)
388- if (!session) {
389- // Use consistent lock ordering: mapping_lock before session_lock
390- std::shared_lock<std::shared_mutex> mapping_locker (_ipmapping_lock);
391- auto real_to_remote = _real_to_remote_ip.find (target_ip);
392- if (real_to_remote != _real_to_remote_ip.end ()) {
393- std::string remote_ip = real_to_remote->second ;
394- std::shared_lock<std::shared_mutex> session_locker (_sessionids_lock);
395- auto session_it = _session_ids.find (remote_ip);
396- if (session_it != _session_ids.end ()) {
397- session = FindSession (session_it->second );
398- std::cout << " Found session via IP mapping: " << target_ip << " -> " << remote_ip << std::endl;
399- }
395+ auto newestFor = [this ](const std::string &addr, BaseKit::UUID *id) {
396+ auto range = _session_ids.equal_range (addr);
397+ if (range.first == range.second )
398+ return false ;
399+ auto newest = range.second ;
400+ --newest;
401+ *id = newest->second ;
402+ return true ;
403+ };
404+
405+ found = newestFor (target_ip, &sessionId);
406+
407+ // If not found, try via IP mapping (for NAT/Router scenarios)
408+ if (!found) {
409+ auto real_to_remote = _real_to_remote_ip.find (target_ip);
410+ if (real_to_remote != _real_to_remote_ip.end ())
411+ found = newestFor (real_to_remote->second , &sessionId);
400412 }
401413 }
402414
403- if (session) {
404- session->SendAsync (data, size);
405- } else {
415+ if (!found) {
406416 std::cout << " No session found for target: " << target_ip << std::endl;
417+ return 0 ;
418+ }
419+
420+ auto session = FindSession (sessionId);
421+ if (!session || !session->IsHandshaked ()) {
422+ std::cout << " Session not ready for target: " << target_ip << std::endl;
423+ return 0 ;
407424 }
408425
409- _active_target = " " ;
410- return size;
426+ // Report failure instead of pretending the frame was delivered; the FBE
427+ // request then fails its future instead of hanging until timeout.
428+ return session->SendAsync (data, size) ? size : 0 ;
411429}
0 commit comments