Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tcpip.js networking examples / docs #1197

Open
gregnr opened this issue Dec 16, 2024 · 1 comment
Open

tcpip.js networking examples / docs #1197

gregnr opened this issue Dec 16, 2024 · 1 comment

Comments

@gregnr
Copy link

gregnr commented Dec 16, 2024

Hey 👋 amazing tool.

So far the networking examples focus on connecting to external networks or proxies (fetch proxy, WISP). I'm interested in local networking - communicating directly between JS and v86. This would open the door to use cases like running a server-side program (like Postgres, nginx, etc) inside v86 and communicate with it from JS.

I built tcpip.js to solve this. It's a virtual network stack that can run in the browser. It's built on top of lwIP compiled to WASM with bindings to hook into each layer of the network stack (L2, L3, L4).

Example use case

Set up plumbing that pipes ethernet packets between v86 and the virtual network stack.

import { createStack } from 'tcpip';
import { createV86NetworkStream } from '@tcpip/v86';

const stack = await createStack();

// Tap interfaces hook into ethernet (L2) frames
const tapInterface = await stack.createTapInterface({
  mac: '01:23:45:67:89:ab',
  ip: '192.168.1.1/24',
});

const emulator = new V86();

// Adds net0 listener and exposes as readable/writable streams
const vmNic = createV86NetworkStream(emulator);

// Forward frames between the tap interface and the VM's NIC
tapInterface.readable.pipeTo(vmNic.writable);
vmNic.readable.pipeTo(tapInterface.writable);

Now we can establish a TCP connection to the emulator from JS (assuming VM has IP 192.168.1.2 and a server listening on port 80):

const connection = await stack.connectTcp({
  host: '192.168.1.2',
  port: 80,
});

// connection.writable is a standard WritableStream
const writer = connection.writable.getWriter();

// Send data
await writer.write(new TextEncoder().encode('Hello, world!'));
await writer.close();

// Listen for incoming data
for await (const chunk of connection) {
  console.log(new TextDecoder().decode(chunk));
}

Or we can create a TCP server and listen for inbound connections:

const listener = await stack.listenTcp({
  port: 80,
});

// TcpListener is an async iterable that yields TcpConnections
for await (const connection of listener) {
  const writer = connection.writable.getWriter();

  // Send data
  await writer.write(new TextEncoder().encode('Hello, world!'));
  await writer.close();

  // Listen for incoming data
  for await (const chunk of connection) {
    console.log(new TextDecoder().decode(chunk));
  }
}

Other use cases

I'm planning to add bridge interface support soon, which would allow you to use tcpip.js as a switch/router. This can be useful for connecting more than 2 VMs together in a LAN.


I was wondering if you are interested in adding docs / examples for this use case? If so happy to put a PR together that adds some demos and networking docs.

@SuperMaxusa
Copy link
Contributor

Nice work! Would you like to share it in this discussion: #1048?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants