C# 常用功能整合-3

目录

WebSocket

1.需求

2.WebSocket 封装

3.前端部分

4.网页端

HTTP通讯

Task常用的两种使用方式

1.无返回值方式

2.有返回值方式

定时器

1.用线程

2.用winform 自带的组件

比较两个对象的属性值是否相等

设置软件开机启动


WebSocket

1.需求

在使用之前,引入Fleck插件,下面案例将采用winfrom作为服务器,html为客户端

需要注意的是,本案例经过测试有一个问题,就是客户端如果连接上服务器后,如果长时间不通讯,那么连接会自动断开,这里其实可以写一个类似TCP协议的心跳机制来解决这个问题。

2.WebSocket 封装

 web_socket代码

using Fleck;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WebSocket
{
    public class Web_Socket
    {
        //客户端url以及其对应的Socket对象字典
        IDictionary<string, IWebSocketConnection> dic_Sockets = new Dictionary<string, IWebSocketConnection>();
        //创建一个 websocket ,0.0.0.0 为监听所有的的地址
        WebSocketServer server = new WebSocketServer("ws://0.0.0.0:30000");

        //打开连接委托
        public delegate void _OnOpen(string ip);
        public event _OnOpen OnOpen;
        //关闭连接委托
        public delegate void _OnClose(string ip);
        public event _OnClose OnClose;
        //当收到消息
        public delegate void _OnMessage(string ip, string msg);
        public event _OnMessage OnMessage;

        /// <summary>
        /// 初始化
        /// </summary>
        private void Init()
        {
            //出错后进行重启
            server.RestartAfterListenError = true;

            //开始监听
            server.Start(socket =>
            {
                //连接建立事件
                socket.OnOpen = () =>
                {
                    //获取客户端网页的url
                    string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort;
                    dic_Sockets.Add(clientUrl, socket);
                    if (OnOpen != null) OnOpen(clientUrl);
                    Console.WriteLine(DateTime.Now.ToString() + " | 服务器:和客户端网页:" + clientUrl + " 建立WebSock连接!");
                };

                //连接关闭事件
                socket.OnClose = () =>
                {
                    string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort;
                    //如果存在这个客户端,那么对这个socket进行移除
                    if (dic_Sockets.ContainsKey(clientUrl))
                    {
                        dic_Sockets.Remove(clientUrl);
                        if (OnClose != null) OnClose(clientUrl);
                    }
                    Console.WriteLine(DateTime.Now.ToString() + " | 服务器:和客户端网页:" + clientUrl + " 断开WebSock连接!");
                };

                //接受客户端网页消息事件
                socket.OnMessage = message =>
                {
                    string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort;
                    Receive(clientUrl, message);
                    if (OnMessage != null) OnMessage(clientUrl, message);
                };
            });
        }

        /// <summary>
        /// 向客户端发送消息
        /// </summary>
        /// <param name="webSocketConnection">客户端实例</param>
        /// <param name="message">消息内容</param>
        public void Send(string clientUrl, string message)
        {
            IWebSocketConnection webSocketConnection = GetUserSocketInstance(clientUrl);
            if (webSocketConnection != null)
            {
                if (webSocketConnection.IsAvailable)
                {
                    webSocketConnection.Send(message);
                }
            }
        }

        /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="clientUrl"></param>
        /// <param name="message"></param>
        private void Receive(string clientUrl, string message)
        {
            Console.WriteLine(DateTime.Now.ToString() + " | 服务器:【收到】来客户端网页:" + clientUrl + "的信息:\n" + message);
        }

        /// <summary>
        /// 获取用户实例
        /// </summary>
        /// <param name="clientUrl">用户的地址</param>
        public IWebSocketConnection GetUserSocketInstance(string clientUrl)
        {
            if (dic_Sockets.ContainsKey(clientUrl))
                return dic_Sockets[clientUrl];
            else
                return null;
        }

        /// <summary>
        /// 关闭某一个用户的连接
        /// </summary>
        /// <param name="clientUrl"></param>
        public void CloseUserConnect(string clientUrl)
        {
            IWebSocketConnection webSocketConnection = GetUserSocketInstance(clientUrl);
            if (webSocketConnection != null)
                webSocketConnection.Close();
        }

        /// <summary>
        /// 关闭与客户端的所有的连接
        /// </summary>
        public void CloseAllConnect()
        {
            foreach (var item in dic_Sockets.Values)
            {
                if (item != null)
                {
                    item.Close();
                }
            }
        }

        public Web_Socket()
        {
            Init();
        }
    }
}

3.前端部分

winform 界面

Form1代码:

using Fleck;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WebSocket
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private Web_Socket webSocket = new Web_Socket();

        private void Form1_Load(object sender, EventArgs e)
        {
            webSocket.OnOpen += this.OnOpen;
            webSocket.OnClose += this.OnClose;
            webSocket.OnMessage += this.OnMessage;

            Init();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            webSocket.CloseAllConnect();
        }

        private void Init()
        {

        }

        //当有客户端连接
        private void OnOpen(string clientUrl)
        {
            this.Invoke(new MethodInvoker(delegate
            {
                ComboBox_IPAddres.Items.Add(clientUrl);
                if (ComboBox_IPAddres.Items.Count > 0)
                    ComboBox_IPAddres.SelectedIndex = 0;
                ListBox_IPList.Items.Insert(ListBox_IPList.Items.Count, clientUrl);
            }));
            AddLogToListView("用户加入", string.Empty, string.Format("IP:{0} 加入了连接", clientUrl), true);
        }

        //当客户端关闭
        private void OnClose(string clientUrl)
        {
            this.Invoke(new MethodInvoker(delegate
            {
                ComboBox_IPAddres.Items.Remove(clientUrl);
                ListBox_IPList.Items.Remove(clientUrl);
            }));
            AddLogToListView("用户退出", string.Empty, string.Format("IP:{0} 关闭了连接", clientUrl), true);
        }

        //当收到客户端消息
        private void OnMessage(string clientUrl, string message)
        {
            AddLogToListView("服务器接收", clientUrl, message);
        }

        /// <summary>
        /// 添加日志到日志列表
        /// </summary>
        /// <param name="clientUrl"></param>
        /// <param name="message"></param>
        private void AddLogToListView(string title, string clientUrl, string message, bool rest = false)
        {
            this.Invoke(new MethodInvoker(delegate
            {
                int len = ListBox_MessageList.Items.Count;
                if (!rest)
                    ListBox_MessageList.Items.Insert(len, string.Format("[{0}] IP:{1} 内容:{2}", title, clientUrl, message));
                else
                    ListBox_MessageList.Items.Insert(len, string.Format("[{0}] {1}", title, message));
            }));
        }

        /// <summary>
        /// 发送按钮点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Send_Click(object sender, EventArgs e)
        {
            string clientUrl = ComboBox_IPAddres.Text;
            string content = TextBox_Message.Text;
            if (string.IsNullOrEmpty(content))
            {
                MessageBox.Show("发送内容为空");
                return;
            }
            if (string.IsNullOrEmpty(clientUrl))
            {
                MessageBox.Show("请选择一个IP地址");
                return;
            }
            webSocket.Send(clientUrl, content);
            TextBox_Message.Text = string.Empty;
            AddLogToListView("服务器发送", clientUrl, content);
        }
    }
}

4.网页端

html:

<!DOCTYPE  HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>websocket client</title>
    <script type="text/javascript">
        var start = function () {
            var inc = document.getElementById('incomming');
            var wsImpl = window.WebSocket || window.MozWebSocket;
            var form = document.getElementById('sendForm');
            var input = document.getElementById('sendText');

            inc.innerHTML += "连接服务器..<br/>";

            // 创建一个新的websocket并连接
            window.ws = new wsImpl('ws://localhost:30000/');

            // 当数据来自服务器时,将调用此方法
            ws.onmessage = function (evt) {
                inc.innerHTML += ("[来自服务器的消息] " + evt.data + '<br/>');
                console.log("[来自服务器的消息] " + evt.data);
            };

            // 当建立连接时,将调用此方法
            ws.onopen = function () {
                inc.innerHTML += '已建立连接.. <br/>';
            };

            // 当连接关闭时,将调用此方法
            ws.onclose = function () {
                inc.innerHTML += '连接已关闭.. <br/>';
            }

            form.addEventListener('submit', function (e) {
                e.preventDefault();
                var val = input.value;
                ws.send(val);
                input.value = "";
            });
        }
        window.onload = start;
    </script>
</head>
<body>
    <form id="sendForm">
        <span>输入内容按回车发送消息</span> <br/>
        <input id="sendText" placeholder="Text to send" />
    </form>
    <pre id="incomming"></pre>
</body>
</html>

经过测试,发送,接收消息正常

HTTP通讯

代码:

using Newtonsoft.Json;
using System.IO;
using System.Net;
using System.Text;

namespace HTTP
{
    public class RestClient
    {
        /// <summary>
        /// Get请求
        /// </summary>
        /// <param name="url">请求url</param>
        /// <returns></returns>
        public static string Get(string url)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            if (req == null || req.GetResponse() == null)
                return string.Empty;

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            if (resp == null)
                return string.Empty;

            using (Stream stream = resp.GetResponseStream())
            {
                //获取内容
                using (StreamReader reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }


        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="postData"></param>
        /// <returns></returns>
        public static string Post(string url, object postData)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            if (req == null)
                return string.Empty;

            req.Method = "POST";
            req.ContentType = "application/json";
            req.Timeout = 15000;

            byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(postData));
            //注意:无需手动指定长度 (否则可能会报流未处理完就关闭的异常,因为ContentLength时候会比真实post数据长度大)
            //req.ContentLength = data.Length;
            using (Stream reqStream = req.GetRequestStream())
            {
                reqStream.Write(data, 0, data.Length);
            }

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            if (resp == null)
                return string.Empty;

            using (Stream stream = resp.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
                {
                    return reader.ReadToEnd();
                }
            }
        }

    }
}

上面的Post请求在有些接口可能没有效果,可以使用下面的方法,其中第一个方法是无参数的Post请求,第二个方法只要将所需的Key和Value添加进字典就行了

/// <summary>
/// 指定Post地址使用Get 方式获取全部字符串
/// </summary>
/// <param name="url">请求后台地址</param>
/// <returns></returns>
public static string Post(string url)
{
    string result = "";
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
    req.Method = "POST";
    HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
    Stream stream = resp.GetResponseStream();
    //获取内容
    using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
    {
        result = reader.ReadToEnd();
    }
    return result;
}


/// <summary>
/// 指定Post地址使用Get 方式获取全部字符串
/// </summary>
/// <param name="url">请求后台地址</param>
/// <returns></returns>
public static string Post(string url, Dictionary<string, string> dic)
{
    string result = "";
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
    req.Method = "POST";
    req.ContentType = "application/x-www-form-urlencoded";
    #region 添加Post 参数
    StringBuilder builder = new StringBuilder();
    int i = 0;
    foreach (var item in dic)
    {
        if (i > 0)
            builder.Append("&");
        builder.AppendFormat("{0}={1}", item.Key, item.Value);
        i++;
    }
    byte[] data = Encoding.UTF8.GetBytes(builder.ToString());
    req.ContentLength = data.Length;
    using (Stream reqStream = req.GetRequestStream())
    {
        reqStream.Write(data, 0, data.Length);
        reqStream.Close();
    }
    #endregion
    HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
    Stream stream = resp.GetResponseStream();
    //获取响应内容
    using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
    {
        result = reader.ReadToEnd();
    }
    return result;
}

Task常用的两种使用方式

1.无返回值方式

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyTask
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine("1秒执行结束");
            }));
            taskList.Add(Task.Factory.StartNew(() =>
            {
                Thread.Sleep(800);
                Console.WriteLine("o.8秒执行结束");
            }));

            Console.WriteLine("执行中");

            TaskFactory taskFactory = new TaskFactory();
            taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), tArray =>
            {
                Thread.Sleep(200);
                Console.WriteLine("等待这些完成后执行");
            }));

            Console.ReadKey();
        }
    }
}

运行:

这里由于作了延时处理,执行顺序不一致

假设任务中出现异常:

代码:

taskList.Add(Task.Factory.StartNew(() =>
{
    try
    {
        int a = int.Parse("");
    }
    catch (Exception ex)
    {
        Console.WriteLine("=========异常:" + ex.Message);
    }
    Console.WriteLine("=========第二种方式");
}));

运行:

2.有返回值方式

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyTask
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Task<int>> taskList1 = new List<Task<int>>();
            taskList1.Add(Task<int>.Factory.StartNew(() =>
                {
                    int num = 3 * 5;
                    Console.WriteLine("有返回值的任务队列 1");
                    return num;
                }
            ));
            taskList1.Add(Task<int>.Factory.StartNew(() =>
                {
                    int num = 3 * 7;
                    Console.WriteLine("有返回值的任务队列 2");
                    return num;
                }
            ));
            new TaskFactory().ContinueWhenAll(taskList1.ToArray(), tArray =>
            {
                Console.WriteLine("有返回值的任务队列执行完毕");
                foreach (Task<int> item in taskList1)
                {
                    Console.WriteLine("执行结果:" + item.Result);
                }
            });


            Console.ReadKey();
        }
    }
}

运行:

定时器

1.用线程

    
private void Form1_Load(object sender, EventArgs e){
    timer = new System.Timers.Timer();
    timer.Interval = 3000;
    timer.AutoReset = false;
    timer.Enabled = true;
    timer.Elapsed += new System.Timers.ElapsedEventHandler(GetCurDateTime);
}

private void GetCurDateTime(object sender, System.Timers.ElapsedEventArgs e)
{
    Label_Log.Text = string.Empty;
}

2.用winform 自带的组件

private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

private void Form1_Load(object sender, EventArgs e){
    timer.Interval = 3000;
    timer.Tick += GetCurDateTime; 
    timer.Enabled = true;
}

private void GetCurDateTime(object sender, EventArgs e)
{
    Label_Log.Text = string.Empty;
}

比较两个对象的属性值是否相等

需要注意的是,两个对象中的字段必须是属性,带有get,set,否则无法识别,返回的就是false

代码

/// <summary>
/// 比较--两个类型一样的实体类对象的值
/// </summary>
/// <param name="oneT"></param>
/// <returns>返回true表示两个对象的数据相同,返回false表示不相同</returns>
private bool CompareType<T>(T oneT, T twoT)
{
    bool result = true;//两个类型作比较时使用,如果有不一样的就false
    Type typeOne = oneT.GetType();
    Type typeTwo = twoT.GetType();
    //如果两个T类型不一样  就不作比较
    if (!typeOne.Equals(typeTwo)) { return false; }
    PropertyInfo[] pisOne = typeOne.GetProperties(); //获取所有公共属性(Public)
    PropertyInfo[] pisTwo = typeTwo.GetProperties();
    //如果长度为0返回false
    if (pisOne.Length <= 0 || pisTwo.Length <= 0)
    {
        return false;
    }
    //如果长度不一样,返回false
    if (!(pisOne.Length.Equals(pisTwo.Length))) { return false; }
    //遍历两个T类型,遍历属性,并作比较
    for (int i = 0; i < pisOne.Length; i++)
    {
        //获取属性名
        string oneName = pisOne[i].Name;
        string twoName = pisTwo[i].Name;
        //获取属性的值
        object oneValue = pisOne[i].GetValue(oneT, null);
        object twoValue = pisTwo[i].GetValue(twoT, null);
        //比较,只比较值类型
        if ((pisOne[i].PropertyType.IsValueType || pisOne[i].PropertyType.Name.StartsWith("String")) && (pisTwo[i].PropertyType.IsValueType || pisTwo[i].PropertyType.Name.StartsWith("String")))
        {
            if (oneName.Equals(twoName))
            {
                if (oneValue == null)
                {
                    if (twoValue != null)
                    {
                        result = false;
                        break; //如果有不一样的就退出循环
                    }
                }
                else if (oneValue != null)
                {
                    if (twoValue != null)
                    {
                        if (!oneValue.Equals(twoValue))
                        {
                            result = false;
                            break; //如果有不一样的就退出循环
                        }
                    }
                    else if (twoValue == null)
                    {
                        result = false;
                        break; //如果有不一样的就退出循环
                    }
                }
            }
            else
            {
                result = false;
                break;
            }
        }
        else
        {
            //如果对象中的属性是实体类对象,递归遍历比较
            bool b = CompareType(oneValue, twoValue);
            if (!b) { result = b; break; }
        }
    }
    return result;
}



//调用
private void btnOrder_Click(object sender, EventArgs e)
{

    //实体类比较
    UserVo userVoOne = new UserVo();
    UserVo userVoTwo = new UserVo();

    userVoOne.UserID = 1;
    //userVoOne.UserAccount = "a";

    userVoTwo.UserID = 1;
 
    bool flag = CompareType(userVoOne, userVoTwo);
    if (flag)
    {
        MessageBox.Show("两个类数据相同");
    }
    else
    {
        MessageBox.Show("两个类数据不相同");
    }
}

设置软件开机启动

界面:

代码:

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 开机启动
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //获得应用程序路径
        private string StrAssName = string.Empty;
        //获得应用程序名
        private string ShortFileName = string.Empty;

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        //添加到开机启动
        public void AddPowerBoot()
        {
            try
            {
                if(string.IsNullOrEmpty(ShortFileName) || string.IsNullOrEmpty(StrAssName))
                {
                    MessageBox.Show("输入框不能为空");
                    return;
                }

                //此方法把启动项加载到注册表中
                //获得应用程序路径
                //string StrAssName = Application.StartupPath + @"\" + Application.ProductName + @".exe";
                //获得应用程序名
                //string ShortFileName = Application.ProductName;
                //路径:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run
                RegistryKey rgkRun = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
                if (rgkRun == null)
                {
                    rgkRun = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
                }
                rgkRun.SetValue(ShortFileName, StrAssName);
                MessageBox.Show("添加到注册表成功");
            }
            catch (Exception ex)
            {
                MessageBox.Show("执行错误:" + ex.Message);
            }
        }

        //移除开机启动
        public void RemovePowerBoot()
        {
            try
            {
                if (string.IsNullOrEmpty(ShortFileName) || string.IsNullOrEmpty(StrAssName))
                {
                    MessageBox.Show("输入框不能为空");
                    return;
                }

                //此删除注册表中启动项
                //获得应用程序名
                //string ShortFileName = Application.ProductName;
                RegistryKey rgkRun = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
                if (rgkRun == null)
                {
                    rgkRun = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
                }
                rgkRun.DeleteValue(ShortFileName, false);
                MessageBox.Show("移除注册表成功");
            }
            catch (Exception ex)
            {
                MessageBox.Show("执行错误:" + ex.Message);
            }
        }

        //设置开机启动
        private void Button_SetPowerBoot_Click(object sender, EventArgs e)
        {
            AddPowerBoot();
        }

        //移除开机启动
        private void Button_RemovePowerBoot_Click(object sender, EventArgs e)
        {
            RemovePowerBoot();
        }

        //选择要操作的程序
        private void Button_GetModuleFileName_Click(object sender, EventArgs e)
        {
            //创建对象
            OpenFileDialog ofg = new OpenFileDialog();
            //设置默认打开路径(绝对路径)
            ofg.InitialDirectory = "C:\\Users\\Administrator\\Desktop";
            //设置打开标题、后缀
            ofg.Title = "请选择程序的exe文件";
            ofg.Filter = "exe文件|*.exe";
            string path = string.Empty;
            if (ofg.ShowDialog() == DialogResult.OK)
            {
                //得到打开的文件路径(包括文件名)
                path = ofg.FileName.ToString();
                Console.WriteLine("打开文件路径是:" + path);

                TextBox_GetModuleFileName.Text = path;
                StrAssName = path;

                string[] arr = path.Split('\\');
                ShortFileName = arr[arr.Length - 1];
                Label_ProgramName.Text = "应用程序名:" + ShortFileName;
            }
            else if (ofg.ShowDialog() == DialogResult.Cancel)
            {
                MessageBox.Show("未选择打开文件!");
            }
        }
    }
}

程序一定要用管理员身份运行,否则会报错

end

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊思宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值