1 | use std::io; |
2 | |
3 | #[cfg (windows)] |
4 | async fn windows_main() -> io::Result<()> { |
5 | use tokio::io::Interest; |
6 | use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions}; |
7 | |
8 | const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client" ; |
9 | |
10 | let server = ServerOptions::new().create(PIPE_NAME)?; |
11 | |
12 | let server = tokio::spawn(async move { |
13 | // Note: we wait for a client to connect. |
14 | server.connect().await?; |
15 | |
16 | let buf = { |
17 | let mut read_buf = [0u8; 5]; |
18 | let mut read_buf_cursor = 0; |
19 | |
20 | loop { |
21 | server.readable().await?; |
22 | |
23 | let buf = &mut read_buf[read_buf_cursor..]; |
24 | |
25 | match server.try_read(buf) { |
26 | Ok(n) => { |
27 | read_buf_cursor += n; |
28 | |
29 | if read_buf_cursor == read_buf.len() { |
30 | break; |
31 | } |
32 | } |
33 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { |
34 | continue; |
35 | } |
36 | Err(e) => { |
37 | return Err(e); |
38 | } |
39 | } |
40 | } |
41 | |
42 | read_buf |
43 | }; |
44 | |
45 | { |
46 | let write_buf = b"pong \n" ; |
47 | let mut write_buf_cursor = 0; |
48 | |
49 | loop { |
50 | let buf = &write_buf[write_buf_cursor..]; |
51 | |
52 | if buf.is_empty() { |
53 | break; |
54 | } |
55 | |
56 | server.writable().await?; |
57 | |
58 | match server.try_write(buf) { |
59 | Ok(n) => { |
60 | write_buf_cursor += n; |
61 | } |
62 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { |
63 | continue; |
64 | } |
65 | Err(e) => { |
66 | return Err(e); |
67 | } |
68 | } |
69 | } |
70 | } |
71 | |
72 | Ok::<_, io::Error>(buf) |
73 | }); |
74 | |
75 | let client = tokio::spawn(async move { |
76 | // There's no need to use a connect loop here, since we know that the |
77 | // server is already up - `open` was called before spawning any of the |
78 | // tasks. |
79 | let client = ClientOptions::new().open(PIPE_NAME)?; |
80 | |
81 | let mut read_buf = [0u8; 5]; |
82 | let mut read_buf_cursor = 0; |
83 | let write_buf = b"ping \n" ; |
84 | let mut write_buf_cursor = 0; |
85 | |
86 | loop { |
87 | let mut interest = Interest::READABLE; |
88 | if write_buf_cursor < write_buf.len() { |
89 | interest |= Interest::WRITABLE; |
90 | } |
91 | |
92 | let ready = client.ready(interest).await?; |
93 | |
94 | if ready.is_readable() { |
95 | let buf = &mut read_buf[read_buf_cursor..]; |
96 | |
97 | match client.try_read(buf) { |
98 | Ok(n) => { |
99 | read_buf_cursor += n; |
100 | |
101 | if read_buf_cursor == read_buf.len() { |
102 | break; |
103 | } |
104 | } |
105 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { |
106 | continue; |
107 | } |
108 | Err(e) => { |
109 | return Err(e); |
110 | } |
111 | } |
112 | } |
113 | |
114 | if ready.is_writable() { |
115 | let buf = &write_buf[write_buf_cursor..]; |
116 | |
117 | if buf.is_empty() { |
118 | continue; |
119 | } |
120 | |
121 | match client.try_write(buf) { |
122 | Ok(n) => { |
123 | write_buf_cursor += n; |
124 | } |
125 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { |
126 | continue; |
127 | } |
128 | Err(e) => { |
129 | return Err(e); |
130 | } |
131 | } |
132 | } |
133 | } |
134 | |
135 | let buf = String::from_utf8_lossy(&read_buf).into_owned(); |
136 | |
137 | Ok::<_, io::Error>(buf) |
138 | }); |
139 | |
140 | let (server, client) = tokio::try_join!(server, client)?; |
141 | |
142 | assert_eq!(server?, *b"ping \n" ); |
143 | assert_eq!(client?, "pong \n" ); |
144 | |
145 | Ok(()) |
146 | } |
147 | |
148 | #[tokio::main] |
149 | async fn main() -> io::Result<()> { |
150 | #[cfg (windows)] |
151 | { |
152 | windows_main().await?; |
153 | } |
154 | |
155 | #[cfg (not(windows))] |
156 | { |
157 | println!("Named pipes are only supported on Windows!" ); |
158 | } |
159 | |
160 | Ok(()) |
161 | } |
162 | |