#named-pipe #pipe #winapi

winpipe

Blocking rust wrapper for Windows named pipes with very similar api to UnixStream/UnixListen

2 releases

0.1.1 Oct 16, 2024
0.1.0 Oct 6, 2024

#3 in #named-pipe

Download history 138/week @ 2024-10-03 270/week @ 2024-10-10 320/week @ 2024-10-17

728 downloads per month

MIT/Apache

83KB
2K SLoC

winpipe

Blocking rust wrapper for Windows named pipes with very similar api to UnixStream/UnixListen.

Listening for a pipe (server)

use winpipe::WinListener;
use std::io;

use std::io::{Read, Write};

use std::time::Duration;

fn listen_for_pipes() -> io::Result<()>{
    let listener = WinListener::bind("\\\\.\\pipe\\my_pipe_is_cool")?;

    loop {
        let (mut stream, _addr) = listener.accept()?;
        stream.set_write_timeout(Some(Duration::from_millis(5000)))?;
        stream.set_read_timeout(Some(Duration::from_millis(5000)))?;

        stream.write("Hello World".as_bytes())?;
        let mut buffer = vec![0u8; 64];
        let received = stream.read(buffer.as_mut_slice())?;
        println!("Received {:?}", &buffer[..received])
        //DROPPING Closes the pipe
    }
}

Connecting a pipe

use winpipe::WinStream;
use std::io;

use std::io::{Read, Write};

use std::time::Duration;

fn connect_pipe() -> io::Result<()>{
    let mut stream = WinStream::connect("\\\\.\\pipe\\my_pipe_is_cool")?;

    stream.set_write_timeout(Some(Duration::from_millis(5000)))?;
    stream.set_read_timeout(Some(Duration::from_millis(5000)))?;

    stream.write("Hello World".as_bytes())?;
    let mut buffer = vec![0u8; 64];
    let received = stream.read(buffer.as_mut_slice())?;
    println!("Received {:?}", &buffer[..received]);
    //DROPPING Closes the pipe
    Ok(())
}

Tip for writing the same IPC code for Windows and Unix

Create type aliases for UnixListen, UnixStream and unix::SocketAddress. On Unix targets let the alias point to the implementations of the stdlib. On Windows targets let the alias point to WinListen, WinStream and WinPipeSocketAddress.

You should only require very few conditional compilation blocks as long as you do not use the advanced features of Unix Sockets (such as for example sharing file descriptors).

Implementation Details

  • All pipes are created/opened by this crate are PIPE_TYPE_STREAM. PIPE_TYPE_PACKET is not implemented.
  • The default timeout for both read and write is infinite.
  • Setting a timeout or calling WinStream::set_nonblocking(true) concurrently will interrupt ongoing read/write calls.
  • Dropping WinStream may block up to 5s in a call to FlushFileBuffers.
  • The io buffer sizes supplied to the Windows API are 0x1_00_00 (64kb)
  • There is no connection backlog
    • It's not possible to implement one given Windows API limitations.
    • As long as you do not actively call WinListener::accept() or WinListener::incoming()::next() no client will be able to connect.
  • Connecting client pipes is implemented using CreateFileA and 200ms polling. It will try to connect for 1s (~4-5 times) before returning ErrorKind::ConnectionRefused
    • a custom timeout can be passed with WinStream::connect_with_timeout. At least 1 attempt to connect will always be made!
  • This library uses the log crate to log all system calls using the trace! macro.
    • If you are troubleshooting problems then I recommend using a log format which includes the thread id!
    • To disable usage of the log crate use default-features=false in your Cargo.toml when including winpipe.

Dependencies

~0–35MB
~527K SLoC