# SerialPortStream replaces the serial port with a buffered Stream

> SerialPortStream is an independent C# implementation of System.IO.Ports.SerialPort built as a ground-up Stream, so writes are fully buffered and the driver is abstracted. The documentation is unusually honest about the cost of that choice, since internal buffering adds a context switch and can slow your software, and it enumerates the specific defects in the Microsoft implementation it is working around.

**jcurl/RJCP.DLL.SerialPortStream** — SerialPortStream is an independent implementation of System.IO.Ports.SerialPort and SerialStream for better reliability and maintainability. Support for Linux with help of a C library.

- Repository: https://github.com/jcurl/RJCP.DLL.SerialPortStream
- Stars: 745 · Forks: 203
- Language: C#
- License: MS-PL
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/jcurl-rjcp-dll-serialportstream

## The goal is a Stream, not a drop-in replacement

SerialPortStream is an independent implementation of System.IO.Ports.SerialPort and SerialStream, for better reliability and maintainability, with support for Linux with the help of a C library.

The implementation is a ground up implementation of a Stream that buffers data to and from a serial port. On Windows it uses low level Win32API for managing events and asynchronous I/O, using a programming model as in the MSDN PipeServer example. On Linux it uses a support library to interface with Posix OS calls for an event loop.

That last distinction explains a lot about the design. The Windows side is the primary one, and it is written against the same overlapped-I/O model that the .NET named pipes use, which is where the reliable asynchronous behaviour comes from. The Linux side is a compatibility layer built on top of a native library rather than a first-class implementation, and the documentation is honest about the difference.

The framing of the goal matters. The goal is to provide a Stream, not an API compatible replacement to the SerialPort. It is not meant to be 100% compatible, but instead to provide similar functionality. So you are not going to drop this in and have every call site work, but you are going to get a class that behaves like a Stream, which is what most code actually wants.

The repository is C#, licensed under MS-PL, with the last push on 2026-10-07 and releases 3.0.5 from 2026-03-27 and 3.0.4 and 3.0.3 from September 2025. The tree holds the solution file, a code/ directory, a dll/ directory, a test/ directory, BUILD.md and CHANGES.md.

## Five specific defects in the Microsoft implementation

The section on issues with the MS Serial Port is the most useful part of the documentation, because it is a list of concrete failures with concrete reasons.

The first concerns the behaviour of the fAbortError flag in the serial DCB, an issue described by Zach Saw. The library defines this flag itself.

The second is the reopen problem. Closing a serial port, then reopening it generally causes problems, and the SerialPortStream should not have this issue, though there are some cases observed where the Operating System hangs and this cannot be avoided.

The third is the ReadTo() implementation, which can subtly change the byte stream buffer when one switches from characters to bytes. The reason given is that the MS implementation converts the characters back to bytes into its buffer, so with UTF-8, if you decode some invalid characters and then have a timeout, those invalid characters get converted back to bytes and you end up with lost data. The author takes some care when decoding bytes to characters to ensure an accurate transition.

The fourth is Write() semantics, and it is the one that most changes how you write application code. With the MS implementation, an asynchronous write call back gives you the number of bytes that were actually transferred to the driver, and you have to check yourself whether that is valid. In the synchronous case the data is simply thrown away. The SerialPortStream method copies data to a local buffer and uses asynchronous writes in a different thread, working in the background to send out the data you provided. If the data cannot be sent you get a TimeoutException without any data being buffered at all.

The fifth is disposal during a blocking write. Disposing or closing the serial port during a blocking write will not abort the write with the MS implementation, while this one aborts with a System.IO.IOException.

That fourth item is the reason to consider the library at all. A write that either succeeds completely or throws, with no partial transfer to reason about, is what lets you implement a reliable protocol without a retry loop around every call.

## Buffering costs a context switch, and the documentation says so

The section on differences to the MS Serial Port starts with the cost rather than the benefit, which is unusual and welcome.

All data is buffered internally in memory, captured using an I/O thread. The extra buffering adds delays by reading the bytes, then performing a context switch for the user code to read the buffer. This can slow down your software.

Then the benefit. Buffering solves the problem, however, that data is read from the serial port in an arbitrary sized memory buffer, and not dependent on the driver, so the likelihood of driver underruns and overruns is reduced. And the documentation says this was an important aspect when writing the library.

So the trade is explicit. You give up some latency for transport that does not depend on the driver's own buffering behaviour. For a protocol where a driver underrun means a lost byte and a resync, that is the right direction. For a high-rate continuous data stream where you are already reading in large blocks, the added context switch is real and you may notice it.

The other stated goal is to abstract the driver implementation and provide for a more reliable transport, by making writing serial data completely buffered. The primary goal of the project today is stated as providing a buffered solution that can be used on various operating systems, with the ability to also abstract hardware.

The second stated goal is reliable and consistent behaviour, which is what the defects list above is the evidence for.

## Testing is Windows-heavy, and Linux is acknowledged as untested

The system requirements section is where the platform reality shows.

Software has been tested and developed using .NET 6.0 and 8.0 on Windows 11 Pro x64 with .NET SDK 10.x, and .NET 4.8.1 on Windows 11 Pro x64. Software has not been recently tested on Linux, but should work.

That sentence is worth weighing. The Linux support is real, with a support library interfacing with Posix OS calls for an event loop, and the library is described as providing portability to Mono on Linux systems. But the development and testing have been on Windows, and the documentation says the known issues section for Linux exists.

Those Linux known issues are specific rather than general. There is a section on Mono on non-Windows platforms. There is a section on driver specific issues on Linux, with subsections on parity errors, garbage data on open, monitoring pins and timing resolution, close times with flow control, and opening ports, where the parenthesised note says some unit test case failures are involved.

The Windows side has its own driver specific list, covering flow control, and BytesToWrite with entries for the CP2101 driver and the PL2303 RA.

So the practical picture is that a CP2101 or PL2303 adapter, which are extremely common USB serial chips, have documented quirks on both platforms, and the sections exist so you can find out which one you have. There is also a section on guidelines on serial protocols, which is the closest thing to advice in the document.

The documentation notes that these notes are for version 3.x, which is a refactoring of v2.x for better maintainability, with the differences to v2.x covered at the end of the notes.

## The developer notes are where the extensibility is

The developer notes section is short in its list but dense in content, and it is the part that tells you the library is designed to be adapted rather than just used.

There is a section on logging, and a separate section on the LogSource abstraction. That abstraction is the hook for the rest of the design, and it is why the .NET Core section has a subsection on dependency injection and another on a singleton via LogSource. The pattern is that a logging source can be injected, or registered as a singleton, which lets the library report what it is doing without the host application having to wire anything up by hand.

The .NET Framework section covers .NET 4.0 to .NET 4.8, and the .NET Core section covers the modern runtime, with the two subsections above. So the library spans a very wide framework range, from the old .NET Framework on Windows through to current .NET on both platforms, which is a lot of surface for one implementation to cover and explains why there is a native support library on the Linux side.

The version 3.x refactoring was for better maintainability, and the LogSource arrangement is a good example of what that buys. Rather than a static logger or a hard dependency, there is an abstraction point that both the classic and the modern target can share, and a host can override.

For anyone extending this, the practical reading is that the logging abstraction is where you would start, since it is the one place the library has deliberately made itself reconfigurable. The other developer notes, on buffering and on the ReadTo behaviour, are more useful as warnings than as extension points.

## Conclusion

Adopt SerialPortStream if you are writing a serial protocol on .NET and have been bitten by the Microsoft implementation's write semantics, buffer handling or the difficulty of reopening a closed port, because those are the specific failures the library is built to remove. Skip it if you only open a port and read a few lines, since the standard class is enough there, and be careful on Linux, since the documentation says the software has not been recently tested there and should work. Verify first which .NET version you are on, whether you need flow control, and the known issues list for your specific USB serial chip, because the CP2101 and PL2303 sections document driver-specific behaviour that differs from the general case.

## FAQ

### What is SerialPortStream and how is it different from System.IO.Ports.SerialPort?

It is an independent implementation of the serial port as a ground up Stream, with all data buffered internally and captured on an I/O thread. The goal is similar functionality rather than 100% API compatibility, and the main practical difference is that writes are fully buffered and either complete or throw a TimeoutException, rather than reporting a partial byte count.

### Does SerialPortStream work on Linux?

It is supported, using a support library that interfaces with Posix OS calls for an event loop, and the library is described as providing portability to Mono on Linux systems. The documentation is candid that the software has not been recently tested on Linux but should work, and it has a dedicated known issues section for Linux.

### What are the known issues with the Windows serial port that SerialPortStream fixes?

The behaviour of the fAbortError flag in the serial DCB, problems closing and then reopening a port, the ReadTo implementation subtly changing the byte stream buffer when switching between characters and bytes, asynchronous writes reporting only the bytes actually transferred, and disposing or closing the port during a blocking write not aborting the write.

## Sources

- [Issues](https://github.com/jcurl/RJCP.DLL.SerialPortStream/issues)
- [jcurl/RJCP.DLL.SerialPortStream on GitHub](https://github.com/jcurl/RJCP.DLL.SerialPortStream)
- [License: MS-PL](https://github.com/jcurl/RJCP.DLL.SerialPortStream/blob/master/LICENSE)
- [README](https://github.com/jcurl/RJCP.DLL.SerialPortStream/blob/master/README.md)
- [Releases](https://github.com/jcurl/RJCP.DLL.SerialPortStream/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jcurl-rjcp-dll-serialportstream
