[c#源码]微风IM V3.1 支持TCP通信发送图片

源码  数据库 (数据库和以前一样
没有变动)

前面有好几位朋友说希望微风IM能够支持图片的发送,于是学习了图片发送的一些相关知识,在微风IMV3.1中实现了图片的发送。

如果客户端之间P2P通道已经打通,则直接在客户端之间发送,不经过服务器。

如果P2P通道没有打通,则通过服务器转发。

效果图:

下面简单的介绍一下相关知识

在TCP 通信中,Image类本身不支持序列化。

我们想要发送的话,需要做一些变通。简单的讲,就是在序列化之前把Image类转化为二进制数据,序列化完成后,解析的时候再把二进制数据解析成Image类。

我们使用的是开源的 protobuf.net序列化器。

对IMage图片类进行包装

using System;
using System.Collections.Generic;
using System.Text;
using ProtoBuf;
using System.Drawing;
using System.IO;
using ProtoBuf;

namespace SimpleIM.Business
{
    [ProtoContract]
   public class ImageWrapper
  {
      /// <summary>
      /// 把Image对象存储为私有的字节数组
      /// </summary>
      [ProtoMember(1)]
      private byte[] _imageData;

      /// <summary>
      /// 图片名称
      /// </summary>
      [ProtoMember(2)]
      public string ImageName { get; set; }

      /// <summary>
       /// 图片对象
      /// </summary>
      public Image Image { get; set; }

      /// <summary>
      /// 私有的无参数构造函数 反序列化时需要使用
      /// </summary>
      private ImageWrapper() { }

      /// <summary>
      /// 创建一个新的 ImageWrapper类
      /// </summary>
      /// <param name="imageName"></param>
      /// <param name="image"></param>
      public ImageWrapper(string imageName, Image image)
      {
          this.ImageName = imageName;
          this.Image = image;
      }

      /// <summary>
      ///序列化之前,把图片转化为二进制数据
      /// </summary>
      [ProtoBeforeSerialization]
      private void Serialize()
      {
          if (Image != null)
          {
              //We need to decide how to convert our image to its raw binary form here
              using (MemoryStream inputStream = new MemoryStream())
              {
                  //For basic image types the features are part of the .net framework
                  Image.Save(inputStream, Image.RawFormat);

                  //If we wanted to include additional data processing here
                  //such as compression, encryption etc we can still use the features provided by NetworkComms.Net
                  //e.g. see DPSManager.GetDataProcessor<LZMACompressor>()

                  //Store the binary image data as bytes[]
                  _imageData = inputStream.ToArray();
              }
          }
      }

      /// <summary>
      /// 反序列化时,把二进制数据转化为图片对象
      /// </summary>
      [ProtoAfterDeserialization]
      private void Deserialize()
      {
          MemoryStream ms = new MemoryStream(_imageData);

          //If we added custom data processes we have the perform the reverse operations here before
          //trying to recreate the image object
          //e.g. DPSManager.GetDataProcessor<LZMACompressor>()

          Image = Image.FromStream(ms);
          _imageData = null;
      }
  }
}

修改一下用于传输聊天信息的契约类,使之包含相关的图片

using System;
using System.Collections.Generic;
using System.Text;
using ProtoBuf;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.ComponentModel;

namespace SimpleIM.Business
{
    /// <summary>
    /// 此契约类存放聊天对话消息
    /// </summary>
    [ProtoContract]
    public class NewChatContract
    {
        //用户ID
        [ProtoMember(1)]
        public string UserID { get; set; }
        //用户名
        [ProtoMember(2)]
        public string UserName { get; set; }
        //目标用户ID
        [ProtoMember(3)]
        public string DestUserID { get; set; }
        //目标用户名
        [ProtoMember(4)]
        public string DestUserName { get; set; }
        //聊天的内容,主要是文本消息
        [ProtoMember(5)]
        public string Content { get; set; }

        //发送的时间
        [ProtoMember(6)]
        public DateTime SendTime { get; set; }

         [ProtoMember(7)]
        public IList<ImageWrapper> ImageList { get; set; }

        //下面这段代码主要是为了防止列表为空,如果列表为空,不加入下面这段代码,序列化会有问题
        [DefaultValue(false), ProtoMember(8)]
        private bool IsEmptyList
        {
            get { return ImageList != null && ImageList.Count == 0; }
            set { if (value) { ImageList = new List<ImageWrapper>(); } }
        }

        public  NewChatContract()
        { }

        public NewChatContract(string userID, string userName, string destUserID, string destUserName, string content, IList<ImageWrapper> imageList, DateTime sendTime)
        {
            this.UserID = userID;
            this.UserName = userName;
            this.DestUserID = destUserID;
            this.DestUserName = destUserName;
            this.Content = content;
            this.ImageList = imageList;
            this.SendTime = sendTime;
        }

    }
}

修改ChatControl控件,添加2个按钮

并添加相关代码,具体大家可以看一下源码

需要说明的是,聊天控件使用的是RichTextBox的扩展控件,本来想是否能够提取出RichTextBox中的内容,即包含所有的文字信息和图片信息,然后以发送二进制数据的形式发送,

但是对这方面不太了解,查了好些文章,也没有完后,对这方面比较了解的朋友请指点一下。

后来使用了一个变通的方法,即把文本内容,和图片文件分开发送。

在ChatControl控件中增加了一个字典类变量

public Dictionary<string, Image> imageDict = new Dictionary<string, Image>();

当用户插入图片时,会把图片添加到此字典中。

当用户发送聊天信息时,会提取字典中的图片列表进行发送,并清空图片字典。

点击发送聊天信息时的相关代码:

 /   /控件中的图片字典
        public Dictionary<string, Image> imageDict = null;
        //图片包装类的列表
        IList<ImageWrapper> imageWrapperList = new List<ImageWrapper>();

        private void chatControl1_BeginToSend(string content)
        {
            this.chatControl1.ShowMessage(Common.UserName, DateTime.Now, content, true);

            imageDict = this.chatControl1.imageDict;

             //把控件中的图片字典,添加到图片包装类列表中
            foreach (KeyValuePair<string, Image> kv in imageDict)
            {
                ImageWrapper newWrapper = new ImageWrapper(kv.Key, kv.Value);

                imageWrapperList.Add(newWrapper);

            }
            //清除控件中图片字典的内容
            this.chatControl1.ClearImageDic();

            //从客户端 Common中获取相应连接
            Connection p2pConnection = Common.GetUserConn(this.friendID);

            if (p2pConnection != null)
            {
                NewChatContract chatContract = new NewChatContract();
                chatContract.UserID = Common.UserID;
                chatContract.UserName = Common.UserName;
                chatContract.DestUserID = this.friendID;
                chatContract.DestUserName = this.friendID;
                chatContract.Content = content;
                chatContract.SendTime = DateTime.Now;
                chatContract.ImageList = imageWrapperList;
                p2pConnection.SendObject("ClientChatMessage", chatContract);
                this.chatControl1.Focus();

                LogInfo.LogMessage("通过p2p通道发送消息,当前用户ID为"+Common.UserID+"当前Tcp连接端口号"+p2pConnection.ConnectionInfo.LocalEndPoint.Port.ToString (), "P2PINFO");

            }
            else
            {
                NewChatContract chatContract = new NewChatContract();
                chatContract.UserID = Common.UserID;
                chatContract.UserName = Common.UserName;
                chatContract.DestUserID = this.friendID;
                chatContract.DestUserName = this.friendID;
                chatContract.Content = content;
                chatContract.SendTime = DateTime.Now;
                chatContract.ImageList = imageWrapperList;
                Common.TcpConn.SendObject("ChatMessage", chatContract);
                this.chatControl1.Focus();

                LogInfo.LogMessage("服务器转发消息", "P2PINFO");
            }

        }

图片的传送是经常用的功能,在其他的程序中也经常用到,稍微改动一下,可以实现客户端拍照片,然后传送到服务器上保存等类似功能。

感谢您的光临

时间: 2024-10-12 03:52:00

[c#源码]微风IM V3.1 支持TCP通信发送图片的相关文章

Spring源码阅读:Spring如何支持各种ORM框架

为了让开发程序更容易,到现在为止,已经有很多ORM框架了,例如:JPA,JDO,Hibernate,Mybatis(之前版本是IBatis)等等.也正因为已经有这么多优秀的ORM框架,Spring团队并没有自己开发一套ORM框架,而是对这些框架都进行了支持,让这些框架在Spring环境下可以得到完全的应用. 通常,在Spring环境下使用这些ORM框架时,都会通过一个Template来使用.Spring对这些框架的集成是这样的: 例如Hibernate,在使用Hibernate时(没有在Spri

NetworkComms V3 之支持TCP连接和UDP连接

NetworkComms V3 无缝的支持TCP连接和UDP连接. 您可以很容易的创建这两种连接 //创建一个连接信息对象 ConnectionInfo connInfo = new ConnectionInfo("192.168.0.1", 10000); //创建一个TCP连接 Connection newTCPConn = TCPConnection.GetConnection(connInfo); //创建一个UDP连接 Connection newUDPConn = UDPC

7.Sentinel源码分析—Sentinel是怎么和控制台通信的?

这里会介绍: Sentinel会使用多线程的方式实现一个类Reactor的IO模型 Sentinel会使用心跳检测来观察控制台是否正常 Sentinel源码解析系列: 1.Sentinel源码分析-FlowRuleManager加载规则做了什么? 2. Sentinel源码分析-Sentinel是如何进行流量统计的? 3. Sentinel源码分析- QPS流量控制是如何实现的? 4.Sentinel源码分析- Sentinel是如何做到降级的? 5.Sentinel源码分析-Sentinel如

【源码下载】分享一个支持自安装自卸载的Windows服务

这个程序来自  www.codeproject.com 具体的出处就忘了 服务器端的程序,我一般采用在windows服务中调用打开的方式,这样既能看到界面,又能避免系统注销时,程序跟着退出.而且能够支持开机启动. 只在windows server 2003 和.net2.0环境下使用,其他运行环境没有测试 点击服务程序能够实现自安装和自卸载,用起来还是很方便的. 在配置文件中,设定了服务器端程序的位置地址 服务器端程序是另一个程序,用来和客户端进行通讯.windows服务用来打开这个服务器端程序

NIO框架之MINA源码解析(三):底层通信与责任链模式应用

本文主要介绍下在mina中责任链模式的应用以及mina对于数据读写的处理. 在mina中,对数据的读操作是在processor类里面触发的,收到新消息后就触发读数据链去处理新消息直到自己的业务逻辑代码(IoHandler). 在mina中,数据的写(write)和发(send)差别相对较大,mina中的写消息最终的结果只是把要写的消息经过写数据链处理后的最终结果放在了一个缓存中,并把当前session标记为可发. 数据的发送就是传统中我们所说的发消息,就是把写消息最终处理的结果发送到客户端,待发

不包含通信框架源码

源码 (不包含通信框架源码,通信框架源码请另行下载)上一篇文章写了如何通过TCP通信发送图片到客户端,有朋友问如何传送文件,本文将就如何发送文件进行探讨.对于比较小的文件,可以把文件转化成字节形式,用契约类包装一下,服务器收到后,再把字节转化成文件即可,这也是本文中实现的方式,这种方式的优点是比较简... 处理客户端发来的文件 private void IncomingUploadFile(PacketHeader header, Connection connection, FileWrapp

[c#源码分享]客户端程序通过TCP通信传送&quot;小文件&quot;到服务器

源码  (不包含通信框架源码,通信框架源码请另行下载) 上一篇文章写了如何通过TCP通信发送图片到客户端,有朋友问如何传送文件,本文将就如何发送文件进行探讨. 对于比较小的文件,可以把文件转化成字节形式,用契约类包装一下,服务器收到后,再把字节转化成文件即可,这也是本文中实现的方式,这种方式的优点是比较简单灵活,缺点是不适合大文件的发送,也不能显示文件发送的进度. 基于TCP的通信机制,对于比较大的文件,这种方式是不可行的,大文件采用分段发送再合成的方式比较好,以后有时间再对如何发送大文件单独探

查看support-v4支持包中的源码

在support-v4包里面,添加了很多的支持控件,比如ViewPager,Fragment等,为了解决一些问题,我们有时候想要看一下实现源码,但是点进去之后,源码并不会显示出来,会出现下面的情况. 那么,我们怎么才能看到支持包里面的源码呢? 下面,给大家一个解决方案. 首先,在文件夹libs下面创建一个文件,名称为android-support-v4.jar.properties,如下: 然后,在文件里面写上我们的support-v4包的源码文件夹的所在,比如,我是Mac系统,文件在下面这个目

android源码大放送(实战开发必备),免费安卓demo源码,例子大全文件详细列表

免费安卓demo源码,例子大全文件详细列表 本列表源码永久免费下载地址:http://www.jiandaima.com/blog/android-demo 卷 yunpan 的文件夹 PATH 列表 卷序列号为 0000-73EC E:. │ jiandaima.com文件列表生成.bat │ 例子大全说明.txt │ 本例子永久更新地址~.url │ 目录列表2016.03.10更新.txt │ ├─前台界面 │ ├─3D标签云卡片热门 │ │ Android TagCloudView云标签