有关WebRTC的一些概念可以参考另外一篇文章 WebRTC概念 我这里交换媒体信息、网络信息交换使用的是WebSocket,媒体信息是什么参考 WebRTC概念 以下的使用方法中,只有使用WebRTC传输通用数据跟音频流的,视频流要再自己配置一下 使用SFU结构,所以并没有用户与用户之间直接的信令交换,这些东西都给后台处理了,什么是SFU架构参考另外一篇文章 WebRTC中的SFU架构 使用方法:userPeer导出一个localAudioRef,这个是本地音视频流的dom;还可以导出一个PeerRef,这是WebRTC要用的peer 其实localAudioRef好像不放这里面也是可以的,具体的情况实际使用的时候再决定吧 代码: import  {  useEffect,  useContext,  useRef }  from  "react" ; 
import  {  AppContext }  from  "../App" ; 
const  usePeer  =  ( )  =>  { const  {  peerRef,  socketRef }  =  useContext ( AppContext) !  const  remoteAudioRef =  useRef < HTMLDivElement> ( null ) ;  const  localAudioRef =  useRef < HTMLAudioElement> ( null )  const  createPeer  =  ( )  =>  {  const  peer =  new  RTCPeerConnection ( ) ; peer. onicecandidate  =  ( event)  =>  {  } peer. ontrack  =  ( event)  =>  {  const  audio =  document. createElement ( 'audio' ) ; audio. srcObject =  event. streams[ 0 ] ; audio. autoplay =  true ; audio. controls =  false ; remoteAudioRef. current?. appendChild ( audio) ; event. track. onmute  =  ( )  =>  {  audio. play ( ) ; }  event. streams[ 0 ] . onremovetrack  =  ( )  =>  {  if ( audio. parentNode)  { audio. parentNode. removeChild ( audio) ; } } } return  peer; } const  getLocalStream  =  async  ( )  =>  {  const  stream =  await  navigator. mediaDevices. getUserMedia ( { audio:  true , video:  false ,  } ) return  stream; } const  handleLocalStream  =  async  ( )  =>  {  const  stream =  await  getLocalStream ( ) ; stream. getTracks ( ) . forEach ( ( track)  =>  { peerRef. current?. addTrack ( track,  stream) ; 			} ) } useEffect ( ( ) => {  handleLocalStream ( ) peerRef. current =  createPeer ( ) ; } , [ ] ) return  {  localAudioRef } 
} export  default  usePeer; 
import  {  useEffect,  useRef,  useContext }  from  "react" ; 
import  {  AppContext }  from  "../App" ; 
const  useDataChannel  =  ( )  =>  { const  {  peerRef }  =  useContext ( AppContext) ! const  dataChannel =  useRef < RTCDataChannel> ( ) ; const  createDataChannel  =  ( )  =>  {  const  channel =  peerRef. current! . createDataChannel ( "myDataChannel66666666_1395212519" ) ; channel. onopen  =  ( )  =>  { console . log ( "[dataChannel open]" ) ; } channel. onmessage  =  ( event)  =>  { } channel. onclose  =  ( )  =>  { console . log ( "[dataChannel close]" ) ; } return  channel} useEffect ( ( ) => {  dataChannel. current =  createDataChannel ( ) ; } , [ ] ) } export  default  useDataChannel
因为不想useSocket太多代码了,所以分了一个这样的文件出来,主要是ws收到信息的函数 import  {  useRef,  useContext,  useEffect }  from  "react" ; 
import  {  AppContext }  from  "../../App" ; const  useHandleOffer  =  ( )  =>  { const  {  peerRef,  socketRef }  =  useContext ( AppContext) ! const  handleOffer  =  async  ( offer:  any )  =>  {  const  peer =  peerRef. currentawait  peer?. setRemoteDescription ( offer) ;  const  answer =  await  peer?. createAnswer ( ) ;  await  peer?. setLocalDescription ( answer) ;  socketRef. current?. send ( )  } const  handleCandidate  =  ( candidate:  any )  =>  {  peerRef. current?. addIceCandidate ( candidate) ;  } return  {  handleOffer,  handleCandidate }  
} export  default  useHandleOffer; 
import  {  useEffect,  useContext }  from  "react" ; 
import  {  AppContext }  from  "../../App" ; 
import  useSocketHandle from  "./useSocketHandle" ; const  WS_URL  =  'wss://xxx'  const  useSocket  =  ( )  =>  { const  {  handleOffer,  handleCandidate }  =  useSocketHandle ( ) ;  const  {  socketRef }  =  useContext ( AppContext) !  let  heartTimer =  0 ;  const  heartCheck  =  ( socket:  WebSocket)  =>  {  clearInterval ( heartTimer) ;  heartTimer =  setInterval ( ( )  =>  { socket. send ( 'xxx' ) ;  } ,  30000 ) ; } const  createSocket  =  ( )  =>  {  if  ( socketRef. current)  return ; const  socket =  new  WebSocket ( ` ${ WS_URL } ` )  socket. onopen  =  ( )  =>  {  console . log ( "[ws open] 连接已建立" ) ; heartCheck ( socket) ; } ; socket. onmessage  =  async  ( event)  =>  {  const  msg =  JSON . parse ( event. data)  switch  ( msg. event)  { case  'offer' :  handleOffer ( JSON . parse ( msg. data) ) break ; case  'candidate' :  handleCandidate ( JSON . parse ( msg. data) ) break ; } } ; socket. onclose  =  ( )  =>  {  console . log ( '[ws close] 连接中断' ) ; socketRef. current =  undefined clearInterval ( heartTimer) ;  } ; socket. onerror  =  ( error)  =>  {  console . log ( ` [error] 连接错误  ` ,  error) ; } ; return  socket; } useEffect ( ( )  =>  {  socketRef. current =  createSocket ( ) ; } ,  [ ] ) 
} export  default  useSocket
要注意的是,我这里只是提供了一个大概的框架,具体的一些细节,比如说跟后台交换candidate、offer、answer这种,还是需要自己去填写的 另外一个点,如果按照这里搞出来,answer、offer什么的都完成了交换,视频也不一定有的,你要自己加上一些视频的配置,比如说获取音视频流的时候video变为true,以及动态生成元素的时候也把video给生成一下。 import  usePeer from  '../../hooks/usePeer' ; 
import  useDataChannel from  '../../hooks/useDataChannel' ; 
import  useSocket from  '../../hooks/socket/useSocket' ; export  default  function  Home ( )  { const  {  localAudioRef }  =  usePeer ( ) useSocket ( ) useDataChannel ( ) return  ( < div className= 'Home' > < div className= "remoteAudioContainer" > < / div> < audio src= ""  ref= { localAudioRef} > < / audio> < / div> ) 
}