Skip to content

Commit 060263f

Browse files
authored
Drop the socket when a send fails in sync tcp client (#3000)
1 parent 5617e05 commit 060263f

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

pymodbus/client/tcp.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,13 @@ def send(self, request, addr: tuple | None = None):
219219
if not self.socket:
220220
raise ConnectionException(str(self))
221221
if request:
222-
return self.socket.send(request)
222+
try:
223+
return self.socket.send(request)
224+
except (BlockingIOError, InterruptedError):
225+
raise
226+
except OSError:
227+
self.close()
228+
raise ConnectionException(str(self)) from None
223229
return 0
224230

225231
def recv(self, size: int | None) -> bytes:

test/client/test_client_sync.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,30 @@ def test_tcp_client_send(self):
150150
assert not client.send(b"")
151151
assert client.send(b"1234") == 4
152152

153+
def test_tcp_client_send_drops_socket_on_os_error(self):
154+
"""Test that a socket the OS tore down is not left in place as connected."""
155+
client = ModbusTcpClient("127.0.0.1")
156+
mock_socket = mock.MagicMock()
157+
mock_socket.send.side_effect = BrokenPipeError(32, "Broken pipe")
158+
client.socket = mock_socket
159+
with pytest.raises(ConnectionException):
160+
client.send(b"1234")
161+
assert not client.connected
162+
assert client.socket is None
163+
164+
def test_tcp_client_send_keeps_socket_on_transient_error(self):
165+
"""Test that a transient write error leaves a healthy socket in place."""
166+
client = ModbusTcpClient("127.0.0.1")
167+
mock_socket = mock.MagicMock()
168+
mock_socket.send.side_effect = BlockingIOError(
169+
11, "Resource temporarily unavailable"
170+
)
171+
client.socket = mock_socket
172+
with pytest.raises(BlockingIOError):
173+
client.send(b"1234")
174+
assert client.connected
175+
assert client.socket is mock_socket
176+
153177
@mock.patch("pymodbus.client.tcp.time")
154178
@mock.patch("pymodbus.client.tcp.select")
155179
def test_tcp_client_recv(self, mock_select, mock_time):

0 commit comments

Comments
 (0)