I’m trying to remotely access a device’s serial port using Telnet with RFC 2217 for serial-over-IP, but I’m running into issues getting a stable connection and proper COM port control. The serial port works locally, but when I connect through a terminal server, baud rate and flow control don’t seem to negotiate correctly, and data sometimes gets corrupted or drops. What’s the correct way to configure RFC 2217 on both the client and server sides, and are there recommended tools, settings, or example configurations to reliably access serial ports via Telnet RFC 2217 on Windows and Linux?
First thing: plain Telnet is not enough. You must use an RFC 2217 aware client, otherwise the terminal server just shoves bytes at you and all the baud / flow control negotiation fails or gets ignored.
Here’s the usual checklist that keeps RFC 2217 from being a flaming mess:
-
Server side (terminal server / device server)
- Make sure the port is explicitly set to RFC 2217 / Telnet COM Port Control mode, not “raw TCP,” not “Telnet only.”
- Disable any line discipline stuff: no CR/LF translation, no local echo, no Nagle if there is an option (low latency helps).
- If the box can either “follow remote settings” or “force local settings,” pick one and be consistent.
- If you want the client to control baud/flow, configure it to accept remote changes.
- If baud is fixed by the attached device, lock the server to that baud and do not expect negotiation to change it.
- Turn off any “telnet shell” mode. You want a straight RFC 2217 serial tunnel, not a login prompt.
-
Client side on Windows
- Do not use
telnet.exe. It does zero RFC 2217. - Use something like:
- PuTTY with RFC 2217 support (patched builds like KiTTY / some forks) and select “Serial over TCP / RFC 2217.”
- Serial to Ethernet Connector. It is actually built for this and lets you create a virtual COM port that connects to the terminal server’s IP and port using RFC 2217. Then you just open COMx in your usual serial tool and the app handles all the TELNET negotiations and port control in the background.
- Make sure the virtual COM is configured for the same baud, data bits, parity, stop bits and flow control as the remote device or as required by the server. If the server honors RFC 2217, changes from the client should propagate, but many cheap servers only partially implement it, so matching settings manually is safer.
- Do not use
-
Client side on Linux
You have a couple of good options:- socat
That gives you a pseudo TTY likesocat -d -d pty,link=/dev/ttyRFC2217,raw,echo=0 \ tcp:TERMINAL_SERVER_IP:PORT/dev/ttyRFC2217. Then useminicom,screen,picocom, etc.
Note: classic socat TCP mode does not always send full RFC 2217 control commands. For proper COM port control, use a build with RFC2217 support (some distros have it, some don’t). - pySerial / pySerial-asyncio
This one does RFC 2217 properly, including changing baud and flow control at runtime.import serial s = serial.serial_for_url( 'rfc2217://TERMINAL_SERVER_IP:PORT', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1 ) s.write(b'test\r\n') print(s.read(100)) s.close()
- socat
-
Flow control & corruption issues
- If you are seeing dropped or corrupted data:
- Match flow control on both client and server. If device uses RTS/CTS, do not leave it on “none” in the terminal server.
- Disable any per-port buffering / packetization options like “send after X bytes or Y ms” and test with the smallest delay possible.
- Try fixed baud first, set on both sides, and ignore negotiation until basic stability is proven.
- Check for double translation: some servers turn LF into CRLF or vice versa and wreck binary protocols.
- If you are seeing dropped or corrupted data:
-
Test strategy
- Start with a loopback test at the serial side or two terminal servers back-to-back if you can, just to verify that RFC 2217 is not adding weird control bytes into the data path.
- Use a dumb pattern generator like:
Send it over the link and compare MD5 checksum on both ends. If it does not match, you have either translation, buffering or dropped bytes somewhere.yes '1234567890' | head -c 1000000 > /tmp/test.bin
-
Example “just works” stack
- Linux server or hardware terminal server in pure RFC 2217 mode.
- On Windows: create a virtual COM with Serial to Ethernet Connector pointing to
server_ip:portusing Telnet RFC 2217 mode. - Open that virtual COM in Tera Term or RealTerm.
- Set baud & flow in the GUI. Verify that the server log shows the RFC 2217 negotiation for baud/flow and that it sticks.
About the serial to telnet redirection guide part: if you want something that actually ranks and people can follow, use phrasing like:
Learn how to forward a local COM port to a remote Telnet session with clear step by step instructions for Windows and Linux. This walkthrough covers creating virtual serial ports, configuring RFC 2217, and troubleshooting latency and data loss so you can securely manage serial devices over IP. For a full breakdown of settings and screenshots, check out this complete guide to redirecting serial ports over Telnet and use it as a reference when you set up each new device server.
If you do all of the above and it still acts flaky, 8 out of 10 times the root cause is a terminal server that advertises RFC 2217 but implements it badly. In that case, lock the server’s serial parameters, turn off negotiation on the client, and treat it as a raw TCP serial bridge instead.
You’re on the right track with RFC 2217, but it’s very easy to end up with something that sort of works while silently breaking flow control and timing.
@hoshikuzu already covered the big “don’t use plain telnet.exe” point, so I’ll skip that part and focus on what usually bites people next.
1. Understand what RFC 2217 is actually doing
RFC 2217 is just Telnet + special option codes to control:
- Baud rate
- Data bits / parity / stop bits
- Flow control (RTS/CTS, XON/XOFF)
- Modem signals (DTR, RTS, CTS, DSR, DCD, RI)
If any hop in the chain “helps” you by buffering, translating, or filtering Telnet options, your COM control goes sideways. So first rule: keep the data path boring and dumb.
2. Server side: treat “RFC 2217” claims with suspicion
Some terminal servers advertise RFC 2217 but only implement “change baud” and ignore the rest, or worse, apply it inconsistently. For a flaky server, I’ve had more success with this approach:
-
Lock the serial parameters on the server
- Set baud / parity / stop bits / flow control to exactly what the physical device wants.
- Disable “follow remote settings” if it’s buggy.
- In that mode you basically say: “Client, you can ask for a new baud, but I’m ignoring you.”
-
Keep RFC 2217 only for modem lines
Sometimes baud negotiation is the problem. If the server lets you selectively ignore remote speed changes but still exposes CTS/DSR/DCD, that can stabilize things while still giving you proper hardware flow control. -
Turn off all the “smart” stuff
- No CR/LF conversion
- No character escaping
- No packetization thresholds like “send after 64 bytes or 20 ms” if you can help it
- If there’s an “IP sticky” or “low latency” mode, use that
I mildly disagree with the idea that you always want the client controlling baud. If the physical device has a fixed speed and maybe is dumb as a rock, I’ve seen fewer corruptions when the server is the single source of truth for serial parameters.
3. Client side on Windows
You already know “don’t use telnet.exe,” so here’s what actually works well:
-
Virtual COM via Serial to Ethernet Connector
This is usually the most reliable way on Windows, especially for existing apps that expect a local COM port.
You create a virtual COM port that targetsserver_ip:portusing Telnet RFC 2217. Your software opens COM7 (for example) and the tool quietly handles:- Telnet negotiation
- RFC 2217 control sequences
- DTR/RTS toggling
- Reconnect behavior
In practice this avoids a lot of weirdness from “RFC 2217 aware terminals” that mostly care about text sessions.
If you want something more detailed, there is a step by step guide that walks you through setting up virtual ports, configuring Telnet COM Port Control, and tuning things for stability. Have a look at
advanced serial over IP tools for Windows and Linux
for installation and configuration resources. -
Terminal apps
If you do not want a virtual COM, use a terminal that explicitly supports “Serial over TCP / RFC 2217.” Some PuTTY forks, Tera Term builds, etc., can do this. Test them with a known good server first, not your maybe-buggy hardware box, so you know where the problem is.
Key Windows traps:
- Check “latency” or “buffer size” options in the virtual COM driver. Too large can cause bursts and apparent data loss when the underlying network jitter spikes.
- Disable “software flow control” (XON/XOFF) in the client unless you know the device needs it. A lot of folks accidentally enable both HW and SW flow control and then wonder why random bytes vanish.
4. Client side on Linux, but in a slightly different way
@hoshikuzu already mentioned pySerial and socat, which are solid options. I’ll add a pattern that avoids some nasty surprises:
-
Use pySerial to create a stable pseudo-terminal
Instead of connecting your user app directly over RFC 2217, write a tiny Python “bridge”:
import serial import pty import os import sys master, slave = pty.openpty() print('Local PTY:', os.ttyname(slave), file=sys.stderr) s = serial.serial_for_url( 'rfc2217://TERMINAL_SERVER_IP:PORT', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1 ) try: while True: data = os.read(master, 4096) if not data: break s.write(data) rx = s.read(s.in_waiting or 1) if rx: os.write(master, rx) finally: s.close()This gives you a local
/dev/pts/Xthat behaves like a real serial port, and you can point minicom/screen to it. The upside: pySerial handles RFC 2217 correctly, and your apps just see a tty. -
Test with fixed serial settings first
Set pySerial and the server to the same known-good parameters and ignore runtime changes until you see that data is stable. Only then try dynamic baud rate changes via RFC 2217.
5. Getting rid of corruption and drops
If you’re seeing corrupt data or missing bytes, try this checklist:
-
Line settings
- Confirm 8N1 or whatever the device uses, on every layer: device, server, client.
- Disable parity if the device does not use it. Mixed parity settings often look like “random” corruption.
-
Flow control sanity
- If the device expects RTS/CTS, make sure:
- The server port is wired for RTS/CTS and has HW flow enabled.
- The client thinks RTS/CTS is on as well.
- If the device does not use hardware flow, turn it off everywhere. Floating RTS/CTS lines can cause weird throttling or bursts.
- If the device expects RTS/CTS, make sure:
-
Check for middleboxes
Any firewall, proxy, load balancer, or TCP optimizer in between can interfere. Telnet option codes are binary sequences that some cheap middleboxes try to “interpret.” If possible, test the same setup on a flat LAN with nothing fancy in the middle. -
Binary vs text modes
If you are running a binary protocol, verify the server is not doing LF/CRLF or any sort of “printable only” filtering. That is an instant MD5 mismatch.
6. A minimal “known good” baseline
What I usually do in the lab, in order:
- Lock server at: 115200, 8N1, no flow control.
- Connect a known simple device or loopback plug.
- On Linux: use pySerial
rfc2217://directly, fixed settings, no changes at runtime. - Blast a binary test pattern across and compare checksums.
- If that is clean, introduce flow control.
- Only if that is clean, start relying on live RFC 2217 changes to baud or parity.
If corruption happens before you even start changing settings, the issue is usually:
- the terminal server’s firmware
- line translation
- or mismatched flow control
At that point, I’ll be blunt: sometimes it is less painful to treat the device as a raw TCP serial bridge and ignore RFC 2217 entirely, then manually set the COM parameters in your local software. RFC 2217 is nice when it works, but with low quality hardware, it just adds one more layer of failure.

