CODESAMPLE

Peer-to-Peer - TypeScript

Share on:

The Peer-to-Peer (P2P) pattern establishes direct communication channels between nodes (peers) in a network, eliminating the need for a central server. Each peer acts as both a client and a server, sharing resources and data directly with others. This implementation uses WebRTC Data Channels in TypeScript to create P2P connections between browsers. Each browser instance represents a peer, and utilizes signaling (omitted for brevity, but typically handled by a server) to discover and connect to other peers. Once connected, data can be sent and received directly, enabling real-time communication without intermediary servers for data transfer. TypeScript’s strong typing and class-based structure promote organization and maintainability, fitting well with the complex coordination required in a P2P system.

// peer.ts
class Peer {
  private dataChannel: RTCDataChannel | null = null;
  private peerConnection: RTCPeerConnection | null = null;
  private remotePeerId: string | null = null;

  constructor() {
    this.createPeerConnection();
    this.createDataChannel();
  }

  private createPeerConnection(): void {
    this.peerConnection = new RTCPeerConnection();

    this.peerConnection.ondatachannel = (event) => {
      this.dataChannel = event.channel;
      this.dataChannel.onmessage = (event) => {
        console.log(`Received message from peer ${this.remotePeerId}: ${event.data}`);
      };
      this.dataChannel.onopen = () => {
        console.log(`Data channel open with peer ${this.remotePeerId}`);
      }
    };

    this.peerConnection.onicecandidate = (event) => {
      if (event.candidate) {
        //In a real application, you would send this to the signaling server.
        console.log("ICE candidate:", event.candidate);
      }
    };
  }

  private createDataChannel(): void {
    this.dataChannel = this.peerConnection!.createDataChannel('myChannel');
    this.dataChannel.onopen = () => {
      console.log('Data channel is open.');
    };
  }

  async connectToPeer(peerId: string, offer: RTCSessionDescription): Promise<void> {
    this.remotePeerId = peerId;
    await this.peerConnection!.setRemoteDescription(offer);
    const answer = await this.peerConnection!.createAnswer();
    await this.peerConnection!.setLocalDescription(answer);
    //In a real application, you would send the answer to the signaling server.
    console.log("Answer (for signaling):", answer);
  }
  
  async createOffer(): Promise<RTCSessionDescription> {
    const offer = await this.peerConnection!.createOffer();
    await this.peerConnection!.setLocalDescription(offer);
    //In a real application, you would send the offer to the signaling server.
    console.log("Offer (for signaling):", offer);
    return offer;
  }

  sendMessage(message: string): void {
    if (this.dataChannel) {
      this.dataChannel.send(message);
    }
  }
}

// Example usage in a browser environment (HTML would be needed similarly)
document.addEventListener('DOMContentLoaded', () => {
  const peer1 = new Peer();
  const peer2 = new Peer();

  const sendButton = document.getElementById('sendButton') as HTMLButtonElement;
  const messageInput = document.getElementById('messageInput') as HTMLInputElement;

  sendButton.addEventListener('click', () => {
    peer1.sendMessage(messageInput.value);
    messageInput.value = "";
  });
  
  peer1.createOffer().then(offer => {
        //Simulate sending offer to peer2 (normally via a signaling server)
        peer2.connectToPeer("peer2", offer);
  });
});