| C#/.NET Core Coding Guidelines :👈 | 👉:Synchronous vs Asynchronous methods |
C#/.NET Communication & Protocols — Examples |
walk through a modular, real-world-ready example that touches on serial communication, TCP/IP sockets, and protocol parsing in C#.
🧩 1. Serial Communication in C#
✅ Example: Basic Serial Port Setup
public class SerialDriver
{
private SerialPort _port;
public void Initialize(string portName, int baudRate = 9600)
{
_port = new SerialPort(portName, baudRate)
{
Parity = Parity.None,
DataBits = 8,
StopBits = StopBits.One,
Handshake = Handshake.None,
ReadTimeout = 2000,
WriteTimeout = 2000
};
_port.DataReceived += OnDataReceived;
_port.Open();
}
private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = _port.ReadLine();
ParseProtocol(data);
}
public void Send(string message)
{
if (_port?.IsOpen == true)
_port.WriteLine(message);
}
private void ParseProtocol(string raw)
{
// Example: parse a simple key-value protocol
var parts = raw.Split('=');
if (parts.Length == 2)
Console.WriteLine($"Key: {parts[0]}, Value: {parts[1]}");
}
}
Note: Need to install Nuget Package System.IO.Ports
🌐 2. TCP/IP Communication
✅ Example: TCP Client
using System.Net.Sockets;
using System.Text;
public class TcpClientDriver
{
private TcpClient _client;
private NetworkStream _stream;
public void Connect(string ip, int port)
{
_client = new TcpClient(ip, port);
_stream = _client.GetStream();
}
public void Send(string message)
{
byte[] data = Encoding.UTF8.GetBytes(message);
_stream.Write(data, 0, data.Length);
}
public void Receive()
{
byte[] buffer = new byte[1024];
int bytes = _stream.Read(buffer, 0, buffer.Length);
string response = Encoding.UTF8.GetString(buffer, 0, bytes);
ParseProtocol(response);
}
private void ParseProtocol(string raw)
{
// Reuse same parser logic or extend for TCP framing
Console.WriteLine($"Received: {raw}");
}
}
🧠 3. Protocol Parsing Best Practices
✅ Do This:
- Define a clear protocol contract: Use structured formats like JSON, XML, or custom delimiters.
- Validate input rigorously: Always check for malformed or unexpected data.
- Use state machines for complex protocols: Especially when dealing with multi-frame or handshake-based communication.
- Log raw and parsed data separately: Helps in debugging and auditing.
- Use async I/O: Prevents blocking and improves scalability.
🚫 Avoid This:
- Assuming fixed-length messages without framing or delimiters.
- Ignoring encoding mismatches (UTF-8 vs ASCII).
- Parsing without bounds checking—leads to buffer overflows or crashes.
- Hardcoding protocol logic—use modular parsers or strategy patterns.
- Skipping retries or timeouts—especially in noisy environments like RS-232.
🧱 Modular Architecture Tip
For scalable driver development:
- Use vertical slice architecture: Each protocol or device type gets its own handler.
- Implement
IProtocolParserinterface and inject parsers via DI for testability and extensibility.
Example interface
public interface IProtocolParser
{
bool TryParse(string raw, out ParsedMessage result);
}
🧨 Common Protocol Parsing Errors
-
1. Incorrect Framing or Delimiters
Cause: Assuming fixed-length messages or failing to detect start/end markers.
Impact: Misaligned reads, partial messages, or buffer overflows.
Fix: Use framing strategies like STX/ETX markers, length-prefixed headers, or newline delimiters.
-
2. Encoding Mismatches
Cause: Mixing UTF-8, ASCII, or binary without explicit handling.
Impact: Garbled data, failed deserialization, or silent corruption.
Fix: Normalize encoding at the transport layer and validate before parsing.
-
3. Stateful Protocol Mismanagement
Cause: Ignoring handshake states, session tokens, or expected transitions.
Impact: Invalid command execution, security holes, or dropped connections.
Fix: Implement a finite state machine (FSM) to track protocol phases.
-
4. Malformed or Unexpected Input
Cause: No schema validation or loose parsing logic.
Impact: Crashes, undefined behavior, or security vulnerabilities.
Fix: Use strict parsers with schema enforcement (e.g., JSON schema, XML XSD, or custom validators).
-
5. Buffer Mismanagement
Cause: Reading too much or too little from the stream.
Impact: Data truncation, overflow, or deadlocks.
Fix: Always check buffer bounds and use circular buffers or stream parsers for continuous data.
-
6. Versioning and Compatibility Issues
Cause: Protocol changes without backward compatibility.
Impact: Legacy clients fail, or newer features break older parsers.
Fix: Embed version headers and use strategy patterns for parser dispatch.
-
7. Concurrency and Race Conditions
Cause: Parsing shared buffers across threads without locks.
Impact: Data corruption or inconsistent state.
Fix: Use thread-safe queues or async pipelines with proper synchronization.
-
8. Silent Failures
Cause: Swallowing exceptions or ignoring parse errors.
Impact: Undetected bugs, data loss, or security risks.
Fix: Log all parse failures with context and metrics. Use structured logging.
🧠 Strategic Tips for Resilient Parsing
- Modular Parsers: Break down parsing into tokenization, validation, and transformation stages.
- Test with Fuzzing: Use fuzzing tools to simulate malformed or edge-case inputs.
- Telemetry Hooks: Track parse success/failure rates, latency, and payload sizes.
- Replay Buffers: Store raw input for replay during debugging or test automation.
| C#/.NET Core Coding Guidelines :👈 | 👉:Synchronous vs Asynchronous methods |