Need help accessing a serial port over Telnet using RFC 2217

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:

  1. 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.
  2. 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.
  3. Client side on Linux
    You have a couple of good options:

    • socat
      socat -d -d pty,link=/dev/ttyRFC2217,raw,echo=0 \
        tcp:TERMINAL_SERVER_IP:PORT
      
      That gives you a pseudo TTY like /dev/ttyRFC2217. Then use minicom, 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
      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()
      
      This one does RFC 2217 properly, including changing baud and flow control at runtime.
  4. 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.
  5. 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:
      yes '1234567890' | head -c 1000000 > /tmp/test.bin
      
      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.
  6. 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:port using 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.

2 Likes

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:

  1. 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.”
  2. 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.

  3. 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 targets server_ip:port using 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:

  1. 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/X that 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.

  2. 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:

  1. 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.
  2. 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.
  3. 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.

  4. 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:

  1. Lock server at: 115200, 8N1, no flow control.
  2. Connect a known simple device or loopback plug.
  3. On Linux: use pySerial rfc2217:// directly, fixed settings, no changes at runtime.
  4. Blast a binary test pattern across and compare checksums.
  5. If that is clean, introduce flow control.
  6. 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.