C# 基于 LumiSoft 实现 SIP 客户端方案

基于 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. 注意事项

  1. 确保网络防火墙允许 SIP 端口(默认 5060)通信
  2. 需要有效的 SIP 服务器地址和账户信息
  3. 支持 UDP 传输协议
  4. 包含基本的错误处理和状态监控
相关推荐
anOnion2 小时前
构建无障碍组件之Meter Pattern
前端·html·交互设计
小码哥_常2 小时前
Spring Boot配置diff:解锁配置管理新姿势
前端
小码哥_常2 小时前
告别onActivityResult!Android数据回传的3大痛点与终极解决方案
前端
hhcccchh2 小时前
1.2 CSS 基础选择器、盒模型、flex 布局、grid 布局
前端·css·css3
专吃海绵宝宝菠萝屋的派大星3 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
爱分享的阿Q3 小时前
Rust加WebAssembly前端性能革命实践指南
前端·rust·wasm
蓝黑20203 小时前
Vue的 value=“1“ 和 :value=“1“ 有什么区别
前端·javascript·vue
小李子呢02113 小时前
前端八股6---v-model双向绑定
前端·javascript·算法
yongui478344 小时前
C# 与三菱PLC通讯解决方案
开发语言·c#