Исправлена гонка записи в сокет
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/pion/webrtc/v4"
|
"github.com/pion/webrtc/v4"
|
||||||
)
|
)
|
||||||
@@ -51,7 +50,7 @@ func OnLocalICECandidate(roomID string, peerID string, candidate webrtc.ICECandi
|
|||||||
buffer.PutUint32(uint32(len([]byte(jsonCandidate))))
|
buffer.PutUint32(uint32(len([]byte(jsonCandidate))))
|
||||||
buffer.PutBytes([]byte(jsonCandidate))
|
buffer.PutBytes([]byte(jsonCandidate))
|
||||||
buffer.Flip()
|
buffer.Flip()
|
||||||
room.Server.Socket.WriteMessage(websocket.BinaryMessage, buffer.Bytes())
|
room.Server.WriteBinary(buffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обработка нового оффера от сервера для конкретного пира (при renegotiation)
|
// Обработка нового оффера от сервера для конкретного пира (при renegotiation)
|
||||||
@@ -73,5 +72,5 @@ func OnServerOffer(roomID string, peerID string, offer webrtc.SessionDescription
|
|||||||
buffer.PutUint32(uint32(len([]byte(jsonOffer))))
|
buffer.PutUint32(uint32(len([]byte(jsonOffer))))
|
||||||
buffer.PutBytes([]byte(jsonOffer))
|
buffer.PutBytes([]byte(jsonOffer))
|
||||||
buffer.Flip()
|
buffer.Flip()
|
||||||
room.Server.Socket.WriteMessage(websocket.BinaryMessage, buffer.Bytes())
|
room.Server.WriteBinary(buffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,31 @@ package sfu
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/pion/webrtc/v4"
|
"github.com/pion/webrtc/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pendingMu sync.Mutex
|
||||||
|
pendingCandidates = map[string][]webrtc.ICECandidateInit{} // key: roomID|peerID
|
||||||
|
)
|
||||||
|
|
||||||
|
func peerKey(roomID, peerID string) string {
|
||||||
|
return roomID + "|" + peerID
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractICEUfrag(sdp string) string {
|
||||||
|
for _, line := range strings.Split(sdp, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "a=ice-ufrag:") {
|
||||||
|
return strings.TrimPrefix(line, "a=ice-ufrag:")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// Вызывается при JoinWithOffer для ретрансляции RTP пакетов от издателя к другим участникам комнаты
|
// Вызывается при JoinWithOffer для ретрансляции RTP пакетов от издателя к другим участникам комнаты
|
||||||
func SetupForwardingForPeer(roomID string, publisherPeerID string, publisherPC *webrtc.PeerConnection) {
|
func SetupForwardingForPeer(roomID string, publisherPeerID string, publisherPC *webrtc.PeerConnection) {
|
||||||
publisherPC.OnTrack(func(remote *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
|
publisherPC.OnTrack(func(remote *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
|
||||||
@@ -95,21 +116,47 @@ func AddICECandidate(roomID string, peerID string, candidate webrtc.ICECandidate
|
|||||||
return ErrRoomNotFound
|
return ErrRoomNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, peer := range room.Peers {
|
var pc *webrtc.PeerConnection
|
||||||
if peer.PeerID == peerID {
|
for _, p := range room.Peers {
|
||||||
return peer.PeerConnection.AddICECandidate(candidate)
|
if p.PeerID == peerID {
|
||||||
|
pc = p.PeerConnection
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pc == nil {
|
||||||
|
return ErrPeerNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := pc.RemoteDescription()
|
||||||
|
if rd == nil {
|
||||||
|
// answer/offer еще не применен — буферизуем
|
||||||
|
pendingMu.Lock()
|
||||||
|
k := peerKey(roomID, peerID)
|
||||||
|
pendingCandidates[k] = append(pendingCandidates[k], candidate)
|
||||||
|
pendingMu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// отбрасываем stale candidate по ufrag
|
||||||
|
if candidate.UsernameFragment != nil {
|
||||||
|
current := extractICEUfrag(rd.SDP)
|
||||||
|
if current != "" && *candidate.UsernameFragment != current {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrPeerNotFound
|
err := pc.AddICECandidate(candidate)
|
||||||
|
if err != nil && strings.Contains(err.Error(), "doesn't match the current ufrags") {
|
||||||
|
// поздний старый кандидат — игнорируем
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обрабатывает SDP ответ от клиента при renegotiation
|
// Обрабатывает SDP ответ от клиента при renegotiation
|
||||||
func HandleClientAnswer(roomID string, peerID string, answer webrtc.SessionDescription) error {
|
func HandleClientAnswer(roomID string, peerID string, answer webrtc.SessionDescription) error {
|
||||||
roomsMu.RLock()
|
room, exists := GetRoom(roomID)
|
||||||
room, ok := rooms[roomID]
|
if !exists {
|
||||||
if !ok {
|
|
||||||
roomsMu.RUnlock()
|
|
||||||
return ErrRoomNotFound
|
return ErrRoomNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +167,24 @@ func HandleClientAnswer(roomID string, peerID string, answer webrtc.SessionDescr
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roomsMu.RUnlock()
|
|
||||||
|
|
||||||
if pc == nil {
|
if pc == nil {
|
||||||
return ErrPeerNotFound
|
return ErrPeerNotFound
|
||||||
}
|
}
|
||||||
return pc.SetRemoteDescription(answer)
|
|
||||||
|
if err := pc.SetRemoteDescription(answer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// после применения answer — применяем отложенные кандидаты
|
||||||
|
k := peerKey(roomID, peerID)
|
||||||
|
pendingMu.Lock()
|
||||||
|
queue := pendingCandidates[k]
|
||||||
|
delete(pendingCandidates, k)
|
||||||
|
pendingMu.Unlock()
|
||||||
|
|
||||||
|
for _, c := range queue {
|
||||||
|
_ = AddICECandidate(roomID, peerID, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ func processData(data <-chan []byte, connection *connection.Connection) {
|
|||||||
response.Put(0x01)
|
response.Put(0x01)
|
||||||
response.Flip()
|
response.Flip()
|
||||||
// Отправляем ответ клиенту
|
// Отправляем ответ клиенту
|
||||||
connection.Socket.WriteMessage(websocket.BinaryMessage, response.Bytes())
|
connection.WriteBinary(response.Bytes())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
connection.Socket.WriteMessage(websocket.BinaryMessage, []byte{0xFF})
|
connection.WriteBinary([]byte{0xFF})
|
||||||
logger.LogWarnMessage("failed handshake from " + connection.Socket.RemoteAddr().String() + " because of invalid secret key")
|
logger.LogWarnMessage("failed handshake from " + connection.Socket.RemoteAddr().String() + " because of invalid secret key")
|
||||||
connection.Socket.Close()
|
connection.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ func processData(data <-chan []byte, connection *connection.Connection) {
|
|||||||
response.PutBytes([]byte(room.RoomID))
|
response.PutBytes([]byte(room.RoomID))
|
||||||
response.Flip()
|
response.Flip()
|
||||||
// Отправляем ответ клиенту
|
// Отправляем ответ клиенту
|
||||||
connection.Socket.WriteMessage(websocket.BinaryMessage, response.Bytes())
|
connection.WriteBinary(response.Bytes())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ func processData(data <-chan []byte, connection *connection.Connection) {
|
|||||||
response.PutBytes(answerBytes)
|
response.PutBytes(answerBytes)
|
||||||
response.Flip()
|
response.Flip()
|
||||||
// Отправляем ответ клиенту
|
// Отправляем ответ клиенту
|
||||||
connection.Socket.WriteMessage(websocket.BinaryMessage, response.Bytes())
|
connection.WriteBinary(response.Bytes())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,38 @@
|
|||||||
package connection
|
package connection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Connection struct {
|
type Connection struct {
|
||||||
Identificator string
|
Identificator string
|
||||||
//Подсоединенный сокет
|
Socket *websocket.Conn
|
||||||
Socket *websocket.Conn
|
|
||||||
|
writeMu sync.Mutex
|
||||||
|
closeMu sync.Mutex
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) WriteBinary(payload []byte) error {
|
||||||
|
c.writeMu.Lock()
|
||||||
|
defer c.writeMu.Unlock()
|
||||||
|
return c.Socket.WriteMessage(websocket.BinaryMessage, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) WriteJSON(v any) error {
|
||||||
|
c.writeMu.Lock()
|
||||||
|
defer c.writeMu.Unlock()
|
||||||
|
return c.Socket.WriteJSON(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) Close() error {
|
||||||
|
c.closeMu.Lock()
|
||||||
|
defer c.closeMu.Unlock()
|
||||||
|
if c.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.closed = true
|
||||||
|
return c.Socket.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user