循序渐进做项目系列(3):迷你QQ篇(1)——实现客户端互相聊天

《循序渐进做项目系列迷你QQ篇》将陆续介绍客户端聊天,文件传输,加好友,群聊,包括语音聊天,视频聊天,远程桌面等等需求如何实现,感兴趣的朋友可以持续关注。考虑到某些需求较为复杂,本系列采用成熟的通信框架ESFramework来做,而不是从socket做起,当然这与本人才疏学浅也有莫大的关系,如果大家不嫌弃小弟写得太“low”,还请捧个人场,顺便给予鼓励!

言归正传,今天就是要实现一个最简单的功能:客户端互相聊天。

一·部署通信设备

参见 循序渐进做项目系列(1):最简单的C/S程序——让服务器来做加法

        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            IRapidPassiveEngine clientEngine = RapidEngineFactory.CreatePassiveEngine();//创建客户端通信引擎
            ClientHandler clientHandler = new ClientHandler();//创建客户端消息处理器  
            Form_Login loginForm = new Form_Login(clientEngine, clientHandler);
            loginForm.ShowDialog();//登陆窗会话完成才能运行主窗体
            Form_Client clientForm = new Form_Client(clientEngine, loginForm.SelfUserID);
            clientHandler.ClientForm = clientForm;
            Application.Run(clientForm);
        }

二·登陆窗设计

    public partial class Form_Login : Form
    {
        private IRapidPassiveEngine clientEngine; 

        private  ClientHandler clientHandler;

        public string SelfUserID//本人用户ID,该属性主要是为了暴露给主窗体用来标识用户
        {                       get { return this.textBox_UserID.Text; }
        }

        public Form_Login(IRapidPassiveEngine _clientEngine, ClientHandler _clientHandler)
        {
            InitializeComponent();
            //传入通信引擎和消息处理器主要是为了登陆的时候进行通信引擎初始化操作
            this.clientEngine = _clientEngine;
            this.clientHandler = _clientHandler;
        }

        private void button_login_Click(object sender, EventArgs e)
        {
            //通信引擎初始化,与目标服务器建立TCP连接
              LogonResponse logonResponse = this.clientEngine.Initialize(this.textBox_UserID.Text, this.textBox_Password.Text, "127.0.0.1", 2000, clientHandler);
              if (logonResponse.LogonResult == LogonResult.Succeed)
              {
                  this.DialogResult = DialogResult.OK;
              }
              else
              {
                  MessageBox.Show("登陆失败!" + logonResponse.LogonResult.ToString() + logonResponse.FailureCause);
              }
        }
    }

三·客户端主窗体设计

    public partial class Form_Client : Form
    {
        private IRapidPassiveEngine clientEngine;

        private string selfUserID;

        public Form_Client(IRapidPassiveEngine _clientEngine,string _selfUserID)
        {
            this.clientEngine = _clientEngine;
            this.selfUserID = _selfUserID;
            this.Text = _selfUserID;//在窗体上显示本用户ID
            InitializeComponent();
        }

        private void button_Send_Click(object sender, EventArgs e)
        {
            string msg = this.textBox_Input.Text;//聊天消息
            Byte[] msgCode = Encoding.UTF8.GetBytes(msg);//转码以备发送
            string targetUserID = this.textBox_TargetUserID.Text;//从输入框读取待发送用户ID
          //利用通信引擎发送消息
            this.clientEngine.CustomizeOutter.Send(targetUserID, InformationType.Chat, msgCode);
            this.ShowChatMsg(this.selfUserID, msg);
        }
        //将聊天消息按一定格式显示在界面上
        public void ShowChatMsg(string UserID, string msg)
        {
            this.richTextBox_Output.AppendText(UserID + "  " + DateTime.Now.ToString() + "\n");
            this.richTextBox_Output.AppendText(msg + "\n");
            this.richTextBox_Output.ScrollToCaret();
            this.textBox_Input.Text = "";
        }
    }

四·客户端收到消息后进行处理

 public class ClientHandler: ICustomizeHandler
    {
        private Form_Client clientForm;

        //该属性是为了在外部将clientForm注入,之所以不在构造函数中传入是因为当时clientForm尚未创建
        public Form_Client ClientForm
        {
            get { return clientForm; }
            set { clientForm = value; }
        }

        public void HandleInformation(string sourceUserID, int informationType, byte[] info)
        {
            if (informationType == InformationType.Chat)
           {
               if (this.clientForm.InvokeRequired) //不是主线程时
               {
                   //转换为主线程
                   this.clientForm.Invoke(new ESBasic.CbGeneric<string, int, byte[]>(this.HandleInformation), sourceUserID, informationType, info);
               }
               else
               {
                  string msg = Encoding.UTF8.GetString(info);//解析消息
                  this.clientForm.ShowChatMsg(sourceUserID, msg);//显示消息
               }
           }
        }

        public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info)
        {
            throw new NotImplementedException();
        }

五·总结

 源码下载:MiniQQ1.0.zip

由于代码中尽量做到规范命名,并做了详细的注释,所以没有再花更多的文字段落去说明,相信大家也能很容易看懂。整个逻辑十分简单,对于新手有两点需要注意一下:

1.各个类之间通过参数传递从而完成合作,传参方式有的是通过构造函数,有的是通过属性注入,新手朋友们可以自己比较一下两种方式的适用场合。

2.其中有一处重构,就是聊天的时候既要显示自己发的消息又要显示收到的消息,这时候代码基本上是一样的,所以我将其重构了。至于什么时候需要重构,如何重构,新手朋友们可以自己多多揣摩。

这两点都是平时做项目中经常遇到的问题,是需要熟练掌握的。

当然,最重要的一点还是循序渐进做项目咯!做的多了这些东西自然就会熟练了,写程序时就能够下笔如有神了!

时间: 2024-07-31 19:33:08

循序渐进做项目系列(3):迷你QQ篇(1)——实现客户端互相聊天的相关文章

循序渐进做项目系列:最简单的C/S程序——让服务器来做加法

(本文是专门针对未接触过C/S开发的初学者而写的,C/S开发高手请自动忽略啊~~) 还在写“Hello world!”式的单机程序吗?还在各种拖控件吗?是否自己都觉得有点low呢?来个质的飞跃吧!看看怎么让服务器帮咱做加法! 所谓C/S程序就是Client/Server程序,自然既包含一个Client项目,又包含一个Server项目.所以先来新建解决方案及项目. 一·开端 1.新建解决方案及项目 (针对初学者,已掌握者跳过) 1.新建一个Windows窗体项目,项目命名为Server. 2.右击

安装Git服务&amp;使用Gitosis管理成员、项目并做项目授权(上)

转自:安装Git服务&使用Gitosis管理成员.项目并做项目授权 这是一篇实际的操作文档,里面的大部分图都是自己亲手截屏,力求达到真实并希望帮助到更多的遇到类似问题的困惑者.如果遇到问题,可以直接在文章最下面联系到我,也欢迎拍砖. 弄清楚关键词眼与概念 裸仓库(bare repository) 即一个没有当前工作目录的仓库.因为该仓库只是一个合作媒介,所以不需要从硬盘上取出最新版本的快照:仓库里存放的仅仅是 Git 的数据.简单地说,裸仓库就是你工作目录中 .git 子目录内的内容. 补充一下

为什么你做不好互联网项目,看完这篇就懂了

大多IT从业者工作久了,会有一种迷茫, 渐渐的感觉自己提升空间越来越小了. 项目团队上下都做的很累, 这样的团队我们遇到太多了, 老板很愁很操心,技术leader或项目经理压力超大, 开发者也迷茫,上下都有严重的问题. 为什么会出现这种原因呢? 根据我们多年的项目实战经历, 问题出在整个团队缺乏对项目实战经验的掌握和学习, 也缺乏真正懂项目的人, 一个人要真正懂项目理解项目, 不是考个证就能懂的, 需要大量项目全程实战经验,才能真正懂. 这个代价有多高. 如果一个100万的小项目给你玩,5个项目

【转】手摸手,带你用vue撸后台 系列二(登录权限篇)

前言 拖更有点严重,过了半个月才写了第二篇教程.无奈自己是一个业务猿,每天被我司的产品虐的死去活来,之前又病了一下休息了几天,大家见谅. 进入正题,做后台项目区别于做其它的项目,权限验证与安全性是非常重要的,可以说是一个后台项目一开始就必须考虑和搭建的基础核心功能.我们所要做到的是:不同的权限对应着不同的路由,同时侧边栏也需根据不同的权限,异步生成.这里先简单说一下,我实现登录和权限验证的思路. 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到t

【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 (13)客户管理

声明:本系列为原创,分享本人现用框架,未经本人同意,禁止转载!http://yuangang.cnblogs.com 希望大家好好一步一步做,所有的技术和项目,都毫无保留的提供,希望大家能自己跟着做一套,还有,请大家放心,只要大家喜欢,有人需要,绝对不会烂尾,我会坚持写完~ 如果你感觉文章有帮助,点一下推荐,让更多的朋友参与进来,也是对本人劳动成果的鼓励,谢谢大家!由于还要工作,所以基本都是牺牲午休时间来写博客的,写博客呢不是简单的Ctrl+C.Ctrl+V,我是要挨着做一遍的,这也是对大家负责

【转】JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐(二)

前言:上篇 JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐 分享了几个项目中比较常用的组件,引起了许多园友的关注.这篇还是继续,因为博主觉得还有几个非常简单.实用的组件,实在不愿自己一人独享,没办法,谁让博主这么爱分享呢~~ 本文原创地址:http://www.cnblogs.com/landeanfen/p/5603790.html 七.多值输入组件manifest 关于文本框的多值输入,一直是一个比较常见的需求,今天博主推荐一款好用的多值输入组件给大家,不要谢我,请叫我“红

基础教程系列之装系统篇

[科普]基础教程系列之装系统篇 鉴于每天有很多新人询问安装系统问题,我决定花一些时间来系统性的整理一下安装系统的步骤,带领新人一步步来安装好系统. 目录 0.前言 1.物质准备 2.制作启动盘 3.选择自己需要的系统 4.其他准备工作 5.进入win pe 6.举例进入win pe 7-1.确保正确安装系统的准备工作 7-2.确保正确安装系统的准备工作 8-1.使用原版镜像安装系统 8-2使用ghost安装系统 8-3使用nt6 hdd installer安装系统 9.重启进入操作系统 10.安

SSM框架开发web项目系列(二) MyBatis真正的力量

前言 上篇SSM框架环境搭建篇,演示了我们进行web开发必不可少的一些配置和准备工作,如果这方面还有疑问的地方,可以先参考上一篇“SSM框架开发web项目系列(一) 环境搭建篇”.本文主要介绍MyBatis的基础内容,包括基本概念.开发步骤.使用实例等.说起MyBatis,工作中做过SSH/SSM相关Web开发的或者正在学习MyBatis的人或多或少都会接触到类似“MyBatis和Hibernate有什么区别?”,“MyBatis和Hibernate哪个更好?”,“为什么Mybatis用的人越来

spring-boot-2.0.3不一样系列之源码篇 - SpringApplication的run方法(一)之SpringApplicationRunListener,绝对有值得你看的地方

前言 Springboot启动源码系列还只写了一篇,已经过去一周,又到了每周一更的时间了(是不是很熟悉?),大家有没有很期待了?我会尽量保证启动源码系列每周一更,争取不让大家每周的期望落空.一周之中可能会插入其他内容的博文,可能和springboot启动源码有关,也可能和启动源码无关. 路漫漫其修远兮,吾将上下而求索! github:https://github.com/youzhibing 码云(gitee):https://gitee.com/youzhibing 前情回顾 这篇是在spri