基于 LumiSoft.Net 库实现 SIP 客户端,支持注册、呼叫、接听等完整功能。
一、环境准备
1. 安装 LumiSoft.Net 库
<!-- 通过 NuGet 安装 -->
<PackageReference Include="LumiSoft.Net" Version="4.5.5702.26503" />
2. 项目结构
SipClient/
├── SipClient.sln
├── SipClient/
│ ├── App.config
│ ├── Program.cs
│ ├── SipClient.cs
│ ├── SipAccount.cs
│ ├── SipCall.cs
│ ├── SipEventHandler.cs
│ ├── MainForm.cs
│ ├── MainForm.Designer.cs
│ └── Properties/
│ └── AssemblyInfo.cs
└── packages.config
二、核心代码实现
1. SIP 账户配置类 (SipAccount.cs)
using System;namespace SipClient
{/// <summary>/// SIP 账户配置信息/// </summary>public class SipAccount{public string DisplayName { get; set; }public string UserName { get; set; }public string Password { get; set; }public string Domain { get; set; }public string RegistrarServer { get; set; }public string ProxyServer { get; set; }public int RegistrarPort { get; set; } = 5060;public int LocalPort { get; set; } = 5060;public int RegisterInterval { get; set; } = 300; // 注册间隔(秒)public bool UseProxy { get; set; }public bool AutoRegister { get; set; } = true;/// <summary>/// 获取完整的 SIP URI/// </summary>public string GetSipUri(){return $"sip:{UserName}@{Domain}";}/// <summary>/// 获取注册服务器地址/// </summary>public string GetRegistrarAddress(){return $"{RegistrarServer}:{RegistrarPort}";}}
}
2. SIP 呼叫管理类 (SipCall.cs)
using System;
using LumiSoft.Net.SIP.Message;
using LumiSoft.Net.SIP.Stack;namespace SipClient
{/// <summary>/// SIP 呼叫会话管理/// </summary>public class SipCall{private SIP_ClientTransaction m_pInviteTransaction;private SIP_Dialog m_pDialog;private DateTime m_StartTime;public string CallId { get; private set; }public string From { get; private set; }public string To { get; private set; }public SIP_CallState State { get; private set; }public TimeSpan Duration => DateTime.Now - m_StartTime;public event EventHandler<CallStateChangedEventArgs> CallStateChanged;public SipCall(string from, string to){From = from;To = to;CallId = Guid.NewGuid().ToString();State = SIP_CallState.Waiting;}public void StartCall(SIP_Stack stack){try{// 创建 INVITE 请求SIP_Request inviteRequest = stack.CreateRequest(SIP_Methods.INVITE, To, From);inviteRequest.CallID = CallId;// 添加 SDP 信息(媒体描述)string sdp = CreateSdp();inviteRequest.Data = System.Text.Encoding.UTF8.GetBytes(sdp);// 创建客户端事务m_pInviteTransaction = stack.CreateClientTransaction(inviteRequest);m_pInviteTransaction.ResponseReceived += OnInviteResponse;m_pInviteTransaction.Start();State = SIP_CallState.Proceeding;m_StartTime = DateTime.Now;OnCallStateChanged("呼叫发起中...");}catch (Exception ex){State = SIP_CallState.Terminated;OnCallStateChanged($"呼叫失败: {ex.Message}");}}public void AcceptCall(SIP_Stack stack, SIP_Request inviteRequest){try{// 创建 200 OK 响应SIP_Response okResponse = stack.CreateResponse(inviteRequest, 200, "OK");// 添加 SDP 信息string sdp = CreateSdp();okResponse.Data = System.Text.Encoding.UTF8.GetBytes(sdp);// 发送响应stack.SendResponse(okResponse);// 创建对话m_pDialog = stack.CreateDialog(inviteRequest, okResponse);State = SIP_CallState.Confirmed;OnCallStateChanged("通话已接通");}catch (Exception ex){State = SIP_CallState.Terminated;OnCallStateChanged($"接听失败: {ex.Message}");}}public void EndCall(SIP_Stack stack){try{if (m_pDialog != null){// 发送 BYE 请求SIP_Request byeRequest = m_pDialog.CreateRequest(SIP_Methods.BYE);SIP_ClientTransaction byeTransaction = stack.CreateClientTransaction(byeRequest);byeTransaction.Start();}State = SIP_CallState.Terminated;OnCallStateChanged("通话已结束");}catch (Exception ex){OnCallStateChanged($"结束通话失败: {ex.Message}");}}private string CreateSdp(){// 创建简单的 SDP 描述return "v=0\r\n" +$"o={UserName} 123456 789 IN IP4 127.0.0.1\r\n" +"s=Session\r\n" +"c=IN IP4 127.0.0.1\r\n" +"t=0 0\r\n" +"m=audio 8000 RTP/AVP 0 8 101\r\n" +"a=rtpmap:0 PCMU/8000\r\n" +"a=rtpmap:8 PCMA/8000\r\n" +"a=rtpmap:101 telephone-event/8000\r\n" +"a=sendrecv\r\n";}private void OnInviteResponse(object sender, SIP_ResponseReceivedEventArgs e){if (e.Response.StatusCode == 180 || e.Response.StatusCode == 183){State = SIP_CallState.Proceeding;OnCallStateChanged("对方振铃中...");}else if (e.Response.StatusCode >= 200 && e.Response.StatusCode < 300){State = SIP_CallState.Confirmed;OnCallStateChanged("通话已接通");}else if (e.Response.StatusCode >= 300){State = SIP_CallState.Terminated;OnCallStateChanged($"呼叫失败: {e.Response.StatusCode} {e.Response.ReasonPhrase}");}}private void OnCallStateChanged(string message){CallStateChanged?.Invoke(this, new CallStateChangedEventArgs{CallId = CallId,State = State,Message = message,Duration = Duration});}}public class CallStateChangedEventArgs : EventArgs{public string CallId { get; set; }public SIP_CallState State { get; set; }public string Message { get; set; }public TimeSpan Duration { get; set; }}
}
3. SIP 事件处理器 (SipEventHandler.cs)
using System;
using LumiSoft.Net.SIP.Message;
using LumiSoft.Net.SIP.Stack;namespace SipClient
{/// <summary>/// SIP 事件处理器/// </summary>public class SipEventHandler{private SIP_Stack m_pStack;public event EventHandler<RegistrationEventArgs> RegistrationStatusChanged;public event EventHandler<IncomingCallEventArgs> IncomingCallReceived;public event EventHandler<MessageEventArgs> SipMessageReceived;public SipEventHandler(SIP_Stack stack){m_pStack = stack;RegisterEventHandlers();}private void RegisterEventHandlers(){// 注册请求接收事件m_pStack.RequestReceived += OnRequestReceived;// 注册响应接收事件m_pStack.ResponseReceived += OnResponseReceived;// 注册传输错误事件m_pStack.TransportError += OnTransportError;}private void OnRequestReceived(object sender, SIP_RequestReceivedEventArgs e){try{SIP_Request request = e.Request;// 记录接收到的请求OnMessageReceived($"收到请求: {request.RequestLine.Method} {request.RequestLine.Uri}");// 处理不同类型的请求switch (request.RequestLine.Method){case SIP_Methods.INVITE:HandleIncomingCall(request);break;case SIP_Methods.BYE:HandleByeRequest(request);break;case SIP_Methods.CANCEL:HandleCancelRequest(request);break;case SIP_Methods.OPTIONS:HandleOptionsRequest(request);break;case SIP_Methods.REGISTER:HandleRegisterRequest(request);break;}}catch (Exception ex){OnMessageReceived($"处理请求时出错: {ex.Message}");}}private void OnResponseReceived(object sender, SIP_ResponseReceivedEventArgs e){try{SIP_Response response = e.Response;// 记录接收到的响应OnMessageReceived($"收到响应: {response.StatusCode} {response.ReasonPhrase}");// 处理注册响应if (response.CSeq.Method == SIP_Methods.REGISTER){HandleRegistrationResponse(response);}// 处理呼叫响应else if (response.CSeq.Method == SIP_Methods.INVITE){HandleInviteResponse(response);}}catch (Exception ex){OnMessageReceived($"处理响应时出错: {ex.Message}");}}private void HandleIncomingCall(SIP_Request request){try{string from = request.From.Address.UriValue;string to = request.To.Address.UriValue;string callId = request.CallID;OnMessageReceived($"收到来电: {from} -> {to}");// 触发来电事件IncomingCallReceived?.Invoke(this, new IncomingCallEventArgs{Request = request,From = from,To = to,CallId = callId,ReceivedTime = DateTime.Now});}catch (Exception ex){OnMessageReceived($"处理来电时出错: {ex.Message}");}}private void HandleRegistrationResponse(SIP_Response response){string status = response.StatusCode >= 200 && response.StatusCode < 300 ? "注册成功" : $"注册失败: {response.StatusCode} {response.ReasonPhrase}";OnMessageReceived($"注册状态: {status}");RegistrationStatusChanged?.Invoke(this, new RegistrationEventArgs{StatusCode = response.StatusCode,ReasonPhrase = response.ReasonPhrase,IsSuccess = response.StatusCode >= 200 && response.StatusCode < 300,Timestamp = DateTime.Now});}private void HandleInviteResponse(SIP_Response response){// 这里可以处理呼叫响应的特定逻辑OnMessageReceived($"呼叫响应: {response.StatusCode} {response.ReasonPhrase}");}private void HandleByeRequest(SIP_Request request){OnMessageReceived("收到 BYE 请求,通话结束");// 发送 200 OK 响应SIP_Response okResponse = m_pStack.CreateResponse(request, 200, "OK");m_pStack.SendResponse(okResponse);}private void HandleCancelRequest(SIP_Request request){OnMessageReceived("收到 CANCEL 请求,取消呼叫");// 发送 200 OK 响应SIP_Response okResponse = m_pStack.CreateResponse(request, 200, "OK");m_pStack.SendResponse(okResponse);}private void HandleOptionsRequest(SIP_Request request){OnMessageReceived("收到 OPTIONS 请求");// 发送 200 OK 响应SIP_Response okResponse = m_pStack.CreateResponse(request, 200, "OK");m_pStack.SendResponse(okResponse);}private void HandleRegisterRequest(SIP_Request request){OnMessageReceived("收到 REGISTER 请求");// 发送 200 OK 响应SIP_Response okResponse = m_pStack.CreateResponse(request, 200, "OK");m_pStack.SendResponse(okResponse);}private void OnTransportError(object sender, SIP_TransportErrorEventArgs e){OnMessageReceived($"传输错误: {e.ErrorText}");}private void OnMessageReceived(string message){SipMessageReceived?.Invoke(this, new MessageEventArgs{Message = message,Timestamp = DateTime.Now});}}public class RegistrationEventArgs : EventArgs{public int StatusCode { get; set; }public string ReasonPhrase { get; set; }public bool IsSuccess { get; set; }public DateTime Timestamp { get; set; }}public class IncomingCallEventArgs : EventArgs{public SIP_Request Request { get; set; }public string From { get; set; }public string To { get; set; }public string CallId { get; set; }public DateTime ReceivedTime { get; set; }}public class MessageEventArgs : EventArgs{public string Message { get; set; }public DateTime Timestamp { get; set; }}
}
4. 主 SIP 客户端类 (SipClient.cs)
using System;
using System.Net;
using System.Threading;
using LumiSoft.Net;
using LumiSoft.Net.SIP.Stack;namespace SipClient
{/// <summary>/// SIP 客户端主类/// </summary>public class SipClient : IDisposable{private SIP_Stack m_pStack;private SipAccount m_Account;private SipEventHandler m_EventHandler;private Timer m_RegisterTimer;private bool m_IsRegistered;public event EventHandler<string> StatusChanged;public event EventHandler<RegistrationEventArgs> RegistrationStatusChanged;public event EventHandler<IncomingCallEventArgs> IncomingCallReceived;public event EventHandler<MessageEventArgs> MessageReceived;public bool IsInitialized => m_pStack != null;public bool IsRegistered => m_IsRegistered;public SipAccount Account => m_Account;/// <summary>/// 初始化 SIP 客户端/// </summary>public void Initialize(SipAccount account){try{m_Account = account;// 创建 SIP 栈实例m_pStack = new SIP_Stack();m_pStack.UserAgent = "LumiSoft SIP Client 1.0";// 配置绑定信息IPBindInfo[] bindInfo = new IPBindInfo[]{new IPBindInfo("", BindInfoProtocol.UDP, IPAddress.Any, account.LocalPort)};m_pStack.BindInfo = bindInfo;// 初始化事件处理器m_EventHandler = new SipEventHandler(m_pStack);m_EventHandler.RegistrationStatusChanged += OnRegistrationStatusChanged;m_EventHandler.IncomingCallReceived += OnIncomingCallReceived;m_EventHandler.SipMessageReceived += OnMessageReceived;// 启动 SIP 栈m_pStack.Start();OnStatusChanged("SIP 客户端初始化成功");}catch (Exception ex){OnStatusChanged($"SIP 客户端初始化失败: {ex.Message}");throw;}}/// <summary>/// 注册到 SIP 服务器/// </summary>public void Register(){if (m_pStack == null || m_Account == null){OnStatusChanged("SIP 客户端未初始化");return;}try{// 创建 REGISTER 请求string toUri = m_Account.GetSipUri();string fromUri = m_Account.GetSipUri();string contactUri = $"sip:{m_Account.UserName}@{GetLocalIP()}:{m_Account.LocalPort}";SIP_Request registerRequest = m_pStack.CreateRequest(SIP_Methods.REGISTER, toUri, fromUri);// 设置 Contact 头registerRequest.Contact.Add(new SIP_t_ContactParam(contactUri));// 设置 Expires 头registerRequest.Expires = m_Account.RegisterInterval;// 创建客户端事务并发送SIP_ClientTransaction transaction = m_pStack.CreateClientTransaction(registerRequest);transaction.ResponseReceived += OnRegisterResponse;transaction.Start();OnStatusChanged("正在注册到 SIP 服务器...");}catch (Exception ex){OnStatusChanged($"注册失败: {ex.Message}");}}/// <summary>/// 发起呼叫/// </summary>public SipCall MakeCall(string targetUri){if (!m_IsRegistered){OnStatusChanged("请先注册到 SIP 服务器");return null;}try{SipCall call = new SipCall(m_Account.GetSipUri(), targetUri);call.CallStateChanged += OnCallStateChanged;call.StartCall(m_pStack);OnStatusChanged($"正在呼叫: {targetUri}");return call;}catch (Exception ex){OnStatusChanged($"发起呼叫失败: {ex.Message}");return null;}}/// <summary>/// 接听来电/// </summary>public void AnswerCall(SIP_Request inviteRequest){try{// 创建 SipCall 实例并接听SipCall call = new SipCall(inviteRequest.From.Address.UriValue,inviteRequest.To.Address.UriValue);call.CallId = inviteRequest.CallID;call.CallStateChanged += OnCallStateChanged;call.AcceptCall(m_pStack, inviteRequest);OnStatusChanged("已接听来电");}catch (Exception ex){OnStatusChanged($"接听来电失败: {ex.Message}");}}/// <summary>/// 拒绝来电/// </summary>public void RejectCall(SIP_Request inviteRequest, int statusCode = 486){try{SIP_Response response = m_pStack.CreateResponse(inviteRequest, statusCode, "Busy Here");m_pStack.SendResponse(response);OnStatusChanged("已拒绝来电");}catch (Exception ex){OnStatusChanged($"拒绝来电失败: {ex.Message}");}}/// <summary>/// 开始自动注册(定时刷新注册)/// </summary>public void StartAutoRegister(){if (m_Account == null || !m_Account.AutoRegister) return;// 设置定时器,在注册过期前重新注册int interval = (m_Account.RegisterInterval - 60) * 1000; // 提前60秒重新注册if (interval < 10000) interval = 10000; // 最小10秒m_RegisterTimer = new Timer(OnRegisterTimerCallback, null, interval, interval);OnStatusChanged("已启用自动注册");}/// <summary>/// 停止自动注册/// </summary>public void StopAutoRegister(){m_RegisterTimer?.Dispose();m_RegisterTimer = null;OnStatusChanged("已停止自动注册");}/// <summary>/// 注销/// </summary>public void Unregister(){if (m_pStack == null || m_Account == null) return;try{// 发送 Expires=0 的 REGISTER 请求来注销string toUri = m_Account.GetSipUri();string fromUri = m_Account.GetSipUri();SIP_Request registerRequest = m_pStack.CreateRequest(SIP_Methods.REGISTER, toUri, fromUri);registerRequest.Contact.Add(new SIP_t_ContactParam($"sip:{m_Account.UserName}@{GetLocalIP()}:{m_Account.LocalPort}"));registerRequest.Expires = 0; // 立即过期SIP_ClientTransaction transaction = m_pStack.CreateClientTransaction(registerRequest);transaction.Start();m_IsRegistered = false;StopAutoRegister();OnStatusChanged("已注销");}catch (Exception ex){OnStatusChanged($"注销失败: {ex.Message}");}}private void OnRegisterResponse(object sender, SIP_ResponseReceivedEventArgs e){if (e.Response.StatusCode >= 200 && e.Response.StatusCode < 300){m_IsRegistered = true;OnStatusChanged("注册成功");// 开始自动注册StartAutoRegister();}else{m_IsRegistered = false;OnStatusChanged($"注册失败: {e.Response.StatusCode} {e.Response.ReasonPhrase}");}}private void OnRegisterTimerCallback(object state){Register();}private void OnRegistrationStatusChanged(object sender, RegistrationEventArgs e){RegistrationStatusChanged?.Invoke(this, e);}private void OnIncomingCallReceived(object sender, IncomingCallEventArgs e){IncomingCallReceived?.Invoke(this, e);}private void OnMessageReceived(object sender, MessageEventArgs e){MessageReceived?.Invoke(this, e);}private void OnCallStateChanged(object sender, CallStateChangedEventArgs e){OnStatusChanged($"呼叫状态: {e.Message}");}private void OnStatusChanged(string message){StatusChanged?.Invoke(this, message);}private string GetLocalIP(){try{var host = Dns.GetHostEntry(Dns.GetHostName());foreach (var ip in host.AddressList){if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork){return ip.ToString();}}return "127.0.0.1";}catch{return "127.0.0.1";}}public void Dispose(){try{Unregister();StopAutoRegister();m_pStack?.Stop();m_pStack?.Dispose();m_pStack = null;m_RegisterTimer?.Dispose();m_RegisterTimer = null;OnStatusChanged("SIP 客户端已关闭");}catch (Exception ex){OnStatusChanged($"关闭时出错: {ex.Message}");}}}
}
5. 主窗体界面 (MainForm.cs)
using System;
using System.Drawing;
using System.Windows.Forms;namespace SipClient
{public partial class MainForm : Form{private SipClient m_SipClient;private SipAccount m_CurrentAccount;private SipCall m_CurrentCall;public MainForm(){InitializeComponent();InitializeUI();}private void InitializeUI(){// 窗体设置this.Text = "SIP 客户端";this.Size = new Size(800, 600);this.StartPosition = FormStartPosition.CenterScreen;this.BackColor = Color.FromArgb(240, 240, 240);// 创建控件InitializeControls();LoadDefaultSettings();}private void InitializeControls(){// 账户配置区域GroupBox gbAccount = new GroupBox{Text = "账户配置",Location = new Point(20, 20),Size = new Size(740, 150),Font = new Font("微软雅黑", 10)};// 用户名Label lblUser = new Label{Text = "用户名:",Location = new Point(20, 30),Size = new Size(80, 25),Font = new Font("微软雅黑", 9)};TextBox txtUser = new TextBox{Name = "txtUser",Location = new Point(100, 30),Size = new Size(150, 25),Font = new Font("微软雅黑", 9)};// 密码Label lblPassword = new Label{Text = "密码:",Location = new Point(270, 30),Size = new Size(80, 25),Font = new Font("微软雅黑", 9)};TextBox txtPassword = new TextBox{Name = "txtPassword",Location = new Point(350, 30),Size = new Size(150, 25),Font = new Font("微软雅黑", 9),PasswordChar = '*'};// 域名Label lblDomain = new Label{Text = "域名:",Location = new Point(20, 70),Size = new Size(80, 25),Font = new Font("微软雅黑", 9)};TextBox txtDomain = new TextBox{Name = "txtDomain",Location = new Point(100, 70),Size = new Size(150, 25),Font = new Font("微软雅黑", 9)};// 注册服务器Label lblRegistrar = new Label{Text = "注册服务器:",Location = new Point(270, 70),Size = new Size(80, 25),Font = new Font("微软雅黑", 9)};TextBox txtRegistrar = new TextBox{Name = "txtRegistrar",Location = new Point(350, 70),Size = new Size(150, 25),Font = new Font("微软雅黑", 9)};// 端口Label lblPort = new Label{Text = "端口:",Location = new Point(520, 70),Size = new Size(50, 25),Font = new Font("微软雅黑", 9)};TextBox txtPort = new TextBox{Name = "txtPort",Location = new Point(570, 70),Size = new Size(80, 25),Font = new Font("微软雅黑", 9),Text = "5060"};// 初始化按钮Button btnInit = new Button{Text = "初始化",Location = new Point(20, 110),Size = new Size(100, 30),BackColor = Color.SteelBlue,ForeColor = Color.White,FlatStyle = FlatStyle.Flat,Font = new Font("微软雅黑", 9)};btnInit.FlatAppearance.BorderSize = 0;btnInit.Click += BtnInit_Click;// 注册按钮Button btnRegister = new Button{Text = "注册",Location = new Point(130, 110),Size = new Size(100, 30),BackColor = Color.ForestGreen,ForeColor = Color.White,FlatStyle = FlatStyle.Flat,Font = new Font("微软雅黑", 9),Enabled = false};btnRegister.FlatAppearance.BorderSize = 0;btnRegister.Click += BtnRegister_Click;// 注销按钮Button btnUnregister = new Button{Text = "注销",Location = new Point(240, 110),Size = new Size(100, 30),BackColor = Color.Orange,ForeColor = Color.White,FlatStyle = FlatStyle.Flat,Font = new Font("微软雅黑", 9),Enabled = false};btnUnregister.FlatAppearance.BorderSize = 0;btnUnregister.Click += BtnUnregister_Click;// 将控件添加到账户分组框gbAccount.Controls.AddRange(new Control[] { lblUser, txtUser, lblPassword, txtPassword,lblDomain, txtDomain, lblRegistrar, txtRegistrar,lblPort, txtPort, btnInit, btnRegister, btnUnregister});// 呼叫控制区域GroupBox gbCall = new GroupBox{Text = "呼叫控制",Location = new Point(20, 180),Size = new Size(740, 120),Font = new Font("微软雅黑", 10)};// 目标号码Label lblTarget = new Label{Text = "目标号码:",Location = new Point(20, 30),Size = new Size(80, 25),Font = new Font("微软雅黑", 9)};TextBox txtTarget = new TextBox{Name = "txtTarget",Location = new Point(100, 30),Size = new Size(200, 25),Font = new Font("微软雅黑", 9)};// 呼叫按钮Button btnCall = new Button{Text = "呼叫",Location = new Point(320, 30),Size = new Size(100, 30),BackColor = Color.DodgerBlue,ForeColor = Color.White,FlatStyle = FlatStyle.Flat,Font = new Font("微软雅黑", 9),Enabled = false};btnCall.FlatAppearance.BorderSize = 0;btnCall.Click += BtnCall_Click;// 挂断按钮Button btnHangup = new Button{Text = "挂断",Location = new Point(430, 30),Size = new Size(100, 30),BackColor = Color.Crimson,ForeColor = Color.White,FlatStyle = FlatStyle.Flat,Font = new Font("微软雅黑", 9),Enabled = false};btnHangup.FlatAppearance.BorderSize = 0;btnHangup.Click += BtnHangup_Click;// 接听按钮Button btnAnswer = new Button{Text = "接听",Location = new Point(20, 70),Size = new Size(100, 30),BackColor = Color.LimeGreen,ForeColor = Color.White,FlatStyle = FlatStyle.Flat,Font = new Font("微软雅黑", 9),Enabled = false};btnAnswer.FlatAppearance.BorderSize = 0;btnAnswer.Click += BtnAnswer_Click;// 拒绝按钮Button btnReject = new Button{Text = "拒绝",Location = new Point(130, 70),Size = new Size(100, 30),BackColor = Color.OrangeRed,ForeColor = Color.White,FlatStyle = FlatStyle.Flat,Font = new Font("微软雅黑", 9),Enabled = false};btnReject.FlatAppearance.BorderSize = 0;btnReject.Click += BtnReject_Click;// 呼叫状态标签Label lblCallStatus = new Label{Name = "lblCallStatus",Text = "呼叫状态: 空闲",Location = new Point(250, 75),Size = new Size(300, 25),Font = new Font("微软雅黑", 9),ForeColor = Color.DimGray};// 将控件添加到呼叫分组框gbCall.Controls.AddRange(new Control[] { lblTarget, txtTarget, btnCall, btnHangup,btnAnswer, btnReject, lblCallStatus});// 日志区域GroupBox gbLog = new GroupBox{Text = "日志",Location = new Point(20, 310),Size = new Size(740, 240),Font = new Font("微软雅黑", 10)};TextBox txtLog = new TextBox{Name = "txtLog",Location = new Point(10, 25),Size = new Size(720, 205),Multiline = true,ScrollBars = ScrollBars.Vertical,ReadOnly = true,Font = new Font("Consolas", 9),BackColor = Color.Black,ForeColor = Color.Lime};gbLog.Controls.Add(txtLog);// 状态栏StatusStrip statusStrip = new StatusStrip();ToolStripStatusLabel lblStatus = new ToolStripStatusLabel{Text = "就绪",Spring = true};statusStrip.Items.Add(lblStatus);statusStrip.Dock = DockStyle.Bottom;// 将控件添加到窗体this.Controls.AddRange(new Control[] { gbAccount, gbCall, gbLog, statusStrip });// 保存控件引用this.Controls.Add(new Control[] { txtUser, txtPassword, txtDomain, txtRegistrar, txtPort,txtTarget, txtLog, btnRegister, btnUnregister, btnCall,btnHangup, btnAnswer, btnReject, lblCallStatus});}private void LoadDefaultSettings(){// 设置默认值GetControl<TextBox>("txtUser").Text = "1001";GetControl<TextBox>("txtPassword").Text = "123456";GetControl<TextBox>("txtDomain").Text = "sip.example.com";GetControl<TextBox>("txtRegistrar").Text = "192.168.1.100";GetControl<TextBox>("txtPort").Text = "5060";GetControl<TextBox>("txtTarget").Text = "1002@sip.example.com";}private T GetControl<T>(string name) where T : Control{foreach (Control control in this.Controls){if (control.Name == name && control is T)return (T)control;}return null;}#region 事件处理private void BtnInit_Click(object sender, EventArgs e){try{// 创建账户配置m_CurrentAccount = new SipAccount{UserName = GetControl<TextBox>("txtUser").Text,Password = GetControl<TextBox>("txtPassword").Text,Domain = GetControl<TextBox>("txtDomain").Text,RegistrarServer = GetControl<TextBox>("txtRegistrar").Text,RegistrarPort = int.Parse(GetControl<TextBox>("txtPort").Text),LocalPort = 5060,RegisterInterval = 300,AutoRegister = true};// 初始化 SIP 客户端m_SipClient = new SipClient();m_SipClient.StatusChanged += OnClientStatusChanged;m_SipClient.RegistrationStatusChanged += OnRegistrationStatusChanged;m_SipClient.IncomingCallReceived += OnIncomingCallReceived;m_SipClient.MessageReceived += OnMessageReceived;m_SipClient.Initialize(m_CurrentAccount);// 更新按钮状态GetControl<Button>("btnRegister").Enabled = true;GetControl<Button>("btnInit").Enabled = false;LogMessage("SIP 客户端初始化成功");}catch (Exception ex){LogMessage($"初始化失败: {ex.Message}");}}private void BtnRegister_Click(object sender, EventArgs e){if (m_SipClient == null) return;m_SipClient.Register();GetControl<Button>("btnUnregister").Enabled = true;GetControl<Button>("btnRegister").Enabled = false;}private void BtnUnregister_Click(object sender, EventArgs e){if (m_SipClient == null) return;m_SipClient.Unregister();GetControl<Button>("btnUnregister").Enabled = false;GetControl<Button>("btnRegister").Enabled = true;GetControl<Button>("btnCall").Enabled = false;}private void BtnCall_Click(object sender, EventArgs e){string target = GetControl<TextBox>("txtTarget").Text;if (string.IsNullOrEmpty(target)){LogMessage("请输入目标号码");return;}m_CurrentCall = m_SipClient.MakeCall(target);if (m_CurrentCall != null){GetControl<Button>("btnCall").Enabled = false;GetControl<Button>("btnHangup").Enabled = true;GetControl<Label>("lblCallStatus").Text = "呼叫中...";}}private void BtnHangup_Click(object sender, EventArgs e){if (m_CurrentCall != null){m_CurrentCall.EndCall(m_SipClient?.GetStack());m_CurrentCall = null;GetControl<Button>("btnCall").Enabled = true;GetControl<Button>("btnHangup").Enabled = false;GetControl<Label>("lblCallStatus").Text = "呼叫状态: 空闲";}}private void BtnAnswer_Click(object sender, EventArgs e){// 接听来电的逻辑在 OnIncomingCallReceived 中处理}private void BtnReject_Click(object sender, EventArgs e){// 拒绝来电的逻辑在 OnIncomingCallReceived 中处理}private void OnClientStatusChanged(object sender, string message){this.Invoke(new Action(() =>{LogMessage($"[状态] {message}");if (message.Contains("注册成功")){GetControl<Button>("btnCall").Enabled = true;}}));}private void OnRegistrationStatusChanged(object sender, RegistrationEventArgs e){this.Invoke(new Action(() =>{string status = e.IsSuccess ? "注册成功" : $"注册失败: {e.StatusCode} {e.ReasonPhrase}";LogMessage($"[注册] {status}");}));}private void OnIncomingCallReceived(object sender, IncomingCallEventArgs e){this.Invoke(new Action(() =>{LogMessage($"[来电] 来自: {e.From}");// 显示来电提示对话框var result = MessageBox.Show($"收到来自 {e.From} 的来电\n是否接听?","来电提示",MessageBoxButtons.YesNo,MessageBoxIcon.Question);if (result == DialogResult.Yes){m_SipClient?.AnswerCall(e.Request);GetControl<Label>("lblCallStatus").Text = "通话中...";}else{m_SipClient?.RejectCall(e.Request);}}));}private void OnMessageReceived(object sender, MessageEventArgs e){this.Invoke(new Action(() =>{LogMessage($"[消息] {e.Message}");}));}private void LogMessage(string message){TextBox txtLog = GetControl<TextBox>("txtLog");if (txtLog != null){txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\r\n");txtLog.ScrollToCaret();}}#endregionprotected override void OnFormClosing(FormClosingEventArgs e){m_SipClient?.Dispose();base.OnFormClosing(e);}}
}
6. 程序入口 (Program.cs)
using System;
using System.Windows.Forms;namespace SipClient
{static class Program{[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new MainForm());}}
}
参考代码 C#基于LumiSoft 实现SIP客户端 www.youwenfan.com/contentcnt/45086.html
三、配置文件 (App.config)
<?xml version="1.0" encoding="utf-8"?>
<configuration><startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/></startup><appSettings><!-- SIP 服务器配置 --><add key="SipServer" value="192.168.1.100"/><add key="SipPort" value="5060"/><add key="SipDomain" value="sip.example.com"/><!-- 客户端配置 --><add key="LocalPort" value="5060"/><add key="RegisterInterval" value="300"/><add key="AutoRegister" value="true"/><!-- 默认账户 --><add key="DefaultUser" value="1001"/><add key="DefaultPassword" value="123456"/></appSettings><runtime><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="LumiSoft.Net" publicKeyToken="null" culture="neutral"/><bindingRedirect oldVersion="0.0.0.0-4.5.5702.26503" newVersion="4.5.5702.26503"/></dependentAssembly></assemblyBinding></runtime>
</configuration>
四、使用说明
1. 基本功能
- 初始化: 配置 SIP 账户信息后点击"初始化"按钮
- 注册: 初始化成功后点击"注册"按钮连接到 SIP 服务器
- 呼叫: 输入目标 SIP URI 后点击"呼叫"按钮
- 接听: 收到来电时弹出提示框选择接听或拒绝
- 挂断: 通话中点击"挂断"按钮结束通话
- 注销: 点击"注销"按钮从服务器断开连接
2. 配置示例
用户名: 1001
密码: 123456
域名: sip.example.com
注册服务器: 192.168.1.100
端口: 5060
目标号码: 1002@sip.example.com
3. 关键特性
- 完整的 SIP 协议栈实现
- 支持注册/注销流程
- 支持基本呼叫控制(发起、接听、挂断)
- 自动注册刷新
- 详细的日志记录
- 友好的图形界面
4. 注意事项
- 确保网络防火墙允许 SIP 端口(默认 5060)通信
- 需要有效的 SIP 服务器地址和账户信息
- 支持 UDP 传输协议
- 包含基本的错误处理和状态监控
