zoukankan      html  css  js  c++  java
  • 01、Windows Phone 套接字(Socket)实战之交互设计

         这个 Demo 主要使用 WP 中提供的 Socket 对象,来与 PC 端进行文字、文件的互相传输。因为在 WP 中系统

    对存储的操作限制的比较多,例如,你把 .doc、.txt、.zip 等常见的格式文件放到手机的存储(包括 SD卡)中,第三

    方应用也是不能获取这些文件的。所以,当你的应用需要操作用户选择的文件的时候,其中的一个解决方案是当用户

    连接到 Wifi 上时(不需要连接数据线),在 PC 端运行一个软件,让这个 PC 软件和 WP 使用 Socket 通过 TCP

    协议进行文件的传输。既然可以传输文件,当然也可以传输文字,即 PC 和 WP 端进行文字聊天。

    一、交互

          这个 Demo 的实现思路图:

    实际操作截图:

    1、服务器端的启动截图:

    2、客户端启动截图:

    3、服务器端开始侦听,点击客户端的“连接” 按钮向服务器端发起连接请求,PC 端侦听到客户端的请求后,便建立通信使用的Socket,然后开始通信:

    二、通信协议

          为了兼顾传递“文件”和“文字”数据使用同一个 Socket 对象,需要在客户端和 PC 端进行定义

    同一个数据协议。并且在文件传输的时候,还需要传递文件的名称和文件的扩展名等额外的信息。因为

    文字和文件数据,在进行 TCP 传输的时候,都是 byte 数组,所以这里在传输数据前,把这些额外

    的描述信息(head)转换成 byte 数组后,放到文字或文件(body)的 byte 数组前面。因为这些描述

    信息的长度是有限的文字,这里暂时定义 500 字节用来装这些描述信息,在这 500 字节后面放置真正的数据。

    协议描述:

    这里自定义一个 DataType 类,用来描述数据体的信息,这里暂时定义三个类型:

      public class DataType
      {
          bool isFile;
          /// <summary>
          /// 是否是文件类型,如果否,则是 string 类型的消息
          /// </summary>
          public bool IsFile
          {
              get { return isFile; }
              set { isFile = value; }
          }
    
          string exten;
          /// <summary>
          /// 文件的后缀名,长度不能超过20个汉字字符(40个英文字符)
          /// </summary>
          public string Exten
          {
              get { return exten; }
              set { exten = value; }
          }
    
          string fileName;
          /// <summary>
          /// 文件的名称,长度不能超过100个汉字字符(200个英文字符)
          /// </summary>
          public string FileName
          {
              get { return fileName; }
              set { fileName = value; }
          }
      }


       在处理这些描述信息的时候,需要定义一个静态的工具类,放一些静态常用方法。首先在工程中添加一个

    CommonHelper.cs,然后添加两个把对象序列化和反序列化成字符串的方法:

    添加命名空间:

    using System.Runtime.Serialization.Json;

    序列化和反序列化:

       #region JSON序列化和反序列化
            /// <summary>  
            /// JSON序列化  
            /// </summary>  
            public static string JsonSerializer<T>(T t)
            {
                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
                MemoryStream ms = new MemoryStream();
                ser.WriteObject(ms, t);
                byte[] buffer = ms.ToArray();
                string jsonString = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
                ms.Close();
                return jsonString;
            }
    
            /// <summary>  
            /// JSON反序列化  
            /// </summary>  
            public static T JsonDeserialize<T>(string jsonString)
            {
                T jsonObject;
                DataContractJsonSerializer jsonSel = new DataContractJsonSerializer(typeof(T));
                using (MemoryStream ms = new MemoryStream())
                using (StreamWriter sw = new StreamWriter(ms))
                {
                    sw.Write(jsonString);
                    sw.Flush();
                    ms.Position = 0;
                    jsonObject = (T)jsonSel.ReadObject(ms);
    
                    sw.Dispose();
                    ms.Dispose();
                }
    
                return jsonObject;
            }
            #endregion

         在进行文件传输的时候,根据上面 DataType 类的定义,文件的后缀名,长度不能超过20个汉字字符(40个英文字符);

    文件的名称,长度不能超过100个汉字字符(200个英文字符)。所以在文件传输前,需要对用户选择的文件的名字和后缀名

    的长度进行判断,因为 1 个汉字占用两个字节,所以这里在 CommonHelper.cs 类中添加判断字数的方法,如果字数

    不合格,则提示用户:

       #region  计算文本长度
       /// <summary>   
       /// 计算文本长度,区分中英文字符,中文算两个长度,英文算一个长度
       /// </summary>
       /// <param name="Text">需计算长度的字符串</param>
       /// <returns>int</returns>
       public static int Text_Length(string Text)
       {
           int len = 0;
    
           for (int i = 0; i < Text.Length; i++)
           {
               byte[] byte_len = System.Text.Encoding.UTF8.GetBytes(Text.Substring(i, 1));
               if (byte_len.Length > 1)
                   len += 2;  //如果长度大于1,是中文,占两个字节,+2
               else
                   len += 1;  //如果长度等于1,是英文,占一个字节,+1
           }
    
           return len;//len / 2 + len % 2;
       }
       #endregion


      

       还需要在 CommonHelper.cs 文件中定义方法,用来把数据表述信息(head)和数据体(body)

    来拼接成真正被发送的信息,和把接收到的信息翻转成描述信息(head)和数据体(body):

            #region 数据转换
            /// <summary>
            /// 套接字发送和接收到的流都分别为两部分,前 500 字节为消息头,后面为消息体
            /// </summary>
            public const int HeaderLength = 500;
            
            // 文件或文字暂时定义为最长 4MB
            public const int FileLength = 1024 * 1024 * 4 + HeaderLength; 
            //const int MsgLength = 1024 * 2; // 消息文本最长字节数
    
            /// <summary>
            /// 把数据类型作为 head,把文字或文件数据作为 body,返回两者 byte[] 的组合结果
            /// </summary>
            /// <param name="dataType">作为 head,指示 body 的数据类型</param>
            /// <param name="strPath">文件的路径,文字或文件只穿递一个,另一个为 null</param>
            /// <param name="strMsg">文字的内容</param>
            /// <returns></returns>
            public static byte[] ConvertDataToByte(DataType dataType, string strPath, string strMsg)
            {
                byte[] byteResult;
    
                if (dataType.IsFile == true)
                {
                    // 文件的后缀名
                    dataType.Exten = Path.GetExtension(strPath);
                    
                    // 文件的名称
                    dataType.FileName = Path.GetFileNameWithoutExtension(strPath);
    
                    if (CommonHelper.Text_Length(dataType.Exten) > 41) // 后缀名中包含一个 .
                    {
                        throw new Exception("文件的后缀名,长度不能超过20个汉字字符(40个英文字符)");
                    }
    
                    if (CommonHelper.Text_Length(dataType.FileName) > 200)
                    {
                        throw new Exception("文件的名称,长度不能超过100个汉字字符(200个英文字符)");
                    }
    
                    // 消息头
                    string strHeader = CommonHelper.JsonSerializer<DataType>(dataType);
                    byte[] byteHeader = Encoding.UTF8.GetBytes(strHeader + "<EOF>");
    
    
                    //通过文件流 读取文件内容
                    using (FileStream fs = new FileStream(strPath, FileMode.OpenOrCreate))
                    {
                        // 消息体
                        byte[] arrFile = new byte[FileLength];
    
                        //读取文件内容到字节数组,并 获得 实际文件大小
                        int fileLength = fs.Read(arrFile, 0, arrFile.Length);
    
                        if (fileLength >= CommonHelper.FileLength)
                        {
                            throw new Exception("文件的尺寸必须小于 4 MB");                        
                        }
    
                        byteResult = new byte[HeaderLength + fileLength];
    
                        // 拷贝字节数组的内容
                        Buffer.BlockCopy(byteHeader, 0, byteResult, 0, byteHeader.Length);
                        Buffer.BlockCopy(arrFile, HeaderLength, byteResult, HeaderLength, fileLength);
                    }
                }
                else
                {
                    // 消息头
                    string strHeader = CommonHelper.JsonSerializer<DataType>(dataType);
    
                    // 添加 "<EOF>" 表示 head 的 json字符串结束
                    byte[] byteHeader = Encoding.UTF8.GetBytes(strHeader + "<EOF>"); 
    
    
                    byte[] byteMsg = Encoding.UTF8.GetBytes(strMsg);
                    byteResult = new byte[HeaderLength + byteMsg.Length];
    
                    Buffer.BlockCopy(byteHeader, 0, byteResult, 0, byteHeader.Length);
                    Buffer.BlockCopy(byteMsg, 0, byteResult, HeaderLength, byteMsg.Length);
                }
    
                return byteResult;
            }
    
            /// <summary>
            /// 转换源 byte[] 数据内容,获取其中的 head 和 body 的实际内容
            /// </summary>
            /// <param name="byteSrc">数据源</param>
            /// <param name="dataType">指示 body 的数据类型返回结果</param>
            /// <param name="byteFile">文件内容返回结果</param>
            /// <param name="strMsg">文字内容返回结果</param>
            public static void ConvertByteToData(byte[] byteSrc, out DataType dataType, out byte[] byteFile, out string strMsg)
            {
                dataType = null;
                byteFile = null;
                strMsg = null;
    
                // 初始化表示 head 的数组
                byte[] byteHeader = new byte[HeaderLength];
                Buffer.BlockCopy(byteSrc, 0, byteHeader, 0, HeaderLength);
    
                // 获取 head 数组的 json 数据字符串
                string strHeader = Encoding.UTF8.GetString(byteHeader);
    
                if (strHeader.Contains("<EOF>"))
                {
                    int index = strHeader.IndexOf("<EOF>");
                    string strHeaderValue = strHeader.Substring(0, index);
    
                    // 把 json 字符串转换成 DataType 对象
                    dataType = CommonHelper.JsonDeserialize<DataType>(strHeaderValue);
                    if (dataType != null)
                    {
                        if (dataType.IsFile == true)
                        {
                            byteFile = new byte[byteSrc.Length - HeaderLength];
                            Buffer.BlockCopy(byteSrc, HeaderLength, byteFile, 0, byteSrc.Length - HeaderLength);
                        }
                        else
                        {
                            byte[] byteMsg = new byte[byteSrc.Length - HeaderLength];
                            Buffer.BlockCopy(byteSrc, HeaderLength, byteMsg, 0, byteSrc.Length - HeaderLength);
    
                            strMsg = Encoding.UTF8.GetString(byteMsg);
                            strMsg = strMsg.Trim('');
                        }
                    }
                }
            }
    
            #endregion


    同时需要在这个文件中添加获取 PC 端 IP 的方法:

      #region 获取 PC 端的 IP 地址
      /// <summary>
      /// 获取本地的 IP 地址
      /// </summary>
      /// <returns></returns>
      public static string GetIPAddress()
      {
          System.Net.IPAddress addr;
          // 获得拨号动态分配IP地址 
          addr = new System.Net.IPAddress(Dns.GetHostByName(Dns.GetHostName()).AddressList[1].Address);
          return addr.ToString();
      }
    
    
      public static string GetLocalIPAddress()
      {
          System.Net.IPAddress addr;
          // 获得本机局域网IP地址 
          addr = new System.Net.IPAddress(Dns.GetHostByName(Dns.GetHostName()).AddressList[0].Address);
          return addr.ToString();
      }
      #endregion
  • 相关阅读:
    如何在eclipse开发环境中连接数据库?oracle和db2
    oracle数据库表的记录误删后如何恢复
    人事经理眼中的好简历
    JavaScript常用方法
    如何解决plsql被用户锁定的问题?
    eclipse 快捷键
    刚开始用9.3的版本,一堆问题。(转)
    十条不错的编程观点(转)
    http://localhost:8099无法登录,出现incorrect info报错信息,怎么办?
    http://localhost:8399/arcgis/rest/services 无法访问,怎么办?
  • 原文地址:https://www.cnblogs.com/hebeiDGL/p/3155221.html
Copyright © 2011-2022 走看看