CMPP协议的客户端

using System;
using System.Security.Cryptography; 
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections;

namespace CMPP.YOURCOMPANY
{
 public delegate void ReportEventHandler(object sender, ReportEventArgs e);  //声明一个事件的指代(指针)
 public delegate void SMSEventHandler(object sender, SMSEventArgs e);   //声明一个事件的指代(指针)
 public delegate void TerminateEventHandler(object sender,TerminateEventArgs e);   //声明收到终止信号
 public delegate void TerminateRespEventHandler(object sender,TerminateRespEventArgs e);  //回应事件发生
 public delegate void TestEventHandler(object sender,TestEventArgs e);
 public delegate void TestRespEventHandler(object sender,TestRespEventArgs e);
 public delegate void ConnectRespEventHandler(object sender,ConnectRespEventArgs e);
 public delegate void CancelRespEventHandler(object sender,CancelRespEventArgs e);
 public delegate void SubmitRespEventHandler(object sender,SubmitRespEventArgs e);
 public delegate void QueryRespEventHandler(object sender,QueryRespEventArgs e);
 public delegate void LogonSuccEventHandler(object sender,EventArgs e); //当成功登录系统
 public delegate void SocketClosedEventHandler(object sender,EventArgs e); //当套接字被检测到关闭
 public delegate void FailedItemDeletedEventHandler(object sender,WaitingQueueItemEventArgs e); //当一条等待队列的消息超过60秒没有回应
 

 public delegate void CMPPClientSvcStopEventHandler(object sender, ClientQueueStateArgs e); //当CMPP服务停止时候触发事件

 /// <summary>
 /// 作为CMPP协议的客户端,具有的登陆、发送、接受功能
 /// 会开3 个线程处理: 1、处理需要发送 MO(下行)的消息
 ///       2、处理从移动服务器发送过来CMPP的消息
 ///       3、处理连接断等信息,检查需要重发的消息,检查收到的报告、短信,并调用 OnReport 事件 OnSMS事件
 /// </summary>
 public class CMPPClient
 {
  public static long CMPP_ACTIVE_TEST_C_TICKs= 30  ;  // *3 ;  //长连接的active_test测试时间
  public static long CMPP_ACTIVE_TEST_T_TICKs= 60 ;    // 消息失败时间 60秒
  public static int CMPP_ACTIVE_TEST_N_COUNT=3;  //3次 
  //public static int CMPP_MSG_MAX=100;   //一次取得的最大消息数量
  public static int CMPP_Port=7890;
 
  public event ReportEventHandler onReportHandler;   //指向事件处理代码的指针
  public event SMSEventHandler onSMSHandler;     //短信到来处理
  public event TestEventHandler onTestHandler;
  public event TestRespEventHandler onTestRespHandler;
  public event ConnectRespEventHandler onConnectRespHandler;
  public event CancelRespEventHandler onCancelRespHandler;
  public event TerminateEventHandler onTerminateHandler;
  public event TerminateRespEventHandler onTerminateRespHandler;
  public event SubmitRespEventHandler onSubmitRespHandler;
  public event QueryRespEventHandler onQueryRespHandler;
  public event LogonSuccEventHandler onLogonSuccEventHandler;
  public event SocketClosedEventHandler onSocketClosedHandler;
  public event FailedItemDeletedEventHandler onWaitingItemDeltedHandler; //当等待队列消息超时
 
  public event CMPPClientSvcStopEventHandler onClientSvcStopedHandler;  //当服务停止时候的事件

  //private 函数区域//
  private Socket  tcp=null;    
  private IPHostEntry ip=null;  
  private IPEndPoint  cmpp_ep=null;  
  private int   RecvTimeOut =1000;       //2000ms的接受超时
  private int   SendTimeout =2000;       //2000ms的发送超时
  private string  CMPP_Server="";   //移动的服务器IP或者DNS名
  private string  systemID="";   //企业编号
  private string  userName="";   //sp的号码 /企业编号
  private string  PassWord="";   //口令 
  private bool  isStop=false;   //本服务是否终止运行
  private bool  isLogin=false;   //是否已经登录   
  private Thread  Send_Thread;   //发送线程,专门处理对移动的数据包
  private Thread  Recv_Thread;   //专门处理接收包
  private Thread  Deamo_Thread;   //监控线程
  private string  ErrorInfo="";   //存放最后一次发生的错误信息 或者参考信息     
  private DateTime _current_time=DateTime.Now;     //上一次 ping的时间 
  private uint  lastSequence;   //流水号,每一次重新启动都需要重新设定 lastSequence
  private SortedList _outSeqQueue=new SortedList();   //消息队列存储 QueueItem,存储发送队列中的状态
  private SortedList  _waitingSeqQueue=new SortedList(); //消息队列存储 QueueItem
  private int   sub_resp=0;       //最后返回的包 Sequence
  private DateTime _lastOkTime;      //最后正确发送消息时间
  private bool  _bNre=false;      //空引用错误,套接字错误
 
  //private ManualResetEvent _connectionDone=new ManualResetEvent(false); //是否连接到套接字服务器,也就是CMPP服务器
  //private ManualResetEvent _lastsendDone=new ManualResetEvent(false);  //上一次发送是否完毕
  //private ManualResetEvent _lastrecvDone=new ManualResetEvent(false);  //上一次接收是否完毕
      
  private void ping()    //发送一次ping包 ,不经过_outSeqQueue 直接存储在 out queue中
  {
   uint seq=this.getNextSequence();
   MSG.CMPP_MSG_TEST test=new MSG.CMPP_MSG_TEST(seq);
   QueueItem q=new QueueItem(seq,(uint)MSG.CMPP_COMMAND_ID.CMPP_ACTIVE_TEST,0,0);
   q.setmsgObj(test);
   this.addToOutQueue(q);
  }
  
  private string getValIdTime(DateTime d)        //返回短信存活时间
  {
   DateTime n=d.AddHours(2); //2小时
   return(n.Year.ToString().Substring(2) + n.Month.ToString().PadLeft(2,'0')+n.Day.ToString().PadLeft(2,'0')+n.Hour.ToString().PadLeft(2,'0')+n.Minute.ToString().PadLeft(2,'0')+n.Second.ToString().PadLeft(2,'0')+"032+");           
  }
 
  private bool isPingTime( )  //是否到了ping一次的时间
  {
   System.TimeSpan   l=(DateTime.Now - this._current_time );
  
   if ( l.TotalSeconds >= (CMPPClient.CMPP_ACTIVE_TEST_C_TICKs))
   {   
    lock(this)
    {
     this._current_time =DateTime.Now;       
     return(true);
    }
   }
   else
   {
    return(false);
   }
  }
 
  private void checkReSend()    //是否需要再一次ping //查询 _waitingSeqQueue 是否存在 上一次 没有相应的消息
  {   //调查waiting queue 中的所有消息,如果入列时间超过60
   for(int i=0;i<this._waitingSeqQueue.Count;i++)
   {
    Thread.Sleep(20);
    QueueItem q=(QueueItem)this._waitingSeqQueue.GetByIndex(i);  
    if(q!=null)
    {
     DateTime this_time=DateTime.Now ; //去当前时间
     TimeSpan t=this_time-q.inQueueTime ;
     if(t.TotalSeconds >CMPPClient.CMPP_ACTIVE_TEST_T_TICKs ) //达到超时时间
     {//需要重新发送消息
      if(q.FailedCount>=CMPPClient.CMPP_ACTIVE_TEST_N_COUNT)
      {
       //报告消息发送失败
       if(this.onWaitingItemDeltedHandler!=null)
       {
        WaitingQueueItemEventArgs e=new WaitingQueueItemEventArgs(q);
        this.onWaitingItemDeltedHandler(this,e);
       }
       this.delFromWaitingQueue(q); //从等待队列中删除
       //q.MsgState =(int)MSG_STATE.SENDED_WAITTING;
      }
      else
      {//可以尝试继续发送
       q.inQueueTime = this_time;
       q.FailedCount ++ ;
       q.MsgState =(int)MSG_STATE.SENDED_WAITTING ;
       this.sendQueueItem(q);     
      }
     }     
    }
   }   
 
  }
 
  private void startThreads()
  {
   Deamo_Thread=new Thread(new ThreadStart(this.DeamonThread));
   Deamo_Thread.Start();
  }
 
  private QueueItem newQueueItem(int msgtype,int msgstate,object msg)  //生成一个消息队列成员对象实例
  {
   uint seq=this.getNextSequence();   //
   QueueItem q=new QueueItem(seq,(uint)msgtype,0,msgstate);
   q.setmsgObj(msg);       //设定消息为 object
   return(q);
  }
 
  private QueueItem getOutQueueItem(uint seq)  //获取MT 队列中的消息项目
  {
   lock(this)
   {
    return((QueueItem)this._outSeqQueue[seq]) ;
   }
  }
 
  private QueueItem getWaitingQueueItem(uint seq)  //获取等待队列中的消息
  {
   return((QueueItem) this._waitingSeqQueue[seq]);
  }
       
  private void addToOutQueue(QueueItem q)
  {
   lock(this)
   {
    this._outSeqQueue.Add(q.Sequence,q);    
   }
  }
 
  private void addToWaitingQueue(QueueItem q)
  {
   lock(this)
   {
    if(!this._waitingSeqQueue.ContainsKey(q.Sequence))
    {
     this._waitingSeqQueue.Add(q.Sequence,q);  
    }
   }
  }
 
  private QueueItem getTopOutQueue()     //需要在取之前进行判断
  {
   for(int i=0;i<this._outSeqQueue.Count;i++)
   {
    QueueItem q=(QueueItem)this._outSeqQueue.GetByIndex(i);  
    if(q!=null)
    {
     if(q.MsgState==(int)MSG_STATE.NEW)  //新消息,立即返回
     {
      lock(this)
      {
       q.MsgState =(int)MSG_STATE.SENDING; //发送状态
      }
      return(q);  
     }
     else
     {
      q=null;
     }
    }
   }   
   return(null);
  }
 
  private ArrayList getTop16Queue() //返回16条最顶的消息
  {
   int arrlength=0;
   ArrayList reArr=new ArrayList() ;
   QueueItem q=getTopOutQueue();
   while(q!=null || arrlength <= 16)
   {    
    if(q!=null)
    {
     reArr.Add(q);
     arrlength++;
    }
    else
    {
     break;
    }
    q=getTopOutQueue();
   }
   
   if(arrlength>0)
   {
    return(reArr);  
   }
   else
   {
    return(null);
   }
  }
 
  private void delFromOutQueue(QueueItem  q)
  {
   lock(this)
   {
    this._outSeqQueue.Remove(q.Sequence);  
   }
  }
 
  private void delFromOutQueue(uint seq)
  {
   lock(this)
   {
    this._outSeqQueue.Remove(seq); 
   }
  }
 
  private void delFromWaitingQueue(QueueItem q)
  {
   lock(this)
   {
    this._waitingSeqQueue.Remove(q.Sequence); 
   }
  }
 
  private void delFromWaitingQueue(uint seq)
  {
   this._waitingSeqQueue.Remove(seq); 
  }
 
  private void  SendLogin(string SystemID,string spNum,string Password)
  {//发送登录验证包   
   systemID=SystemID;
   userName=spNum;
   PassWord=Password;
   uint seq=this.getNextSequence(); //取得一个流水号
   MSG.CMPP_MSG_CONNECT cn=new MSG.CMPP_MSG_CONNECT(seq);
   cn.Password =Password.Trim();
   cn.SourceAdd =SystemID.Trim();
   tcp.Send(cn.ToBytes());
  }
 

  private byte[]  prepairPKs(QueueItem outitem)//将QueueItem发送出去
  {
   uint seq=outitem.Sequence ;
   uint msgtype=outitem.MsgType;  
   switch(msgtype)
   {
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_ACTIVE_TEST :
     MSG.CMPP_MSG_TEST test=(MSG.CMPP_MSG_TEST) outitem.getMsgObj(); //发送队列中取出
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING;
      this.delFromOutQueue(seq);
      this.addToWaitingQueue(outitem);    //等待服务器的active_TEST_resp
     }
     outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ;
     return(test.toBytes());      
      
 
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_ACTIVE_TEST_RESP:
     MSG.CMPP_MSG_TEST_RESP test_reply=(MSG.CMPP_MSG_TEST_RESP)outitem.getMsgObj(); //发送队列中取出//取出需要发送的具体消息
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING ;
      this.delFromOutQueue(seq);
     }
     outitem.MsgState = (int)MSG_STATE.SENDING_FINISHED ;  //完成
     return(test_reply.toBytes());
      
      
 
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_CANCEL :
     MSG.CMPP_MSG_CANCEL cancel=(MSG.CMPP_MSG_CANCEL)outitem.getMsgObj();    //还原成消息类
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING ;
      this.delFromOutQueue(seq);
      this.addToWaitingQueue(outitem);    //等待回应
     }
     outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ;
     return(cancel.toBytes());     
 
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_DELIVER_RESP:
     MSG.CMPP_MSG_DELIVER_RESP deliver_resp=(MSG.CMPP_MSG_DELIVER_RESP)outitem.getMsgObj(); //发送队列中取出;
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING ;
      this.delFromOutQueue(seq);
     }
     outitem.MsgState=(int)MSG_STATE.SENDING_FINISHED  ;  //完成
     return (deliver_resp.toBytes());      
      
 
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_QUERY  :
     MSG.CMPP_MSG_QUERY query = (MSG.CMPP_MSG_QUERY )outitem.getMsgObj(); //发送队列中取出;
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING  ;
      this.delFromOutQueue(seq);
      this.addToWaitingQueue(outitem);
     }
     outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ; //等待回应
     return(query.toBytes());      
      
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_SUBMIT :
     MSG.CMPP_MSG_SUBMIT submit =(MSG.CMPP_MSG_SUBMIT)outitem.getMsgObj(); //发送队列中取出;
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING ;
      this.delFromOutQueue(seq);
      this.addToWaitingQueue (outitem);
     }
     outitem.MsgState =(int)MSG_STATE.SENDING_FINISHED ;
     return(submit.toBytes());
            
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_TERMINATE :
     MSG.CMPP_MSG_TERMINATE terminate=(MSG.CMPP_MSG_TERMINATE)outitem.getMsgObj(); //发送队列中取出;
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING ;
      this.delFromOutQueue(seq);
      this.addToWaitingQueue(outitem);
     }
     outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ;
     return(terminate.toBytes());     
 
    case (uint)MSG.CMPP_COMMAND_ID.CMPP_TERMINATE_RESP :
     MSG.CMPP_MSG_TERMINATE_RESP terminate_resp=(MSG.CMPP_MSG_TERMINATE_RESP)outitem.getMsgObj(); //发送队列中取出;
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING ;
      this.delFromOutQueue(seq);
     }
     outitem.MsgState =(int)MSG_STATE.SENDING_FINISHED  ;
     return(terminate_resp.toBytes()); 

    default:  
     test=(MSG.CMPP_MSG_TEST) outitem.getMsgObj(); //发送队列中取出
     lock(this)
     {
      outitem.MsgState =(int)MSG_STATE.SENDING;
      this.delFromOutQueue(seq);
      this.addToWaitingQueue(outitem);    //等待服务器的active_TEST_resp
     }
     outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ;
     return(test.toBytes()); 
   }   
  }

  private void sendQueueItem(QueueItem outitem)//将QueueItem发送出去
  {
   uint seq=outitem.Sequence ;
   uint msgtype=outitem.MsgType;
   try
   {
    switch(msgtype)
    {
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_ACTIVE_TEST :
      MSG.CMPP_MSG_TEST test=(MSG.CMPP_MSG_TEST) outitem.getMsgObj(); //发送队列中取出
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING;
       this.delFromOutQueue(seq);
       this.addToWaitingQueue(outitem);    //等待服务器的active_TEST_resp
      }
      tcp.Send(test.toBytes());
      outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ;
      break;
 
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_ACTIVE_TEST_RESP:
      MSG.CMPP_MSG_TEST_RESP test_reply=(MSG.CMPP_MSG_TEST_RESP)outitem.getMsgObj(); //发送队列中取出//取出需要发送的具体消息
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING ;
       this.delFromOutQueue(seq);
      }
      tcp.Send(test_reply.toBytes());
      outitem.MsgState = (int)MSG_STATE.SENDING_FINISHED ;  //完成
      break;
 
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_CANCEL :
      MSG.CMPP_MSG_CANCEL cancel=(MSG.CMPP_MSG_CANCEL)outitem.getMsgObj();    //还原成消息类
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING ;
       this.delFromOutQueue(seq);
       this.addToWaitingQueue(outitem);    //等待回应
      }
      tcp.Send(cancel.toBytes());     
      outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ;
      break;
 
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_DELIVER_RESP:
      MSG.CMPP_MSG_DELIVER_RESP deliver_resp=(MSG.CMPP_MSG_DELIVER_RESP)outitem.getMsgObj(); //发送队列中取出;
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING ;
       this.delFromOutQueue(seq);
      }
      tcp.Send(deliver_resp.toBytes());
      outitem.MsgState=(int)MSG_STATE.SENDING_FINISHED  ;  //完成
      break;
 
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_QUERY  :
      MSG.CMPP_MSG_QUERY query = (MSG.CMPP_MSG_QUERY )outitem.getMsgObj(); //发送队列中取出;
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING  ;
       this.delFromOutQueue(seq);
       this.addToWaitingQueue(outitem);
      }
      tcp.Send(query.toBytes());
      outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ; //等待回应
      break;
 
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_SUBMIT :
      MSG.CMPP_MSG_SUBMIT submit =(MSG.CMPP_MSG_SUBMIT)outitem.getMsgObj(); //发送队列中取出;
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING ;
       this.delFromOutQueue(seq);
       this.addToWaitingQueue (outitem);
      }
      tcp.Send(submit.toBytes());
      outitem.MsgState =(int)MSG_STATE.SENDING_FINISHED ;
      break;
 
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_TERMINATE :
      MSG.CMPP_MSG_TERMINATE terminate=(MSG.CMPP_MSG_TERMINATE)outitem.getMsgObj(); //发送队列中取出;
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING ;
       this.delFromOutQueue(seq);
       this.addToWaitingQueue(outitem);
      }
      if(this.tcpIsCanUse())
      {
       tcp.Send(terminate.toBytes());
       outitem.MsgState =(int)MSG_STATE.SENDED_WAITTING ;       
      }
      this.isStop =true;     //通知其他线程可以退出了
      break;
 
     case (uint)MSG.CMPP_COMMAND_ID.CMPP_TERMINATE_RESP :
      MSG.CMPP_MSG_TERMINATE_RESP terminate_resp=(MSG.CMPP_MSG_TERMINATE_RESP)outitem.getMsgObj(); //发送队列中取出;
      lock(this)
      {
       outitem.MsgState =(int)MSG_STATE.SENDING ;
       this.delFromOutQueue(seq);       
      }
      tcp.Send(terminate_resp.toBytes());
      outitem.MsgState =(int)MSG_STATE.SENDING_FINISHED  ;     
      break;
    }
    LogLastOkTime(DateTime.Now );  //记录当前最后一次消息soket正确时间
   }
   catch(SocketException se)
   {
    //发生套接字错误
    this.ErrorInfo =this.ErrorInfo +"/r/n"+se.ToString ();
   }
   catch(NullReferenceException nre)
   {
    this._bNre =true;  //出现空引用错误
    this.ErrorInfo =this.ErrorInfo +"/r/n"+nre.ToString ();
   }
  }
 
  private bool tcpIsCanUse()  //测试当前tcp是否可用
  {
   bool reval=true;
   DateTime t=DateTime.Now ;
   TimeSpan ts=t- this._lastOkTime;
   if(ts.TotalSeconds > CMPPClient.CMPP_ACTIVE_TEST_T_TICKs ) //60秒
   {
    reval=false;  //不可用
   }
   if(this._bNre )
   {
    reval=false;
   }
   return(reval);
  }
 
  private void _reStartRecvNSend()
  {
   Send_Thread=new Thread(new ThreadStart(this.SendSPMsgThread));
   Send_Thread.Start();
   Recv_Thread=new Thread(new ThreadStart(this.RecvISMGMsgThread));
   Recv_Thread.Start();
  }
 
  private void LogLastOkTime(DateTime lastoktime)
  {
   lock(this)
   {
    this._lastOkTime=lastoktime;  //设定最后成功消息交互时间
   }
  }
 
  private void defaultReportHandler() //却省的报告事件处理函数
  {
 
  }
 
  private void defaultSMSHandler()
  {
 
  }
 
  private void defaultTeminateHandler()
  {
 
  }
 
  private void defaultTestEventHandler()
  {
 
  }
  private void defaultTestRespEventHandler()
  {
 
  }
  private void defaultTerminateEventHandler()
  {
  }
  private void defaultTerminateRespEventHandler()
  {
  }
  private void defaultCancelRespEventHandler()
  {
  }
  private void defaultQueryRespEventHandler()
  {
  }
 
  private void defaultConnectRespEventHandler()
  {
   QueueItem q=new QueueItem(this.getNextSequence(),(uint)MSG.CMPP_COMMAND_ID.CMPP_ACTIVE_TEST,0,(int)MSG_STATE.NEW);
   MSG.CMPP_MSG_TEST test=new MSG.CMPP_MSG_TEST(q.Sequence ); //立即发送包过去
   q.setmsgObj(test);
   this.addToOutQueue(q);  
  }
  private void defaultSubmitRespEventHandler()
  {
  }

  private void defaultClientStopEventHandler()
  {}
    
  private void rePortError(string info)
  {
 
  }
       
  private bool _init(string CMPPServer,int CMPPPort)
  {
   bool reVal=false;
   CMPP_Server=CMPPServer;
   CMPP_Port=CMPPPort;
   try
   {
    tcp=new Socket(AddressFamily.InterNetwork ,SocketType.Stream ,ProtocolType.Tcp );
    ip=Dns.GetHostByName(CM

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值