SIP.js یک کتابخانه جاوااسکریپت/TypeScript برای ساخت تلفن نرمافزاری داخل مرورگر است. این کتابخانه SIP را از طریق WebSocket به PBX یا SIP Server میرساند و برای Media از WebRTC استفاده میکند. نتیجه میتواند یک تلفن تحت وب باشد که داخل CRM، پنل پشتیبانی، سامانه فروش، داشبورد Call Center یا یک PWA اجرا شود و بدون نصب Softphone جداگانه تماس بگیرد.
اما ساخت یک Web Phone واقعی فقط با چند خط new SimpleUser() تمام نمیشود. در Production باید همزمان چند لایه را درست طراحی کنید: WSS و Certificate، دسترسی Microphone، Autoplay، ICE/STUN/TURN، DTLS-SRTP، Reconnect، Registration، تغییر شبکه و محدودیتهای Background/Push مرورگر.
تا ۲۰ اکتبر ۲۰۲۶، صفحه Release رسمی SIP.js نسخه 0.21.2 را بهعنوان آخرین Release فهرست میکند. خانواده 0.21 همچنین SessionManager را برای سناریوهای چندتماسی اضافه کرده و نسخه 0.21.2 امکان SIP OPTIONS Ping در SessionManager را ارائه میکند.
خلاصه معماری: Browser با WSS به Asterisk/Kamailio/SBC وصل میشود، SIP Signaling روی WebSocket جابهجا میشود و صدای تماس از مسیر WebRTC یعنی ICE + DTLS-SRTP عبور میکند. WSS فقط Signaling است و بهتنهایی مشکل NAT یا Media را حل نمیکند.
SIP.js دقیقاً چه کاری انجام میدهد؟
SIP.js یک SIP User Agent برای محیط JavaScript است. پروژه رسمی آن قابلیت تماس صوتی/تصویری WebRTC، SIP over WebSocket، Hold، Transfer، DTMF، Messaging و Presence را فراهم میکند و با SIP Serverهای استاندارد مانند Asterisk و FreeSWITCH سازگار است.
برای توسعه سه سطح اصلی دارید:
| سطح | مناسب برای | محدودیت/مزیت |
|---|---|---|
| SimpleUser | یک تماس ساده در SPA | شروع سریع؛ یک Session همزمان |
| SessionManager | Call Center و چند Session | کنترل بیشتر، Reconnect/Retry بهتر |
| Full API | محصول حرفهای سفارشی | انعطاف کامل و پیچیدگی بیشتر |
اگر فقط یک تلفن تکخط میخواهید، SimpleUser بهترین نقطه شروع است. اگر چند تماس همزمان، Queue، Holdهای متعدد یا State Management پیچیده دارید، SessionManager منطقیتر است.
معماری یک تلفن تحت وب با SIP.js
Browser / SIP.js
|
| WSS (SIP signaling)
v
Asterisk / SBC / SIP Proxy
|
+------ SIP Trunk ------ Provider
|
+------ RTP/WebRTC Media Path
Browser Media:
getUserMedia()
|
v
RTCPeerConnection
|
ICE / STUN / TURN
|
DTLS-SRTP
|
v
PBX / Media Gateway
دو مسیر را از هم جدا ببینید:
🔹 Signaling: REGISTER، INVITE، BYE، REFER و بقیه پیامهای SIP روی WSS.
🔹 Media: Audio/Video روی WebRTC با ICE و DTLS-SRTP.
به همین دلیل ممکن است SIP REGISTER کاملاً موفق باشد ولی تماس صدا نداشته باشد؛ در این حالت Signaling سالم است اما Media Path شکست خورده است.
WSS؛ اولین شرط یک Web Phone واقعی
صفحه شما در Production باید روی HTTPS اجرا شود و اتصال SIP نیز بهتر است با wss:// باشد. مرورگرها Mixed Content را محدود میکنند و طبق راهنمای MDN نباید از صفحه HTTPS یک WebSocket ناامن ws:// باز کنید.
نمونه URL:
wss://pbx.example.com:8089/ws
در Asterisk، WebRTC معمولاً از HTTP/WebSocket داخلی استفاده میکند. مستند رسمی Asterisk مثال زیر را برای HTTPS/WSS ارائه میکند:
[general]
enabled=yes
bindaddr=0.0.0.0
bindport=8088
tlsenable=yes
tlsbindaddr=0.0.0.0:8089
tlscertfile=/etc/asterisk/keys/fullchain.pem
tlsprivatekey=/etc/asterisk/keys/privkey.pem
و Transport مربوط به PJSIP:
[transport-wss]
type=transport
protocol=wss
bind=0.0.0.0
بعد از Reload، این دستور مفید است:
asterisk -rx "http show status"
باید URI مربوط به /ws و HTTPS Listener را ببینید.
Certificate: Self-signed Certificate در WebRTC Production دردسر زیادی ایجاد میکند. Asterisk نیز برای Browser Client استفاده از Certificate معتبر عمومی را توصیه میکند؛ چون Browser ممکن است اتصال WSS را بدون Dialog واضح رد کند.
تنظیم Endpoint WebRTC در Asterisk
در Asterisk گزینه webrtc=yes چند تنظیم مهم را همزمان فعال میکند: rtcp_mux، use_avpf، ice_support و media_use_received_transport. همچنین Media Encryption روی DTLS قرار میگیرد و Fingerprint Verification فعال میشود.
نمونه ساده:
[web1001]
type=aor
max_contacts=3
remove_existing=yes
[web1001]
type=auth
auth_type=userpass
username=web1001
password=CHANGE_TO_A_LONG_RANDOM_PASSWORD
[web1001]
type=endpoint
aors=web1001
auth=web1001
webrtc=yes
context=from-webphone
disallow=all
allow=opus,ulaw
برای Production، Context این Endpoint را محدود طراحی کنید و Credential مستقل برای هر User داشته باشید. Password Trunk یا Credential مدیریتی را هرگز داخل Frontend قرار ندهید.
یک نمونه ساده SIP.js برای تماس صوتی
ابتدا نصب:
npm install sip.js
در HTML یک Audio Element برای صدای Remote قرار دهید:
<audio id="remoteAudio" playsinline></audio>
نمونه TypeScript/JavaScript:
import {{ Web }} from "sip.js";
const remoteAudio = document.getElementById("remoteAudio");
if (!(remoteAudio instanceof HTMLAudioElement)) {{
throw new Error("remoteAudio element not found");
}}
const server = "wss://pbx.example.com:8089/ws";
const phone = new Web.SimpleUser(server, {{
aor: "sip:web1001@example.com",
media: {{
constraints: {{
audio: true,
video: false
}},
remote: {{
audio: remoteAudio
}}
}},
reconnectionAttempts: 5,
reconnectionDelay: 4,
registererOptions: {{
expires: 600
}},
userAgentOptions: {{
authorizationUsername: "web1001",
authorizationPassword: "USER_SPECIFIC_SECRET",
displayName: "Web Phone 1001"
}}
}});
phone.delegate = {{
onServerConnect: () => {{
console.log("WSS connected");
}},
onServerDisconnect: (error) => {{
console.warn("WSS disconnected", error);
}},
onRegistered: () => {{
console.log("SIP registered");
}},
onCallReceived: () => {{
console.log("Incoming call");
}},
onCallHangup: () => {{
console.log("Call ended");
}}
}};
await phone.connect();
await phone.register();
برای تماس خروجی:
await phone.call("sip:1002@example.com");
برای پاسخ به تماس ورودی:
await phone.answer();
این مثال برای آموزش معماری است. در محصول واقعی State رابط کاربری، خطاها، Permission، Reconnect، Audio Playback و Call Lifecycle را جدا مدیریت کنید.
مشکل اول: Microphone Permission
WebRTC برای Microphone از navigator.mediaDevices.getUserMedia() استفاده میکند. طبق MDN این API فقط در Secure Context یعنی HTTPS یا localhost در دسترس است و User باید اجازه Microphone بدهد.
قبل از اولین تماس میتوانید Permission را به شکل کنترلشده درخواست کنید:
async function checkMicrophone() {{
try {{
const stream = await navigator.mediaDevices.getUserMedia({{
audio: true,
video: false
}});
stream.getTracks().forEach(track => track.stop());
return true;
}} catch (error) {{
console.error("Microphone permission failed", error);
return false;
}}
}}
اگر Web Phone داخل iframe است، ممکن است Permissions Policy هم لازم باشد:
<iframe
src="https://phone.example.com"
allow="microphone"
></iframe>
MDN توضیح میدهد اگر Microphone توسط Permissions Policy بسته شده باشد، getUserMedia() با NotAllowedError رد میشود.
مشکل دوم: تماس Answer شده ولی صدای Remote پخش نمیشود
حتی وقتی WebRTC MediaStream درست رسیده، Browser ممکن است پخش خودکار Audio را بهدلیل Autoplay Policy متوقف کند. فراخوانی play() میتواند با NotAllowedError رد شود.
بنابراین همیشه Promise را کنترل کنید:
async function ensureRemoteAudioCanPlay() {{
try {{
await remoteAudio.play();
}} catch (error) {{
console.warn("Autoplay blocked", error);
// Show a visible button:
// "فعالکردن صدای تماس"
}}
}}
بهترین UX این است که User قبل از استفاده از تلفن یک Interaction واضح مثل «فعالکردن تلفن» یا «شروع کار» داشته باشد. به Autoplay کورکورانه تکیه نکنید.
مشکل سوم: REGISTER موفق است ولی Media برقرار نمیشود
این مشکل معمولاً WSS نیست. SIP Signaling به PBX رسیده ولی ICE نتوانسته مسیر Media مناسبی بسازد.
مرورگر از RTCPeerConnection و ICE استفاده میکند. STUN به Client کمک میکند Candidate عمومی پیدا کند و TURN در شبکههایی که مسیر مستقیم ممکن نیست Media را Relay میکند.
ساختار مفهومی:
Browser
|
+--> host candidate
|
+--> STUN -> srflx candidate
|
+--> TURN -> relay candidate
|
v
Media Path
در WebRTC Production داشتن فقط STUN معمولاً کافی نیست. Corporate Firewall، CGNAT، Symmetric NAT و Mobile Network میتوانند Candidate مستقیم را شکست دهند؛ TURN باید بهعنوان Fallback وجود داشته باشد.
نمونه Configuration برای Peer Connection در سطح UserAgentOptions:
sessionDescriptionHandlerFactoryOptions: {{
peerConnectionConfiguration: {{
iceServers: [
{{
urls: "stun:stun.example.com:3478"
}},
{{
urls: "turn:turn.example.com:3478",
username: "short-lived-user",
credential: "short-lived-secret"
}},
{{
urls: "turns:turn.example.com:5349",
username: "short-lived-user",
credential: "short-lived-secret"
}}
],
iceTransportPolicy: "all"
}}
}}
Credentialهای TURN را ترجیحاً Short-lived بسازید و Secret ثابت عمومی داخل Bundle Frontend قرار ندهید.
مطلب مرتبط: اگر برای WebRTC به Relay نیاز دارید، مقاله Coturn ایرانیکاسرور را هم ببینید.
ICE State را در UI واقعی نمایش دهید
بهجای نمایش یک Status کلی «تماس برقرار است»، وضعیت ICE را هم مانیتور کنید:
peerConnection.addEventListener(
"iceconnectionstatechange",
() => {{
console.log(
"ICE state:",
peerConnection.iceConnectionState
);
}}
);
Stateهای مهم شامل checking، connected، completed، disconnected و failed هستند. اگر SIP Call Established است ولی ICE روی failed رفته، مشکل را در NAT/TURN/Firewall دنبال کنید نه در Dialplan.
Reconnect؛ قطع اینترنت را بخشی از طراحی بدانید
در لپتاپ و موبایل، تغییر Wi-Fi، Sleep/Wake، VPN و جابهجایی شبکه طبیعی است. یک Web Phone نباید با اولین Disconnect تا Refresh صفحه مرده بماند.
SIP.js برای SimpleUser و SessionManager گزینههای reconnectionAttempts و reconnectionDelay دارد. در API سطح پایینتر نیز مستند رسمی نمونهای برای userAgent.reconnect()، Re-register و Event آنلاین Browser ارائه میکند.
برای UI:
window.addEventListener("online", () => {{
console.log("Browser is online again");
}});
window.addEventListener("offline", () => {{
console.log("Network unavailable");
}});
اما فقط به Event online اعتماد نکنید؛ آنلاینبودن Browser به معنی سالمبودن WSS یا Registrar نیست.
OPTIONS Ping در SessionManager
در Release 0.21.2، SIP.js گزینه OPTIONS Ping را به SessionManager اضافه کرده است. در API فعلی گزینههای optionsPingInterval و optionsPingRequestURI وجود دارند و برای تشخیص Availability مسیر SIP مفیدند.
این Ping جای WebSocket State یا Application Health Monitoring کامل را نمیگیرد؛ فقط یک Signal دیگر برای تشخیص زندهبودن مسیر است.
Background Tab با «برنامه بسته» فرق دارد
یکی از بزرگترین اشتباهها در ساخت Web Phone این است که تصور کنیم اگر Tab در پسزمینه رفت، همیشه مثل یک Softphone Native زنده میماند.
MDN توضیح میدهد Browserها Timerهای Tabهای Background را Throttle میکنند؛ WebSocket و WebRTC در بسیاری از حالتها برای جلوگیری از Timeout از بخشی از این Throttling معافاند. اما این به معنی تضمین ۲۴ ساعته نیست. سیستمعامل، Mobile Browser، Memory Pressure، Sleep و بستهشدن Tab میتوانند Session را متوقف کنند.
نتیجه مهم: «Tab مخفی» ممکن است هنوز WebSocket فعال داشته باشد؛ ولی «Browser/App بسته» را نباید مثل SIP Phone همیشه Registered فرض کنید.
Push Notification برای تماس ورودی چه کمکی میکند؟
Push API میتواند حتی وقتی Web App در Foreground نیست، Service Worker را برای یک Push Event فعال کند و Notification نشان دهد. این قابلیت برای اعلام «تماس ورودی دارید» مفید است.
اما Service Worker یک Process دائمی نیست. طبق MDN، Service Worker میتواند وقتی Idle است Terminate شود و هنگام Event دوباره Start شود. بنابراین طراحی «یک SIP WebSocket دائمی داخل Service Worker» قابل اتکا نیست.
معماری منطقی برای PWA:
Incoming SIP call
|
v
Backend / PBX integration
|
v
Web Push Service
|
v
Service Worker wakes
|
v
Show incoming-call notification
|
v
User taps notification
|
v
Open / focus Web App
|
v
Reconnect WSS + Register
|
v
Continue app-specific call flow
این Workflow باید با Call Routing واقعی شما هماهنگ باشد. Push خودش SIP INVITE را به Browser تبدیل نمیکند و تضمین نمیکند Call Session بازمانده روی PBX تا زمان بازشدن App هنوز قابل Answer باشد.
چرا برای تماس ورودی Background باید Backend داشته باشیم؟
اگر Pure Browser Client بسته باشد، WSS آن هم وجود ندارد. در این وضعیت PBX باید بداند چطور Event تماس ورودی را به Backend شما بدهد تا Backend Push Notification بسازد، Timeout تماس را مدیریت کند یا Route جایگزین داشته باشد.
برای تجربهای واقعاً شبیه تماس Native روی موبایل ــ مخصوصاً وقتی App کامل بسته است ــ معمولاً Wrapper/Native App با Push Integration مخصوص Platform قابلاتکاتر از Pure Web App است.
Session Recovery؛ Refresh صفحه تماس جاری را نجات نمیدهد
اگر User وسط تماس صفحه را Refresh کند، JavaScript Context و RTCPeerConnection قبلی از بین میروند. SIP.js نمیتواند بهصورت عمومی یک Browser Session حذفشده را دوباره از همان نقطه ادامه دهد.
برای جلوگیری از UX بد:
🔹 هنگام تماس، Refresh/Navigation را در UI واضح کنید.
🔹 State تماس را در Backend هم نگه دارید.
🔹 در صورت Reload، سریع Reconnect/Register کنید.
🔹 برای Call Recovery از Flow اختصاصی PBX/Application استفاده کنید، نه انتظار از Browser State قدیمی.
امنیت Credential در SIP.js
اگر Browser مستقیماً REGISTER میکند، Credential همان User باید در Runtime Client موجود باشد. بنابراین:
🔹 Credential مدیریتی یا SIP Trunk را داخل Frontend قرار ندهید.
🔹 برای هر کاربر Account جدا داشته باشید.
🔹 دسترسی Dialplan همان Account را محدود کنید.
🔹 Password را داخل Repository یا Bundle هاردکد نکنید.
🔹 در صورت امکان Credential موقت یا Provisioning امن از Backend طراحی کنید.
🔹 WSS و HTTPS با Certificate معتبر اجباری باشند.
Obfuscate کردن JavaScript راه امنیتی نیست؛ User Browser به کدی که روی دستگاه خودش اجرا میشود دسترسی دارد.
Checklist قبل از Production
| بخش | وضعیت مطلوب |
|---|---|
| Frontend | HTTPS معتبر |
| SIP Transport | WSS با Certificate معتبر |
| PBX | WebRTC Endpoint با DTLS-SRTP و ICE |
| Microphone | Permission Flow واضح |
| Remote Audio | Autoplay Error Handling |
| ICE | STUN + TURN Production |
| Reconnect | WSS Reconnect + Re-register |
| Network Change | Online/offline + ICE State Monitoring |
| Background | عدم اتکا به Tab برای Always-On |
| Incoming Push | Backend + Service Worker + Notification |
| Credential | User-specific، محدود و غیرهاردکد |
| Monitoring | WSS، SIP Response، ICE و Call Metrics |
معماری پیشنهادی برای یک Web Phone سازمانی
+------------------+
| Web App Backend |
| auth / push / API|
+--------+---------+
|
Web Push / HTTPS
|
v
+---------+ WSS +------+ SIP +----------+
| Browser | ------------> | SBC | --------------> | Asterisk |
| SIP.js | +------+ +----------+
+----+----+ |
| |
| WebRTC Media |
| ICE / DTLS-SRTP |
v v
STUN/TURN <----------- Media Path
|
v
coturn
این معماری بهتر از اتصال مستقیم همه Browserها به یک PBX خام است؛ چون لایه Backend برای Auth/Push و SBC برای Edge Control را جدا میکند. در پروژه کوچک میتوانید بعضی اجزا را سادهتر کنید، اما مسئولیت هر بخش باید روشن باشد.
میخواهید تلفن تحت وب SIP.js را روی Asterisk راهاندازی کنید؟
برای یک استقرار Production باید PBX، WSS، Certificate، STUN/TURN، Route تماس، Security و رفتار Background مشخص باشند. بعد میتوان Frontend و زیرساخت را بدون وابستگی به تنظیمهای موقت Browser پیادهسازی کرد.
ایرانیکاسرور میتواند برای تهیه VPS، راهاندازی Asterisk، WSS، TURN و معماری WebRTC کمک کند.
تماس با پشتیبانی ایرانیکاسرور: 021-91302467
پرسشهای رایج
SIP.js چیست؟
SIP.js یک کتابخانه JavaScript/TypeScript برای ساخت SIP User Agent در Web است. Signaling را از طریق WebSocket و Media را با WebRTC مدیریت میکند.
برای SIP.js حتماً WSS لازم است؟
در Production روی صفحه HTTPS عملاً باید WSS داشته باشید. Browserها Mixed Content را محدود میکنند و Certificate نامعتبر نیز میتواند اتصال را خراب کند.
چرا Microphone در SIP.js باز نمیشود؟
getUserMedia فقط در Secure Context کار میکند و User باید Permission بدهد. Permissions Policy یا iframe بدون اجازه Microphone نیز میتواند NotAllowedError ایجاد کند.
برای SIP.js فقط STUN کافی است؟
نه همیشه. در NAT و Firewallهای سخت، TURN باید بهعنوان Relay Fallback وجود داشته باشد. WebRTC از ICE برای انتخاب مسیر مناسب استفاده میکند.
SIP.js بعد از قطع اینترنت خودکار Reconnect میشود؟
SimpleUser و SessionManager گزینههای Reconnection دارند، اما Application باید State، Re-registration و تغییر شبکه را هم مدیریت و مانیتور کند.
آیا Web Phone وقتی Browser بسته است تماس ورودی میگیرد؟
نباید روی WebSocket همیشهفعال در Browser بسته تکیه کنید. Web Push میتواند Service Worker را برای Notification بیدار کند، اما Service Worker Process دائمی SIP نیست و Call Flow باید با Backend/PBX هماهنگ شود.
چرا تماس وصل است ولی صدای Remote پخش نمیشود؟
ممکن است Browser Autoplay را Block کرده باشد. Promise مربوط به HTMLMediaElement.play() را کنترل کنید و در صورت NotAllowedError یک دکمه تعامل مستقیم برای فعالکردن صدا نشان دهید.
SimpleUser یا SessionManager کدام بهتر است؟
برای یک تماس ساده و یک Session همزمان SimpleUser مناسب است. برای چند تماس همزمان و مدیریت پیشرفته Sessionها، SessionManager گزینه مناسبتری است.
جمعبندی
برای ساخت تلفن نرمافزاری تحت وب با SIP.js ابتدا Signaling و Media را جدا ببینید: SIP روی WSS و Media روی WebRTC با ICE و DTLS-SRTP حرکت میکند.
بیشترین خطاهای Production از خود SIP.js نیستند؛ Certificate WSS، Permission Microphone، Autoplay، NAT/TURN و Reconnect معمولاً نقاط شکست اصلیاند.
اگر Web Phone باید تماس ورودی را حتی در Background یا بعد از بستهشدن App اعلام کند، Pure WebSocket کافی نیست. Push Notification و Backend باید در معماری حضور داشته باشند و محدودیت چرخه عمر Service Worker را در نظر بگیرید.
برای محصول ساده با یک تماس، SimpleUser شروع خوبی است؛ برای Call Center یا چند Session همزمان، SessionManager یا Full API کنترل لازم را میدهد.






دیدگاهها (۰)