sdk/Peer.js — endPair():
- this.rtc?.destroy() eklendi
- Yerel akışlar durur, RTCPeerConnection kapanır
sdk/index.js — sinyal handler'ları:
- end/pair: peer.rtc?.destroy() (alan taraf da kapatır)
- peer/disconnect: peer.rtc?.destroy() (WebSocket kopunca RTC da kapanır)
public/studio/Studio.js:
_clearPeerTiles(peerId):
- data-peer-id dataset'i ile local + remote grid'den tile'ları kaldırır
- Gizli <audio> elemanını srcObject=null + remove() ile temizler
- CSS.escape ile güvenli selector
tile.dataset.peerId: _addLocalTile ve _addRemoteTile her ikisinde eklendi
tile._audioEl: ses tile'larında referans saklanır (clearPeerTiles için)
_watchIncoming: peer.rtc.on('disconnected') → clearPeerTiles + rebuildPeerItems
end/pair handler: clearPeerTiles(from) çağrısı eklendi
peer/disconnect handler: clearPeerTiles(peer.socketId) çağrısı eklendi
_addLocalTile/_addRemoteTile: peerLabel yerine peer objesi alır
go test -race ./... — yeşil
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||
|---|---|---|
| internal | ||
| loadtest | ||
| public | ||
| sdk | ||
| tools | ||
| .agent-prompt-port.txt | ||
| .agent-prompt.txt | ||
| .gitea-auth.example.json | ||
| .gitignore | ||
| CLAUDE.md | ||
| LICENSE | ||
| PORT-PROGRESS.md | ||
| README.md | ||
| REVIEW.md | ||
| decisions.md | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| todo.md | ||
README.md
MWSE — Micro Web Socket Engine
MWSE, kendisine bağlanan eşleri birbirleriyle eşleştirerek eşler arası veri tünelleri oluşturan geniş ölçekli bir WebSocket mikroservis altyapısıdır.
Servis; cihazları senkronize etmek, odalar oluşturmak, sohbet ve görüntülü görüşme yazılımları için gerçek zamanlı altyapı sağlamak amacıyla kullanılır. Sunucu cihazları sanallaştırdığı için eşler birbirlerinin gerçek IP adresini veya cihaz bilgisini bilmeden düşük gecikmeli, çift yönlü iletişim kurabilir.
Durum (Go engine, v1.0.0)
Motor Node.js'ten Go ile yeniden yazıldı. Concurrency modeli goroutine +
sync.RWMutex + bağlantı başına tek-yazıcı (actor) deseni üzerine kuruludur;
Node.js'teki "leave-while-send" race condition ve EventPool promise takılması (#33)
giderildi. Yük testi: 150 bağlantı, ~210 k msg/s relay, RSS ~43 MB.
| Özellik | Durum |
|---|---|
| WebSocket bağlantı yaşam döngüsü | ✅ |
| Oda oluşturma / katılma / çıkma | ✅ |
| Eşleme (pair) sistemi | ✅ |
| Paket tünelleme (pack/to, pack/room) | ✅ |
| Veri senkronizasyonu (data/sync, sync/pool) | ✅ |
| Bildirim + suit yanıtı (notify/send, notify/reply) | ✅ |
| 3. parti sunucu köprüsü (bridge) | ✅ |
| Sanal IP / numara / kısa kod + alt ağ | ✅ |
| WebRTC kütüphanesi (perfect negotiation, çoklu track) | ✅ |
| Studio UI (Miller kolonlar) | ✅ |
| İkili çerçeveleme (binary framing) | ⏳ 2.5.0 |
| SRS entegrasyonu (binlerce izleyici) | ⏳ 2.0.0 |
Kurulum ve çalıştırma
Gereksinimler
- Go 1.22+
Sunucuyu başlat
go mod tidy
go run .
# Varsayılan: 0.0.0.0:7707
Ortam değişkenleri
| Değişken | Varsayılan | Açıklama |
|---|---|---|
MWSE_HOST |
0.0.0.0 |
Bind adresi |
MWSE_PORT |
7707 |
Dinleme portu |
MWSE_PUBLIC_DIR |
./public |
Statik dosyalar (/status.xml vb.) |
MWSE_SDK_DIR |
./sdk |
ES modül SDK (/sdk/) |
MWSE_OUTBOUND_BUFFER |
1024 |
Bağlantı başına gönderim kuyruğu |
MWSE_MAX_MESSAGE_SIZE |
16777216 |
Maksimum gelen frame boyutu (bayt) |
MWSE_PING_INTERVAL |
10s |
Heartbeat ping aralığı |
MWSE_SHUTDOWN_TIMEOUT |
10s |
Graceful shutdown bekleme süresi |
BRIDGE_APPROVE_URL |
— | Bağlantı onay URL'i (3. parti köprü) |
BRIDGE_TRIGGER_URL |
— | Suit yanıtı push URL'i |
BRIDGE_INBOX |
— | 1 ile inbox'ı etkinleştir |
Testler
go test -race ./...
Frontend SDK entegrasyonu
SDK saf vanilla ES modülü olarak /sdk/ endpoint'inden sunulur. Bundler gerekmez;
native import ile çalışır.
<script type="module">
import MWSE from 'http://localhost:7707/sdk/index.js';
const mwse = new MWSE(); // endpoint: otomatik aynı sunucudan
mwse.on('scope', async () => {
console.log('Bağlandı:', mwse.me.socketId);
const room = mwse.room({ name: 'genel', joinType: 'free', ifexistsJoin: true });
await room.createRoom();
room.on('message', (pack, peer) => console.log(peer.socketId, ':', pack));
room.send({ text: 'merhaba!' });
});
</script>
WebRTC (P2P ses/video/dosya)
import MWSE from '/sdk/index.js';
import { MediaSources } from '/sdk/webrtc/index.js';
const mwse = new MWSE();
mwse.me.on('accepted/pair', async peer => {
const polite = mwse.me.socketId < peer.socketId;
peer.rtc.connect({ polite });
// Kamera + mikrofon
const stream = await MediaSources.cameraAndMic();
peer.rtc.addStream('cam', stream);
// Gelen video/ses
peer.rtc.on('track', (track, streams) => {
const video = document.createElement('video');
video.srcObject = streams[0];
video.autoplay = true;
document.body.appendChild(video);
});
// Dosya gönderme
await peer.rtc.sendFile(file);
});
Studio UI
import MWSE from '/sdk/index.js';
import Studio from '/sdk/studio/index.js';
const mwse = new MWSE();
const studio = new Studio(mwse, '#app');
mwse.on('scope', () => studio.mount());
Demo dosyaları
Sunucu çalışırken http://localhost:7707/demos/ altında:
| Demo | Dosya | Açıklama |
|---|---|---|
| Chat | chat.html |
~20 satır JS ile odalı gerçek zamanlı sohbet |
| Sesli görüşme | audio.html |
P2P WebRTC mikrofon (çift yönlü) |
| Video görüşme | video.html |
P2P WebRTC kamera ızgara görünümü |
API kontrolü (/api)
# API anahtarı al
KEY=$(curl -s -X POST localhost:7707/api/auth/key \
-H 'Content-Type: application/json' \
-d '{"domain":"myapp"}' | jq -r .key)
# Tüm odaları listele
curl -s localhost:7707/api/rooms | jq .
# Belirli bir istemciye mesaj gönder
curl -s -X POST localhost:7707/api/client/<id>/send \
-H "x-api-key: $KEY" \
-H 'Content-Type: application/json' \
-d '{"pack": {"hello": "world"}}'
# 3. parti köprü — inbox boşalt
curl -s -X POST localhost:7707/api/bridge/inbox \
-H "x-api-key: $KEY"
Mimari
Tarayıcı (WebSocket)
│
▼
ws.Hub — router + client registry
├─ services/yourid.go Bağlantı açılınca wsts/hello + id sinyalleri
├─ services/auth.go Pairing, erişilebilirlik
├─ services/room.go Oda oluşturma / yönetimi
├─ services/datatransfer.go pack/to, request/to, response/to tünelleri
├─ services/notify.go Store-and-forward bildirim + suit yanıtı
├─ services/datastore.go Aktif & pasif veri senkronizasyonu
├─ services/bridge.go 3. parti sunucu inbox
├─ services/ippressure.go Sanal IP / numara / kısa kod + alt ağ (/24)
└─ services/session.go Oturum bayrakları (readable/writable/pairinfo)
httpserver
├─ GET /sdk.js → /sdk/index.js (301 yönlendirme)
├─ GET /sdk/* ES modül SDK dosyaları
├─ GET /demos/* Demo HTML dosyaları
├─ GET|POST /api/* Kontrol düzlemi
└─ GET /* Statik public/ dosyaları
sdk/
├─ index.js MWSE ana sınıf (bağlantı + sinyal yönlendirme)
├─ webrtc/ WebRTC kütüphanesi
│ ├─ index.js RTCEngine (PeerConnection yönetimi, ICE restart)
│ ├─ PeerConnection.js RTCPeerConnection wrapper + full event izleme
│ ├─ Negotiator.js Perfect negotiation (RFC 8829)
│ ├─ StreamManager.js addStream/replaceTrack/setEncodings
│ ├─ DataChannel.js Birincil veri kanalı + oto-yeniden bağlanma
│ ├─ MediaSources.js getUserMedia/getDisplayMedia/AudioContext fabrikaları
│ ├─ FileSender.js Paralel DataChannel dosya transferi
│ └─ CanvasCompositor.js Çoklu video track birleştirme (grid/pip/focus)
└─ studio/
├─ index.js Studio UI giriş noktası
├─ ColumnView.js Miller kolon yöneticisi
├─ Column.js Tek kolon (başlık + arama + liste)
└─ style.css Koyu masaüstü teması
Güvenlik
- İstemci mesajları uygulama katmanında doğrulanmaz. Hassas veriler için imzalama/şifreleme ekleyin.
-
- parti köprü (
BRIDGE_APPROVE_URL) kullanılıyorsa bağlantı onayı uygulama sunucusuna delege edilir (fail-closed).
- parti köprü (
.gitea-auth.jsondosyası commit'e asla girmez (.gitignore'da).