Fix frozen client on ctrl+q: send bye instead of half-closing
Dropping the client's channel sender never closed the socket (the server's reader thread keeps the connection open), so the client froze waiting for frames. The server now sends a bye message; the client exits cleanly on it and its socket close lets the server clean up. Messages from already-disconnected clients are ignored. Verified end-to-end with the real client in a PTY (exits 0, daemon survives). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,8 @@ fn client_loop(
|
||||
}
|
||||
stdout.flush()?;
|
||||
}
|
||||
// Servern har kopplat ner oss (t.ex. ctrl+q) — avsluta snyggt.
|
||||
Ok(ServerMessage::Bye) => return Ok(()),
|
||||
Ok(_) => {}
|
||||
Err(mpsc::TryRecvError::Empty) => break,
|
||||
Err(mpsc::TryRecvError::Disconnected) => return Ok(()),
|
||||
|
||||
@@ -111,6 +111,10 @@ pub enum ClientRole {
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ServerMessage {
|
||||
HelloOk { version: String, socket_path: String },
|
||||
/// Servern kopplar ner klienten kontrollerat (t.ex. efter ctrl+q
|
||||
/// från klienten). Klienten ska återställa terminalen och avsluta —
|
||||
/// det är klientens socket-stängning som låter servern städa upp.
|
||||
Bye,
|
||||
Frame {
|
||||
width: u16,
|
||||
height: u16,
|
||||
|
||||
12
src/main.rs
12
src/main.rs
@@ -438,6 +438,11 @@ fn handle_ipc_event(
|
||||
all_clients.remove(&client_id);
|
||||
}
|
||||
IpcEvent::Message { client_id, msg } => {
|
||||
// Ignorera meddelanden från klienter som redan kopplats ner
|
||||
// (t.ex. efter bye) men vars socket ännu inte hunnit stängas.
|
||||
if !all_clients.contains_key(&client_id) {
|
||||
return;
|
||||
}
|
||||
match msg {
|
||||
ClientMessage::Input { data } => {
|
||||
let bytes = ipc::from_hex(&data);
|
||||
@@ -540,7 +545,12 @@ fn disconnect_instead_of_quit(
|
||||
) {
|
||||
if app.should_quit {
|
||||
app.should_quit = false;
|
||||
// Sändaren droppas → klientens ström stängs → klienten avslutar.
|
||||
// Skicka bye så klienten avslutar och stänger socketen — att bara
|
||||
// droppa sändaren räcker inte, eftersom serverns läsartråd håller
|
||||
// anslutningen öppen och klienten annars fryser i väntan på frames.
|
||||
if let Some((_, _, _, tx)) = all_clients.get(&client_id) {
|
||||
let _ = tx.try_send(ServerMessage::Bye);
|
||||
}
|
||||
all_clients.remove(&client_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,13 +148,15 @@ def main():
|
||||
disp2.recv_until("frame")
|
||||
disp2.send(key_event(ch="q", modifiers="CONTROL"))
|
||||
try:
|
||||
got_bye = False
|
||||
deadline = time.time() + 3
|
||||
while time.time() < deadline:
|
||||
disp2.recv()
|
||||
disconnected = False
|
||||
if disp2.recv().get("type") == "bye":
|
||||
got_bye = True
|
||||
break
|
||||
except (EOFError, TimeoutError, OSError):
|
||||
disconnected = True
|
||||
check("ctrl+q från klient kopplar ner klienten", disconnected)
|
||||
pass # EOF duger också som nedkoppling
|
||||
check("ctrl+q från klient ger bye till klienten", got_bye)
|
||||
frame = disp.recv_until("frame")
|
||||
check("daemonen överlever klientens exit", frame.get("width", 0) > 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user