基于 LumiSoft.Net 库实现 SIP 客户端,支持注册、呼叫、接听等完整功能。
一、环境准备
1. 安装 LumiSoft.Net 库
xml
<!-- 通过 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)
csharp
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)
csharp
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)
csharp
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)
csharp
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)
csharp
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();
}
}
#endregion
protected override void OnFormClosing(FormClosingEventArgs e)
{
m_SipClient?.Dispose();
base.OnFormClosing(e);
}
}
}
6. 程序入口 (Program.cs)
csharp
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/contentcst/45086.html
三、配置文件 (App.config)
xml
<?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 传输协议
- 包含基本的错误处理和状态监控