微信平台的模拟实现(2)---客户端开发

上一篇我们简单的分析了微信平台的实现原理,由于博主的表达比较欠缺,说的比较简单,可能看得不是很明白,不过随着一步步进行或者查看源码,大家就会清楚的明白了。

好了,不废话,这一篇我们先来实现客户端。

客户端的灵魂是基于socket实现即时通讯,消息的收发都要通过它。

为了看起累直观明了,我们为这个客户端包装一下,也就是加个界面,让它看起来更像一个客户端。

本文用的是wpf来做的界面,当然也可以以用winform窗体,看个人了。

本客户端主要有以下几个界面:

文件结构如下:

1.登录/注册页面,为了简单,点击登陆就获取对应的账号密码登录,点击注册用的也是同样的输入框。

2.登陆后的界面,底部有三个导航按钮,默认显示的是会话历史记录页面

3.联系人页面

4.“发现”页面,用于搜索公众号或者用户的界面

下面主要来看一下客户端核心代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows;

namespace wechat
{
    class Client
    {
        private static Interface_client client;
        private static int port = 8885;
        private static string serverIP = "127.0.0.1";
        private static Socket clientSocket;
        private static byte[] recResult = new byte[1024];
        private static Thread mainthread,recivethread;
        private static bool connected = false;

        public Client(int port, string serverIP, Interface_client client)
        {
            Client.port = port;
            Client.serverIP = serverIP;
            Client.client = client;
        }

        public Client( Interface_client client)
        {
            Client.client = client;
        }

        public void Start()
        {
            mainthread = new Thread(start);
            mainthread.IsBackground = true;
            mainthread.Start();
        }

        public void Stop()
        {
            mainthread.Abort();
        }

        public void Send(string content)
        {
            try { clientSocket.Send(Encoding.UTF8.GetBytes(content)); }
            catch { }
        }

        private void start()
        {

            //GetIPAndPort();
            IPAddress ip = IPAddress.Parse(serverIP);
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ////是客户端,同时也是服务器
            //clientSocket.Bind(new IPEndPoint(ip, port));
            //clientSocket.Listen(20);

            ConnectToServer(ip,client);
        }
        /*
         *
         * 从输入获取ip和端口号
         * */
        private static void GetIPAndPort()
        {
            Console.WriteLine("enter the IPAddress ,default is :{0}", serverIP);
            GetAndCheckIpAddress();//判断输入的字符串是否为ip

            Console.WriteLine("enter the PORT ,default is :{0}", port);
            string tempPORT = Console.ReadLine();
            if (tempPORT.Length > 0)
                port = Convert.ToInt32(tempPORT);
        }
        //接入服务器
        private static void ConnectToServer(IPAddress ip, Interface_client client)
        {
            try
            {
                clientSocket.Connect(new IPEndPoint(ip, port));
                Console.WriteLine("connect to the server successful!");
                connected = true;
                clientSocket.Send(Encoding.UTF8.GetBytes("newconnection|"+Model.username));
                //开启消息和接收消息的线程
                //client.getMessage(clientSocket);
                recivethread = new Thread(RecMessage);
                recivethread.IsBackground = true;
                recivethread.Start(client);
            }
            catch (Exception e)
            {
                connected = false;
                MessageBox.Show("与连接失败......");
                Console.WriteLine(e.Message);
                //GetIPAndPort();
                Thread.Sleep(30000);
                MessageBox.Show("正在尝试重新连接......");
                ConnectToServer(ip,client);
            }
        }
        private static void SendMessage()
        {
            while (true)
                try { clientSocket.Send(Encoding.UTF8.GetBytes(Console.ReadLine())); }
                catch { }

        }

        private static void RecMessage(Object o)
        {
            Interface_client client = o as Interface_client;
            while (true)
            {
                try
                {
                    client.getMessage(clientSocket);
                    //int recLength = clientSocket.Receive(recResult);
                    //getMessage(Encoding.UTF8.GetString(recResult, 0, recLength));
                    //Console.WriteLine("recive message from {0} : {1}",
                    //    clientSocket.RemoteEndPoint.ToString(),
                    //    Encoding.UTF8.GetString(recResult, 0, recLength));
                }
                catch (Exception e)
                {
                    connected = false;
                    //Console.WriteLine(e.Message);
                    MessageBox.Show("与服务器断开连接......");
                    //clientSocket.Shutdown(SocketShutdown.Both);
                    //clientSocket.Close();
                    Thread.Sleep(30000);
                    MessageBox.Show("正在尝试重新连接......");
                    //ConnectToServer(IPAddress.Parse(serverIP), client);
                    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    while (!connected)
                    {
                        try
                        {
                            clientSocket.Connect(new IPEndPoint(IPAddress.Parse(serverIP), port));
                            Console.WriteLine("connect to the server successful!");
                            connected = true;
                            clientSocket.Send(Encoding.UTF8.GetBytes("newconnection|" + Model.username));
                            //开启消息和接收消息的线程
                            //client.getMessage(clientSocket);
                            recivethread = new Thread(RecMessage);
                            recivethread.IsBackground = true;
                            recivethread.Start(client);
                        }
                        catch (Exception e1)
                        {
                            MessageBox.Show("与连接失败......");
                            Console.WriteLine(e1.Message);
                            //GetIPAndPort();
                            Thread.Sleep(30000);
                            MessageBox.Show("正在尝试重新连接......");

                        }
                    }
                    break;
                }
            }
        }

        private static void GetAndCheckIpAddress()
        {
            string tempIP = Console.ReadLine();
            if (tempIP.Length <= 0) return;
            if (IsIpAddress(tempIP))
            {
                serverIP = tempIP;
            }
            else
            {
                Console.WriteLine("the IP you‘ve enter is not right,please enter again ,default is :127.0.0.1");
                GetAndCheckIpAddress();
            }
        }
        private static bool IsIpAddress(string str)
        {
            string[] strings = str.Split(‘.‘);
            if (strings.Length != 4) return false;
            else
            {
                for (int i = 0; i < 4; i++)
                {
                    string tempstring = strings[i];
                    if (tempstring == "") return false;
                    int temp = Convert.ToInt32(tempstring);
                    if (temp >= 0 && temp <= 255) continue;
                    else return false;
                }
            }
            return true;
        }
    }

}

这个是socket客户端类Client,这个类开了一个专门接收消息的接口,实际用的时候要传入实现了 interface_client 接口的类的对象

使用接口是为了能够根据情况的不懂,对接收到的数据进行不同的解析方式,而且不会乱。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Windows;

namespace wechat
{
    class MyClient : Interface_client
    {
        public delegate void ReciveMessageHandle(string content);
        public static ReciveMessageHandle ReciveMessage;

        private static byte[] recResult = new byte[2048];
        string getdata;
        void Interface_client.getMessage(System.Net.Sockets.Socket clientSocket)
        {
            //while (true)
            //{
                try
                {
                    int recLength = clientSocket.Receive(recResult);
                    getdata = Encoding.UTF8.GetString(recResult, 0, recLength);
                    if (getdata.Length > 0)
                        ReciveMessage(getdata);
                        //MessageBox.Show(getdata);
                    //getMessage(Encoding.UTF8.GetString(recResult, 0, recLength));
                    //Console.WriteLine("recive message from {0} : {1}",
                    //    clientSocket.RemoteEndPoint.ToString(),
                    //    Encoding.UTF8.GetString(recResult, 0, recLength));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    clientSocket.Shutdown(SocketShutdown.Both);
                    clientSocket.Close();
                    //break;
                }
            //}
        }
    }
}

实现了interface_client 接口的类 MyClient ,实现了具体接收数据的方法,这里仅简单的接收字符串。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BaseSqlHelper;
using System.Data.SqlClient;
using System.Data;
using System.Xml;
using System.IO;

namespace wechat
{
    class DBTool
    {
        static SqlHelper helper = new SqlHelper(@"Data Source=MENG\SQLEXPRESS;Initial Catalog=wechat;Integrated Security=True");

        public static int register(string username,string pwd){
           try{ return helper.ExecuteNonQuery("insert into users(username,pwd,usertype) values (@username,@pwd,@usertype)",
                new SqlParameter("@username", username), new SqlParameter("@pwd", pwd), new SqlParameter("@usertype", "0"));
           }
            catch{return 0;}
        }

        public static int login(string username,string pwd){
           try{ return (int)helper.ExecuteScalar("select count(1) from users where [email protected] and pwd = @pwd",
                new SqlParameter("@username", username), new SqlParameter("@pwd", pwd));
           }
            catch{return 0;}
        }

        public static DataTable search(string username, string findusername)
        {
            return helper.GetDataSet(@"select * ,type = case when usertype=‘0‘ then ‘普通用户‘ else ‘公众号‘ end
                                    from users where username != @username and username like ‘%‘[email protected]+‘%‘", new SqlParameter("@username", username),
                                                                                                 new SqlParameter("@findusername", findusername)).Tables[0];
        }

        public static DataTable getuserinfo(string username,string viewusername)
        {
            return helper.GetDataSet(@"select * ,type = case when usertype=‘0‘ then ‘普通用户‘ else ‘公众号‘ end ,
                (select COUNT(1) from friend where [email protected] and [email protected]) isfriend
                from users  where username [email protected]", new SqlParameter("@username", username), new SqlParameter("@viewusername", viewusername)).Tables[0];
        }

        public static int addfriend(string username, string viewusername)
        {
            try
            {
                return helper.ExecuteNonQuery("insert into friend(username,friendname) values (@username,@viewusername)",
                    new SqlParameter("@username", username), new SqlParameter("@viewusername", viewusername));
            }
            catch { return 0; }
        }

        public static int delfriend(string username, string viewusername)
        {
            try
            {
                return helper.ExecuteNonQuery("delete from friend where [email protected] and [email protected]",
                    new SqlParameter("@username", username), new SqlParameter("@viewusername", viewusername));
            }
            catch { return 0; }
        }

        public static DataTable getmyfriend(string username)
        {
            return helper.GetDataSet(@"select users.* ,[type] = case when users.usertype=‘0‘ then ‘普通用户‘ else ‘公众号‘ end
                        from friend  left join users on users.username = friend.friendname where friend.username = @username ",
                                    new SqlParameter("@username", username)).Tables[0];
        }

        public static int addhistdialog(string username, string viewusername)
        {
            try
            {
                return helper.ExecuteNonQuery(@"if exists (select * from histdialog where fromusername [email protected] and tousername [email protected])
                    return else insert into histdialog(fromusername,tousername) values (@fromusername,@tousername)",
                    new SqlParameter("@tousername", username), new SqlParameter("@fromusername", viewusername));
            }
            catch { return 0; }
        }

        public static DataTable gethistdialog(string username)
        {
            return helper.GetDataSet(@"
                    select users.* ,[type] = case when users.usertype=‘0‘ then ‘普通用户‘ else ‘公众号‘ end ,
                    (select count(1) from messages where messages.tousername=histdialog.tousername
                    and  messages.fromusername=histdialog.fromusername and readstate=‘0‘ )unread
                    from histdialog
                    left join users on users.username = histdialog.fromusername
                    where histdialog.tousername = @username",
                                    new SqlParameter("@username", username)).Tables[0];
        }

        public static int getunreadcount(string username)
        {
            try
            {
                return (int)helper.ExecuteScalar("select count(1) from messages where [email protected] and readstate=‘0‘",
                new SqlParameter("@username", username));
            }
            catch { return 0; }
        }

        public static int setreaded(string username,string fromusername)
        {
            try
            {
                return (int)helper.ExecuteScalar("update messages set readstate=‘1‘ where [email protected] and [email protected]",
                new SqlParameter("@username", username), new SqlParameter("@fromusername", fromusername));
            }
            catch { return 0; }
        }

        public static DataTable getmessageofdialog(string username,string fromusername)
        {
            return helper.GetDataSet(@"select * from messages where ([email protected] and fromusername [email protected]) or ([email protected] and tousername [email protected]) order by id asc",
                new SqlParameter("@username", username), new SqlParameter("@fromusername", fromusername)).Tables[0];
        }

        public static Dictionary<string ,string> getxmlstring(string xmlcontent)
        {
            DataSet dsData = new DataSet();
            Dictionary<string, string> dictionary = new Dictionary<string, string>();
            dsData.ReadXml(new XmlTextReader(new StringReader(xmlcontent)));
            DataTable dt = dsData.Tables["HH"];

            foreach (DataRow dr in dt.Rows)
            {
                foreach (DataColumn dc in dr.Table.Columns)
                {
                    string n = dc.ColumnName;
                    string value = dr[n].ToString();
                    dictionary.Add(n, value);
                }
            }
            return dictionary;
        }

    }
}

DBTool类是对数据库数据操作的类(这里需要说明的是,在实际的这类客户端开发中,并不会直接的链接到数据库读写数据,

而是通过向服务器发送请求,服务器返回比如xml或者json之类的数据,客户端再解析,我猜也因该是这样的。这里为了偷懒,就除了通讯,其它的聊天记录,联系人等直接通过数据库获得)

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

namespace wechat
{
    class Model
    {
        public static string userid = "";
        public static string username = "";
        public static string viewusername = "0";
        public static string ontalkingusername = "";
        public static Client client;

        public static void SendMessage(string content)
        {
            client.Send(Model.username + "|" + Model.viewusername + "|" + content + "|" + DateTime.Now.ToString());
        }

    }
}

这个模型类是一个静态类,里边有一些静态属性,在这里用于保存一些数据用于传递数据,这个是本文所采用的解决方案,目前也不知道有什么好的办法在wpf里边传递数据,望知道的朋友不吝赐教。

if (DBTool.login(text_username.Text, text_pwd.Password) == 1)
            {
                //MessageBox.Show("登录成功!");
                Model.username = text_username.Text;
                new MainWindow().Show();
                this.Hide();
                Model.client = new Client(new MyClient());
                Model.client.Start();
            }

客户端在用户登录成功后才开始链接到我们开发的服务器(后面会讲到),只有连接上了服务器才能进行通讯。

注意:这里的client 用静态类里的client 来保存,是为了我们在其它地方发送消息时直接使用静态类里的client 发送消息即可。

前台界面用了较多的委托事件来触发消息的提示和某些ui的刷新,这个在这里也不是一言两语说得清楚,还不清楚委托的朋友可以先了解一下再看代码,这样有利于理解。

时间: 2024-07-29 21:02:12

微信平台的模拟实现(2)---客户端开发的相关文章

微信平台的模拟实现(1)---分析实现

前些日子在家突然想看看微信公众号的开发,看了它的通讯机智后发现可以试着模拟出这样一个平台. 微信公众平台的通讯机制: 由此可以看出公众号的实现需要三大部分:客户端(用户部分,也就是微信客户端)  服务端   公众号(实际为动态网页). 而这里主要的是服务端和客户端两个部分,它们构成了“平台”,而公众号是不同的公众号持有者自己开发的. 服务端和客户端之间的通讯这里,为了实现即时通讯,我将采用基于 socket 的 tcp 通讯. (对socket还不了解的,可以百度了解学习一些,这个就不多说了,这

atitit. access token是什么??微信平台公众号开发access_token and Web session保持状态机制

atitit. access token是什么??微信平台公众号开发access_token and Web session保持状态机制 1. token机制and  session保持状态机制 1 2. access token是什么?? 1 3. 为什么需要access token 2 4. 需不需要保存access_token,如何保存??? 2 5. access_token在何时被创建 2 6. 为什么不直接使用appid保持状态 2 7. access_token的过期问题 3 8.

微信平台开发之利用本地环境加新花生壳解释做测试环境

准备工作: 1.搭建本地PHP开发环境. 2.注册并安装新花生壳. 3.注册微信公众平台帐号. 第一步:搭建本地PHP开发环境,案例使用wamp集成环境. 下载并安装集成环境确保集成环境正常运行. 第二步:注册并安装新花生壳,过程(略). 具体教程可查看http://www.oray.com/ 第三步:对开发环境做解释. 登陆花生壳做解释: 开放wamp的外部访问:教程http://hi.baidu.com/aubbll/item/8bd5a52253d3ecd7a517b685 至此,外网就可

微信平台的开发与集成

最近一直在调用微信的API,却发现一直调用不成功,纠结了好久,各方面找教程,找官方,官方里的文档也只是写得很模糊,说是按三步走. 1.申请App_ID 2.填写包名3. 获取程序签名的md5值, 这三步只要你走对了就能调通,可是大家都不知道有时候我们Eclipse调用的keystore和我们打包的keystore获取到的程序签名的md5是不一样的.而且是每一个keystore对应生成的apk的值都会不一样.我们在申请的时候填的程序签名值是正式打包的,但我们在eclipse部署上去的却是用的我们默

基于ThinkPHP3的微信平台开发_1

微信公众平台是个好东西,具体的就不说了,我直接说技术>_< 下图为目录结构一览: 微信开发 - 文件目录结构 平台功能: 此次开发的平台是面向多微信公众号.微信多公众号主(下面简称号主)的第三方管理平台.功能全部开发完毕后,号主通过注册.填写微信公众号配置(在腾讯的平台上设置,如Token.EncodingAESKey等).填写本平台配置(配置Token.EncodingAESKey等),即可实现微信公众平台大部分业务功能(可能受微信公众号类型限制). 逻辑划分: 本平台基于ThinkPHP框

微信平台ASPX高级定制开发(一):如何使用C#建立响应微信接入和自动回复的代码

微信平台不解释了,如果不了解的百度一下下,如果不会用微信,请自宫,光盘重启电脑,打开CMD输入Format C:.网上有很多针对企业级的高级定制ASPX开发,写得草草了事,很多男人干事都草草了事,这可不行,您不懂小伙伴们的心情.初学者总是希望无码,即不要太多代码就能运行起来的示例,本人保证贴出来的代码全是可运行的,代码虽然有点干瘪,但给你想象和改造的空间很大,感觉对于微信平台是真正的互联网技术改造传统行业,为什么? 找度大娘(我的QQ26959368问我,别发邮件收不到!). 本代码实现了接入微

CSDN Android客户端开发(二):详解如何基于Java用Jsoup爬虫HTML数据

本文参考链接详细介绍如何使用Jsoup包抓取HTML数据,是一个纯java工程,并将其打包成jar包.希望了解如何用java语言爬虫网页的可以看下. 杂家前文就又介绍用HTTP访问百度主页得到html的string字符串,但html的文本数据如果不经过处理就是个文本字符串没有任何效果的.所谓的浏览器就是负责将文本的html"翻译"成看到的界面.在前文有介绍,这个csdn的客户端app分首页.业界.移动.研发.程序员.云计算五大类.以业界为例,http://news.csdn.net/ 

Windows客户端开发简介(二)

一个典型的Windows客户端程序要有哪几部分构成呢?下面我会以一个国内比较流行的互联网客户端程序的基本架构来跟大家逐步展开分析,由于涉及到知识产权的问题,请大家不要问我是什么产品,当然,如果你能猜到,那我就管不着了^_^. 某视频影音互联网PC客户端产品基本架构 如上只是个粗略的分层架构图,没有更细致的划分,但是有几个地方是需要特别关注的,比如最上层的那几个部分,音视频解码引擎,UI引擎,WebKit浏览器内核,内核通信模块,日志系统. 因为音视频解码引擎和内核通信模块只是对于视频客户端和P2

【转】微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引

微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引 Senparc.Weixin.MP SDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到比较稳定的版本,这个过程中我觉得有必要整理一些思路和经验,和大家一起分享.也欢迎大家的补充! SDK还在不断优化升级中,开源项目见:https://github.com/JeffreySu/WeiXinMPSDK 微信技术交流社区:http://www.weiweihi.com/QA Senparc