当前位置: 代码迷 >> C# >> [c#源码分享]软风IM V3.1 支持TCP通信发送图片
  详细解决方案

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

热度:87   发布时间:2016-05-05 04:35:40.0
[c#源码分享]微风IM V3.1 支持TCP通信发送图片
微风IM 采用开源的networkcomms2.3.1c#通信框架
源码  数据库 (数据库和以前一样 没有变动)
前面有好几位朋友说希望微风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");
            }

        }

图片的传送是经常用的功能,在其他的程序中也经常用到,稍微改动一下,可以实现客户端拍照片,然后传送到服务器上保存等类似功能。
------解决思路----------------------
LZ新年好!非常不错,支持源码分享!
------解决思路----------------------
不明觉屌.....
------解决思路----------------------
支持源码分享。源码和注解都很好理解。
------解决思路----------------------
 感谢楼主
------解决思路----------------------
谢谢楼主的分享
------解决思路----------------------
不错,推荐一下
------解决思路----------------------
好码先顶再撸
------解决思路----------------------
对于分享代码的行为,一直都是点赞的!
------解决思路----------------------
这是要抢腾讯的饭碗呢。
支持一下。
------解决思路----------------------
支持源码分享。
  相关解决方案