5

Nó đã làm việc ! Chúng tôi có hai trình duyệt trên các máy khác nhau nói chuyện với nhau mà không cần máy chủ.

Chà ... sau cái bắt tay ban đầu. Bạn vẫn cần một máy chủ cho điều đó.

Đó là một ngày cuối tuần vui vẻ. Hai phiên sinh hoạt. Cuối cùng nó cũng hoạt động vào đêm khuya khi máy quay không quay. Tôi không ở đâu để nó hoạt động kịp thời cho một bản demo blockchain thực sự cho WeAreDevelopers của tôi nói chuyện vào thứ Sáu này.

Bạn có thể thử nó ở đây . Mở liên kết đó trong hai trình duyệt, có thể trên các máy khác nhau.

Bạn có thể phải thử một vài lần, nó hơi khó. 

Đây là cách nó hoạt động:

Cách sử dụng WebRTC để kết nối trình duyệt trên các thiết bị khác nhau

Bạn có thể sử dụng WebRTC để làm cho các trình duyệt nói chuyện trực tiếp với nhau mà không cần máy chủ. Nhưng vì không có khám phá dịch vụ, bạn cần một máy chủ báo hiệu để các trình duyệt có thể tìm thấy nhau.

Dòng chảy như thế này:

  1. Khách hàng 1 nói xin chào với máy chủ và đăng ký.
  2. Khách hàng 2 nói xin chào đến máy chủ và đăng ký.
  3. Máy chủ chứa một danh sách các định danh (tên người dùng).
  4. Khách hàng 1 yêu cầu máy chủ gọi cho Khách hàng 2.
  5. Máy chủ báo cho Khách hàng 2 có cuộc gọi.
  6. Khách hàng 2 trả lời cuộc gọi.
  7. Khách hàng 1 và Khách hàng 2 hiện đang nói chuyện trực tiếp.

Chúng tôi đã theo dõi ví dụ trò chuyện WebRTC này từ MDN để mô hình hóa mã của chúng tôi.

Báo hiệu máy chủ WebSocket

Báo hiệu là quá trình bắt tay giữa hai trình duyệt. Việc triển khai của chúng tôi sử dụng WebSockets để làm điều đó.

Phần máy chủ giống như ví dụ MDN. Bản sao mì ống nguyên chất.

Chúng tôi thực hiện một số thay đổi để làm cho nó làm việc với now.sh . Cụ thể, chúng tôi đã xóa tất cả nội dung SSL. Zeit kết thúc các máy chủ của chúng tôi trong một máy chủ SSL an toàn, sau đó nói chuyện với máy chủ thực tế của chúng tôi thông qua một kết nối không được mã hóa.

Đó là một vấn đề đau đớn để học hỏi.

WebSockets không hoạt động trong các trình duyệt hiện đại mà không có SSL. Và họ không làm việc với các chứng chỉ tự ký trừ khi bạn đang chạy localhost. Nếu bạn muốn các trình duyệt không có trên máy của mình để nói chuyện, bạn sẽ phải đảm bảo chứng chỉ SSL thực.

Cách dễ nhất là triển khai trên now.

Kết nối với máy chủ báo hiệu

Nói chuyện với máy chủ xảy ra thông qua WebSockets với lớp trình trợ giúp 40 dòng . Khởi tạo lớp học, tạo kết nối, lắng nghe tin nhắn.

Chúng tôi tạo một new WebSockettrong connectToSocket, thêm một số cuộc gọi lại và hy vọng điều tốt nhất. Trình onmessagenghe cho phép chúng ta thêm các trình nghe tin nhắn bổ sung sau thông qua messageListenersmảng.

sendToServercho phép chúng tôi gửi một đối tượng JSON đến máy chủ của chúng tôi và addMsgListenercho phép chúng tôi thêm một trình nghe tin nhắn mới. Chúng tôi sẽ sử dụng điều này để kết nối PeerConnectionngười trợ giúp của chúng tôi với máy chủ của chúng tôi.

Thiết lập kết nối ngang hàng

Học bài học của chúng tôi từ WebRTC phần 1 , chúng tôi chia công cụ RTCPeerConnection của chúng tôi thành một lớp trợ giúp .

Đó là khoảng 148 dòng và xử lý toàn bộ vòng đời. Chúng tôi đã nói về mã trước đây , vì vậy đây là một bản tóm tắt:

constructorthiết lập một loạt các ví dụ, thiết lập một RTCPeerConnectionđối tượng mới , cho biết nó iceServerssẽ sử dụng, kết nối người nghe sự kiện cục bộ, bắt đầu nghe tin nhắn trên máy chủ báo hiệu của chúng tôi và thêm luồng phương tiện của chúng tôi vào peerConnection.

Bước tiếp theo là handleICECandidate, thiết lập kết nối tương tác, sẽ kích hoạt khi thử kết nối mới. Nó ping máy chủ báo hiệu của chúng tôi và nói, "Yo, ứng cử viên băng mới ở đây."

handleICECandidateEvent = event => {
        if (event.candidate) {
            this.signalingConnection.sendToServer({
                type: "new-ice-candidate",
                target: this.targetUsername,
                candidate: event.candidate
            });
        }
    };

Sau đó, chúng tôi đã có handleNegotiationNeededEvent, được gọi là khi RTCPeerConnectionmột số cuộc đàm phán cần phải xảy ra. Tôi không biết những gì làm cho nó nói rằng.

Nhưng chức năng tạo ra một ưu đãi kết nối mới, cập nhật mô tả SDP cục bộ và thông báo cho máy chủ báo hiệu của chúng tôi rằng chúng tôi đang cố gắng gọi cho ai đó.

handleNegotiationNeededEvent = () => {
        const {
            username,
            targetUsername
        } = this;
        this.peerConnection
            .createOffer()
            .then(offer => this.peerConnection.setLocalDescription(offer))
            .then(() =>
                this.signalingConnection.sendToServer({
                    name: username,
                    target: targetUsername,
                    type: "video-offer",
                    sdp: this.peerConnection.localDescription
                })
            )
            .catch(console.error);
    };

Xử lý tin nhắn báo hiệu

Sau đó, chúng tôi có những thứ thú vị: xử lý tin nhắn từ máy chủ báo hiệu của chúng tôi.

 onSignalingMessage = (msg) => {
        switch (msg.type) {
            case "video-answer": // Callee has answered our offer
                this.videoAnswer(msg);
                break;

            case "new-ice-candidate": // A new ICE candidate has been received
                this.newICECandidate(msg)
                break;

            case "hang-up": // The other peer has hung up the call
                this.close()
                break;
        }
    }

Khi một tin nhắn đến, chúng ta có thể làm một vài điều khác nhau. Thiết lập bản thân là bên trả lời, thêm ứng viên mới vào kết nối của chúng tôi hoặc đóng.

Các chức năng này là các trình bao bọc mỏng trên API WebRTC.

 videoAnswer = ({
        sdp
    }) => {
        this.peerConnection
            .setRemoteDescription(new RTCSessionDescription(sdp))
            .catch(console.error);
    }

    newICECandidate = ({
        candidate
    }) => {
        this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
    }

   close = () => {
        this.peerConnection.close();
        this.peerConnection = null;

        this.onClose()
    }

Đó là đối tượng PeerConnection của chúng tôi. Về lý thuyết, chúng ta có thể khởi tạo nhiều trong số chúng để kết nối với nhiều máy từ xa cùng một lúc.

Đó sẽ là một thử nghiệm thú vị.

Để tất cả chúng cùng nhau

Giữ tất cả lại với nhau là WebRTCPeerConnectionWithServerthành phần React của chúng tôi . Nó kết xuất giao diện người dùng, khởi tạo cả hai lớp trợ giúp từ phía trên và xử lý người dùng nhấp vào nút để điều khiển quá trình.

Bạn có thể xem toàn bộ tập tin trên GitHub .

Dưới đây là những phần nổi bật.

    call = user => {
        this.setState({
            targetUsername: user
        });
        this.createPeerConnection();
    };

    hangUp = () => {
        this.signalingConnection.sendToServer({
            name: this.state.username,
            target: this.state.targetUsername,
            type: "hang-up"
        });
        this.peerConnection.close();
    };

    createPeerConnection = () => {
        if (this.peerConnection) return;

        this.peerConnection = new PeerConnection({
            gotRemoteStream: this.gotRemoteStream,
            gotRemoteTrack: this.gotRemoteTrack,
            signalingConnection: this.signalingConnection,
            onClose: this.closeVideoCall,
            localStream: this.state.localStream,
            username: this.state.username,
            targetUsername: this.state.targetUsername
        });
    };

    closeVideoCall = () => {
        this.remoteVideoRef.current.srcObject &&
            this.remoteVideoRef.current.srcObject
                .getTracks()
                .forEach(track => track.stop());
        this.remoteVideoRef.current.src = null;

        this.setState({
            targetUsername: null,
            callDisabled: false
        });
    };

calllà nơi niềm vui bắt đầu. Lưu người mà chúng ta đang gọi để nêu và tạo kết nối ngang hàng.

createPeerConnectionvượt qua tất cả mọi thứ vào PeerConnectionlớp học của chúng tôi .

hangUpcloseVideoCalllàm việc cùng nhau để kết thúc cuộc gọi của chúng tôi. Chúng tôi cần cả hai vì một cái là do người dùng điều khiển và cái còn lại được gọi khi hangout đến từ phía bên kia.

Một điều cuối cùng

Có một tin nhắn từ máy chủ báo hiệu chúng ta phải xử lý trong khu vực keo: lời đề nghị cho một cuộc gọi.

  case "video-offer": // Invitation and offer to chat
        this.createPeerConnection();
        this.peerConnection.videoOffer(msg);
        break;

Khi máy chủ cho chúng tôi biết ai đó muốn kết nối, chúng tôi phải tạo một PeerConnectionđối tượng mới trên máy khách của mình và xử lý ưu đãi. Xử lý đề nghị có nghĩa là đặt mô tả SDP từ xa và gửi câu trả lời.

 videoOffer = ({
        sdp
    }) => {
        const {
            username,
            targetUsername
        } = this;

        this.peerConnection
            .setRemoteDescription(new RTCSessionDescription(sdp))
            .then(() => this.peerConnection.createAnswer())
            .then(answer => {
                return this.peerConnection.setLocalDescription(answer);
            })
            .then(() => {
                this.signalingConnection.sendToServer({
                    name: username,
                    targetUsername: targetUsername,
                    type: "video-answer",
                    sdp: this.peerConnection.localDescription
                });
            })
            .catch(console.error);
    }

Và sau đó nó hoạt động

Nếu tất cả các ngôi sao đều thẳng hàng, giờ đây bạn có thể có một cuộc gọi giữa 2 trình duyệt trên các máy khác nhau mà không cần nói chuyện lại với máy chủ.

|