《“透视”个人大数据》项目开发小记 --(二)网络服务端,邮箱验证和手机验证(C#,Java)

      现在网络的应用越来越普及,网络的构建也越来越简便,对于某些研究性项目自建网络服务端
也是可行的方案。本项目的网络服务,是用C#,基于Socket构建的,核心的工作是通过自定的BS60传输协议,实现与手机端,PC端的数据传输。服务端有两个基本数据表,一个是用户注册表,一个是用户在线数据表。数据管理系统,主要处理网络任务数据,网友分享数据。采用PC机,基于Socket的网络服务,用WIFI局域网就可以进行开发测试,外网要通过内网穿透技术。以下扼要介绍一下服务端的基本架构及邮箱验证和手机验证。
      (一),用C#构建基于Socket网络服务
            网络服务的用户监听,项目开始有两种模式,一个是ASYNC 模式,一个是Thread 模式,后来一直采用的是Thread 模式。除了基本的通用架构,服务端主要是处理(接收及发送)手机端及PC端的数据请求。
          1),网络服务的基本架构
     

      //获取本机IP地址
         public List<string> getIP()
         {
            List<string> listIP = new List<string>();
            try
            {
                string HostName = Dns.GetHostName(); //得到主机名
                IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
                for (int i = 0; i < IpEntry.AddressList.Length; i++)
                {
                    //从IP地址列表中筛选出IPv4类型的IP地址
                    //AddressFamily.InterNetwork表示此IP为IPv4,
                    //AddressFamily.InterNetworkV6表示此地址为IPv6类型
                    if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
                    {
                        listIP.Add(IpEntry.AddressList[i].ToString());
                    }
                }
                return listIP;
            }

            catch (Exception ex)
            {
                MessageBox.Show("获取本机IP出错:" + ex.Message);
                listIP.Clear();
                return listIP;
            }
        }
        //  启动网络服务监听
        private Thread threadWatch = null;//负责监听客户端连接请求的线程
        private Socket socketWatch = null;//负责监听的套接字
        int READBUFFERSIZE = 1024 * 1024;
        private void startSeverThread()
        {
            // 创建一个基于IPV4的TCP协议的Socket对象
            if (socketWatch == null)
            {
                socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            else
            {
                return;
            }
            //取得IP地址
            string strIPaddress = "";
            List<string> listIP = getIP();
            if (listIP.Count == 0)
            {
                   //  未能获取IP!
                   return;
            }
            else if (listIP.Count >= 1)
            {
                strIPaddress = listIP[0];
            }
            IPAddress ip = IPAddress.Parse(strIPaddress));
            //创建一个网络端点
            string strPort = "8899";
            IPEndPoint ippoint = new IPEndPoint(ip, int.Parse(strPort));
            //把负责监听的套接字绑定到唯一的IP和端口
            socketWatch.Bind(ippoint);
            //设置监听队列的长度
            socketWatch.Listen(10);

            threadWatch = new Thread(WatchConnection);
            threadWatch.IsBackground = true;//设置为后台
            threadWatch.Start();// 启动线程
    
        }
        // 用户SOCKET 结构  
        struct userSocket
        {
            public Socket curSocket;
            public int iposj;// 在线用户表的位置
        }
        // 监听客户端连接请求的线程
        void WatchConnection()
        {
            while (true)
            {
                //创建监听的连接套接字
                Socket sockConnection = socketWatch.Accept();
                // 创建连接成功添加到 Online User manager 对象里面
                // 将 IP ADDRESS 转换成 Long 数据
                long Ipvalue = GetIpLongVale(sockConnection.RemoteEndPoint.ToString());
                // 将当前访问时间存到 int 数据中 
                int icTimes = GetLnkServerTime();
                // 将访问信息添加到在线用户表中
                int icposj = onLineUserManager.AppendOnlineUser(0, sockConnection, Ipvalue, icTimes);
                // 创建用户线程 参数传递结构
                userSocket cLnkuser = new userSocket();
                //为客户端套接字连接开启新的线程用于接收客户端发送的数据
                cLnkuser.curSocket = sockConnection;
                cLnkuser.iposj = icposj;
                // 创建接收数据的线程
                Thread t = new Thread(RecMsg);
                //设置为后台线程
                t.IsBackground = true;
                //为客户端连接开启线程
                t.Start((object)cLnkuser);
            }
        2),接收处理客户端数据的线程
            这一部分是每个项目的核心,不同的项目处理的方式方法也可能不同,本项目处理方式如下。
        /// 接收客户端信息的线程部分代码
        void RecMsg(object o)
        {
            userSocket curStruct = (userSocket)o;
            Socket socketClient = curStruct.curSocket;
            int icurposj = curStruct.iposj;
            Boolean loopRecvblv = true;
            //创建一个字节数组接收数据
            byte[] arrMsgRec = new byte[READBUFFERSIZE];
            byte[] recArrFastReply = new byte[256];
            // 通过在线用户管理,协调控制数据的接收与发送
            onLineUserManager.checkSetOnlineUser(icurposj, SET_RECEIVE_RUN);// 设置接收运行
            onLineUserManager.TaskRecReplyCtrl(icurposj, REPLY_INIT_SET);// 任务接收初始设置
            Boolean crecLockblv = false;// 接收数据锁保护
            int iheartBeatTimenum = 0;// 心跳包数
            onLineUserManager.checkResetThreadLoopWait();
            while (loopRecvblv)
            {
                // 设置循环等待参数,可根据在线用户数自动调整
                Thread.Sleep(onLineUserManager.mThreadLoopSpaceMisec);
                // 检查用户是否在线
                if (!onLineUserManager.checkUserIfOnlineblv(icurposj)) break;// 用户离线
                if (crecLockblv)// 一项数据接收完前循环等待
                {
                    Thread.Sleep(ConstStringIntDataClass.CYBERLOOPWAIT_RECEIVE);
                    continue;
                }
                // 等待快速回应
                if (onLineUserManager.TaskRecReplyCtrl(icurposj, REPLYOPER_ITEM) != REPLY_EMPTY)
                {// reply user task 
                    if (onLineUserManager.TaskRecReplyCtrl(icurposj, REPLYOPER_STATUS) != REPLY_FINISH)
                    {
                        Thread.Sleep(20);
                        continue;
                    }
                    crecLockblv = true;// 接收控制锁
                    int length = socketClient.Receive(recArrFastReply);
                    if (length >= 10)// 接收回应数据
                    {
                         // 检查处理回应的数据
                         //  。。。。。。。   
                    }
                    crecLockblv = false;// 数据接收完成开锁
                    continue;
                }
                // 正常等待接收数据
                crecLockblv = true;// 数据接收锁
                try
                {
                    //接收数据
                    mReceiveSumn = 0;// 重置本次接收的字节数
                    int length = socketClient.Receive(arrMsgRec);
                    if (length > 0)// 接收到数据
                    {
                        int icgLen = length;
                        if (icgLen > 20) icgLen = 20;
                        // 检查是否是数据头
                        int iretcd = mglobal_checkDataHead(arrMsgRec, length);
                        if (iretcd == DATAKIND_ERROR){// 不是合法数据
                             crecLockblv = false;
                             continue;
                        }
                        if (iretcd == DATAKIND_HEARTBEAT)
                        {// 接收到心跳包
                            onLineUserManager.checkSetOnlineUser(icurposj, APPENDOPER_HEARTBEAT);
                            crecLockblv = false;
                            continue;
                        }
                        // 数据一次接收完成
                        if (iretcd == DATAKIND_FINISH)
                        {
                           // 处理完成的接收数据
                           // 。。。。
                            crecLockblv = false;
                            continue;
                        }
                        // 数据接收未完成
                            try
                            {
                                while (true)
                                {// 继续读入数据
                                    length = socketClient.Receive(arrMsgRec);
                                     // 添加接收的数据,并检查是否接收完成,接收完成后,退出
                                     // 。。。 
                                }
                                // 处理接收的数据
                                crecLockblv = false;
                            }
                            catch (Exception e)
                            {
                                // 用户掉线或离开!
                                loopRecvblv = false;
                                break;
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    // 用户掉线或离开!
                    loopRecvblv = false;
                    break;
                }
                // 无数据或数据处理完成,继续循环等待
                crecLockblv = false;
            }
            onLineUserManager.checkSetOnlineUser(icurposj, SET_RECEIVE_STOP);// 设置停止接收数据
            onLineUserManager.removeOnlineUser(icurposj);
            //关闭套接字,退出监听
            socketClient.Close();
            //结束当前线程
            Thread.CurrentThread.Abort();
            arrMsgRec = null;
        }


       3), 数据的发送
          项目数据的发送,依据客户端的请求不同,具体的处理流程也是不同的,这里只给处最底层的部分代码。
       

public void bs60Send(Socket socket, byte[] buffer, int offset, int size, int timeout, int onLineposj)
        {
            int startTickCount = Environment.TickCount;
            int sent = 0;  // 已经发送的字节数
            do
            {
                if (Environment.TickCount > startTickCount + timeout)
                    throw new Exception("发送超时");
                try
                {
                    sent += socket.Send(buffer, offset + sent, size - sent, SocketFlags.None);
                }
                catch (SocketException ex)
                {
                    if (ex.SocketErrorCode == SocketError.WouldBlock ||
                        ex.SocketErrorCode == SocketError.IOPending ||
                        ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
                    {
                        // 缓冲区可能已满,等待并重试
                        Thread.Sleep(30);
                    }
                    else
                        throw ex;  // 发生任何严重错误
                }
            } while (sent < size);
            if (sent >= size)
            {
                  // 发送完成
            }
            else
            {
                  //  发送失败!
            }
        }


     (二),PC客户端的邮箱验证
       网络服务中,对于PC端的用户通常采用邮箱验证方式,在此提供部分代码供参考。
      进行邮箱验证,首先要选择一个已经开通了SMTP功能的电子邮箱,项目中使用的163的邮箱。
       

 public static string Email163SmtpSend(string senderName, string toEmail, string copyEmail, string emailTitle, string emailBody)
        {
            String fromEmail = "xxxxxx@163.com";// 
            string smtpServer = "smtp.163.com";
            string smtpasswd = "h163126";
            MailMessage msg = new MailMessage();//邮件信息类
            msg.From = new MailAddress(fromEmail, senderName, Encoding.UTF8);//邮箱,发件人,编码
            msg.To.Add(toEmail);//收件人,可以循环添加收件人
            if (copyEmail.Length > 6)
            {
                msg.CC.Add(copyEmail);//抄送人,同上
            }
            msg.Subject = emailTitle;//邮件主题
            msg.BodyEncoding = Encoding.UTF8;//邮件内容编码
            msg.SubjectEncoding = Encoding.UTF8;//邮件主题编码
            msg.IsBodyHtml = false;//是否是html格式
            msg.Body = emailBody;
            msg.Priority = MailPriority.High;//邮件优先级

            SmtpClient client = new SmtpClient();// 客户端smtp
            client.DeliveryMethod = SmtpDeliveryMethod.Network;//指定邮件发送方式为网络
            client.Host = smtpServer;// 指定服务器
            client.EnableSsl = false;//服务器支持安全连接,安全为true
            client.UseDefaultCredentials = false;//是否随着请求一起发
            client.Credentials = new NetworkCredential(fromEmail, smtpasswd);//用户名密码
            string strInforet = "";
            try
            {
                client.Send(msg);
                strInforet = "发送成功!";
            }
            catch (Exception)
            {
                strInforet = "发送失败!";
            }
            return strInforet;
        }


      (三),移动端的手机验证
          网络服务中的手机验证码发送,是通过“验证服务手机”的专用APP,协同网络服务端手机验证程序完成的。架构是服务端设置一个专用网络数据接口,启动专用 监听程序,处理验证服务手机的登录。移动端开发了一个专用的连网登录及接收验证发送任务的APP程序。
     1), C# 网络端手机验证码发送的部分代码
     这部分就是向已经通过端口连接的验证服务手机,发送服务端生成的手机验证码相关数据(接收验证码的手机号及验证码)
 

   public class VerifyPhone
    {
        // 保存验证用手机的相关信息:套接字与接收缓存
        public Socket socket;
        public byte[] Rcvbuffer;
        //实例化方法
        public VerifyPhone(Socket s)
        {
            socket = s;
        }
        //清空接受缓存,新的接收之前要调用该方法
        public void ClearBuffer()
        {
            Rcvbuffer = new byte[1024];
        }
        //
        public void Dispose()
        {
            try
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }
            finally
            {
                socket = null;
                Rcvbuffer = null;
            }
        }
    }
    // 验证服务程序
    class SocketServerVerifyClass
    {
        // 监听的套接字
        TcpListener listener;
        // 是否启动连接了发送验证码的手机
        bool IsVerifyStart = false;
        public List<VerifyPhone> phoneLst = new List<VerifyPhone>();
        // 设置启动端口监听
        public void Start(string strIPaddress, int intPort)
        {
            if (IsVerifyStart)
                return;
            //服务器启动监听
            IPEndPoint localep = new IPEndPoint(IPAddress.Parse(strIPaddress), intPort);
            listener = new TcpListener(localep);
            listener.Start(10);
            IsVerifyStart = true;
            AsyncCallback callback = new AsyncCallback(AcceptCallBack);
            listener.BeginAcceptSocket(callback, listener);
        }
        private void AcceptCallBack(IAsyncResult ar)
        {
            try
            {
                // 完成异步接收连接请求的异步调用
                // 连接信息添加到手机列表
                Socket handle = listener.EndAcceptSocket(ar);
                VerifyPhone vphone = new VerifyPhone(handle);
                phoneLst.Add(vphone);
                AsyncCallback callback;
                // 调用异步方法接收连接请求
                if (IsVerifyStart)
                {
                    callback = new AsyncCallback(AcceptCallBack);
                    listener.BeginAcceptSocket(callback, listener);
                }
                // 连接上进行异步的数据接收
                vphone.ClearBuffer();
                callback = new AsyncCallback(ReceiveCallback);
                vphone.socket.BeginReceive(vphone.Rcvbuffer, 0, vphone.Rcvbuffer.Length, SocketFlags.None, callback, vphone);
            }
            catch
            {
                // 在调用引发异常,套接字Listener被关闭,设置为未启动侦听状态
                IsVerifyStart = false;
            }
        }
        private void ReceiveCallback(IAsyncResult ar)
        {
            VerifyPhone vphone = (VerifyPhone)ar.AsyncState;
            try
            {
                int i = vphone.socket.EndReceive(ar);
                if (i == 0)
                {
                    RemoveClient(vphone);
                    return;
                }
                else
                {
                    vphone.ClearBuffer();
                    AsyncCallback callback = new AsyncCallback(ReceiveCallback);
                    vphone.socket.BeginReceive(vphone.Rcvbuffer, 0, vphone.Rcvbuffer.Length, SocketFlags.None, callback, vphone);
                }
            }
            catch
            {

            }
        }

        public void SendData(VerifyPhone vphone, string data)
        {
            byte[] byteAr = Encoding.UTF8.GetBytes(data);
            try
            {
                AsyncCallback callback = new AsyncCallback(SendCallback);
                frd.socket.BeginSend( byteAr, 0,  byteAr.Length, SocketFlags.None, callback,vphone);
            }
            catch(SocketException ex) {
            }

        }
        private void SendCallback(IAsyncResult ar)
        {
            VerifyPhone vphone = (VerifyPhone)ar.AsyncState;
            vphone.socket.EndSend(ar);

        }

        private void RemoveClient(VerifyPhone vphone)
        {
            foreach (VerifyPhone curphone in phoneLst)
            {
                if (curphone == vphone)
                {
                    phoneLst.Remove(vphone);
                }
            }
        }
        public void RemoveAllClient()
        {
            foreach (VerifyPhone curphone in phoneLst)
            {
                 phoneLst.Remove(curphone);
            }

        }
        public void Stop()
        {
            if (!IsVerifyStart)
                return;
            listener.Stop();
            IsVerifyStart = false;
        }
    }


    // 启动运行 验证码服务程序
    SocketServerVerifyClass verifyServer = new SocketServerVerifyClass();
    // 启动服务监听
    string strIPaddress = getIP();// 得到IP地址
    string verifyPort = "8805";// 专用端口
    verifyServer.Start(strIPaddress, int.Parse(verifyPort));
    // 验证码的发送处理过程
    服务端收到手机验证申请后
    A,自动生成一个随机手机验证码;
    B,检查是否连接了验证码发送手机,没有则向申请注册手机发送现在不能注册的提示;
    C,构建验证码发送数据:string strSendSMS = "bs60SMSPhone:" + 手机号 + "," + 验证码;
    D,通过运行验证服务程序(verifyServer) 发送验证码到验证服务手机
         verifyServer.SendData((VerifyPhone)phoneLst[0], strSendSMS);
    E,同时也向申请验证的手机发送验证码(注,手机的验证是在移动端完成的)。
    2),验证服务手机的验证码接收及发送APP
        任何一部安装安装了Android“手机验证码发送服务APP”的手机,都可以成为“验证服务手机”。
        在手机端同样通过Socket进行网络数据接收与发送。发送部分是保持活动在线的心跳包。下面是java语言的部分代码。

        // 网络连接
private void doConnect() {
        //子线程请求网络
        new Thread(new Runnable() {
            @Override
            public void run() {
                String strname = "服务器连接....";
                try {
                    msocket = new Socket();
                    // 用服务端IP地址和端口号,建立Socket连接
                    String serverIP = "192.168.1.188";// 服务器端IP
                    String serverPort = "8805";// 服务器端口号
                    int icport = Integer.valueOf(serverPort);
                    msocket.connect(new InetSocketAddress(serverIP, icport), 5 * 1000);
                    bos = new BufferedOutputStream(msocket.getOutputStream());
                    // 启动监听,读取网络数据
                    readThread = new ReadThread();
                    readThread.start();
                    strname = "服务器连接成功!";
                    isconnectBoolean=true;
                   // 启动心跳包
                   new Thread(new KeepAliveHeartBeat()).start();
                } catch (Exception e) {
                    e.printStackTrace();
                    strname = "服务器连接连接失败!";
                }
                Message msg = new Message();
                byte[] data = strname.getBytes();
                msg.obj = data;
                msg.what = SHOW_OPERINFO;// 显示操作信息
                mHandler.sendMessage(msg);
            }
        }).start();
    }
    //启动一个线程,一直读取从服务端发送过来的消息
    private class ReadThread extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    BufferedInputStream bufferedInput = new BufferedInputStream(msocket.getInputStream());
                    if (bufferedInput.available() > 0) {
                        int len = bufferedInput.available();
                        byte[] data = new byte[len];
                        int nowbyten =bufferedInput.read(data);
                        Message msg = new Message();
                        msg.obj = data;
                        msg.what = CHECK_DATA;// 检查接收到的数据,自动发送验证码
                        mHandler.sendMessage(msg);
                    }
                }catch (IOException e){

                }
            }
        }
    }
    // 维持在线的心跳包
    class KeepAliveHeartBeat implements Runnable{

        long checkDelay = 10;
        long keepAliveDelay = 2000;
        int iheratBreakErrNum = 0;
        int iheratBreakNormalNum = 0;
        public void run() {
            cKeepAliveblv = true;
            while(!isconnectBoolean){// wait connect,...
                try {
                    Thread.sleep(checkDelay);
                }catch(InterruptedException e){

                }
            }
            while(isconnectBoolean){
                if(System.currentTimeMillis()-lastSendTime>keepAliveDelay && sendLockblv == false){
                    sendLockblv = true;//
                    try {
                        bos.write("bs60HEARTBEAT".getBytes());
                        //要flush一下
                        bos.flush();
                        iheratBreakErrNum = 0;
                        iheratBreakNormalNum++;
                    } catch (IOException e) {
                        e.printStackTrace();
                        iheratBreakErrNum++;
                    }
                    lastSendTime = System.currentTimeMillis();
                    sendLockblv = false;
                    if(iheratBreakErrNum > 3){
                        break;
                    }
                }else{
                    try {
                        Thread.sleep(checkDelay);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if(iheratBreakNormalNum >= 4){
                    iheratBreakNormalNum = 0;
                    Message msg = new Message();
                    msg.obj = "...";
                    msg.what = SERVER_NORMAL;// 显示网络连接正常的信息
                    mHandler.sendMessage(msg);
                }
            }
            if(iheratBreakErrNum > 3){
                Message msg = new Message();
                msg.obj = "Server Break";
                msg.what = SERVER_BREAK;// 处理服务器中断
                mHandler.sendMessage(msg);
            }
            cKeepAliveblv = false;
        }
    }


    // 发送SMS
      要从验证手机向其它手机发送手机验证码,需要得到发送SMS权限。此类代码网上比较多,这里就不提供了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

newstart60

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

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

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

打赏作者

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

抵扣说明:

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

余额充值