Skip to content
This repository was archived by the owner on Jul 24, 2022. It is now read-only.
This repository was archived by the owner on Jul 24, 2022. It is now read-only.

Split packages in ModbusMasterTcpConnection #174

Description

@nicolasr75

I am experiencing problems with split packages received by ModbusMasterTcpConnection.

Some background: I have programmed a virtual Modbus device that wraps a real device with a proprietary communication protocol by using the ModbusTcpSlave.

I have a client that requests values from this virtual device once a second. At first it worked without any issues but after a while (several minutes) the communication breaks down with strange errors.

Closer examination with Wireshark and debugging the NModbus code shows that it receives an incomplete header package now and then because the TCP driver for whatever reason split the header package. This leads to all sorts of strange errors because the code does not wait for the rest of the header. To handle this I changed the NModbus code to use ReadAsync in a loop until all bytes have been read. This is my extension to the code in ModbusMasterTcpConnection:

private async Task HandleRequestAsync()
{
	while (true)
	{
		Debug.WriteLine($"Begin reading header from Master at IP: {EndPoint}");
		
		int readBytesTotal = 0;

		while (readBytesTotal < 6)
		{
			int readBytes = await Stream.ReadAsync(_mbapHeader, readBytesTotal, 6 - readBytesTotal).ConfigureAwait(false);
			
			if (readBytes == 0)
			{
				Debug.WriteLine($"0 bytes read, Master at {EndPoint} has closed Socket connection.");
				ModbusMasterTcpConnectionClosed?.Invoke(this, new TcpConnectionEventArgs(EndPoint));
				return;
			}

			readBytesTotal += readBytes;
		}

		ushort frameLength = (ushort)IPAddress.HostToNetworkOrder(BitConverter.ToInt16(_mbapHeader, 4));
		Debug.WriteLine($"Master at {EndPoint} sent header: \"{string.Join(", ", _mbapHeader)}\" with {frameLength} bytes in PDU");
		
		_messageFrame = new byte[frameLength];

		readBytesTotal = 0;

		while (readBytesTotal < frameLength)
		{
			int readBytes = await Stream.ReadAsync(_messageFrame, readBytesTotal, frameLength - readBytesTotal).ConfigureAwait(false);
			
			if (readBytes == 0)
			{
				Debug.WriteLine($"0 bytes read, Master at {EndPoint} has closed Socket connection.");
				ModbusMasterTcpConnectionClosed?.Invoke(this, new TcpConnectionEventArgs(EndPoint));
				return;
			}

			readBytesTotal += readBytes;
		}

		Debug.WriteLine($"Read frame from Master at {EndPoint} completed {readBytesTotal} bytes");
		byte[] frame = _mbapHeader.Concat(_messageFrame).ToArray();
		Debug.WriteLine($"RX from Master at {EndPoint}: {string.Join(", ", frame)}");
		
		var request = ModbusMessageFactory.CreateModbusRequest(_messageFrame);
		request.TransactionId = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 0));

		// perform action and build response
		IModbusMessage response = _slave.ApplyRequest(request);
		response.TransactionId = request.TransactionId;

		// write response
		byte[] responseFrame = Transport.BuildMessageFrame(response);
		Debug.WriteLine($"TX to Master at {EndPoint}: {string.Join(", ", responseFrame)}");
		await Stream.WriteAsync(responseFrame, 0, responseFrame.Length).ConfigureAwait(false);
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions