这个 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 类,用来描述数据体的信息,这里暂时定义三个类型:

  1. public class DataType
  2. {
  3. bool isFile;
  4. /// <summary>
  5. /// 是否是文件类型,如果否,则是 string 类型的消息
  6. /// </summary>
  7. public bool IsFile
  8. {
  9. get { return isFile; }
  10. set { isFile = value; }
  11. }
  12.  
  13. string exten;
  14. /// <summary>
  15. /// 文件的后缀名,长度不能超过20个汉字字符(40个英文字符)
  16. /// </summary>
  17. public string Exten
  18. {
  19. get { return exten; }
  20. set { exten = value; }
  21. }
  22.  
  23. string fileName;
  24. /// <summary>
  25. /// 文件的名称,长度不能超过100个汉字字符(200个英文字符)
  26. /// </summary>
  27. public string FileName
  28. {
  29. get { return fileName; }
  30. set { fileName = value; }
  31. }
  32. }

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

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

添加命名空间:

  1. using System.Runtime.Serialization.Json;

序列化和反序列化:

  1. #region JSON序列化和反序列化
  2. /// <summary>
  3. /// JSON序列化
  4. /// </summary>
  5. public static string JsonSerializer<T>(T t)
  6. {
  7. DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
  8. MemoryStream ms = new MemoryStream();
  9. ser.WriteObject(ms, t);
  10. byte[] buffer = ms.ToArray();
  11. string jsonString = Encoding.UTF8.GetString(buffer, , buffer.Length);
  12. ms.Close();
  13. return jsonString;
  14. }
  15.  
  16. /// <summary>
  17. /// JSON反序列化
  18. /// </summary>
  19. public static T JsonDeserialize<T>(string jsonString)
  20. {
  21. T jsonObject;
  22. DataContractJsonSerializer jsonSel = new DataContractJsonSerializer(typeof(T));
  23. using (MemoryStream ms = new MemoryStream())
  24. using (StreamWriter sw = new StreamWriter(ms))
  25. {
  26. sw.Write(jsonString);
  27. sw.Flush();
  28. ms.Position = ;
  29. jsonObject = (T)jsonSel.ReadObject(ms);
  30.  
  31. sw.Dispose();
  32. ms.Dispose();
  33. }
  34.  
  35. return jsonObject;
  36. }
  37. #endregion

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

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

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

不合格,则提示用户:

  1. #region 计算文本长度
  2. /// <summary>
  3. /// 计算文本长度,区分中英文字符,中文算两个长度,英文算一个长度
  4. /// </summary>
  5. /// <param name="Text">需计算长度的字符串</param>
  6. /// <returns>int</returns>
  7. public static int Text_Length(string Text)
  8. {
  9. int len = ;
  10.  
  11. for (int i = ; i < Text.Length; i++)
  12. {
  13. byte[] byte_len = System.Text.Encoding.UTF8.GetBytes(Text.Substring(i, ));
  14. if (byte_len.Length > )
  15. len += ; //如果长度大于1,是中文,占两个字节,+2
  16. else
  17. len += ; //如果长度等于1,是英文,占一个字节,+1
  18. }
  19.  
  20. return len;//len / 2 + len % 2;
  21. }
  22. #endregion

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

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

  1. #region 数据转换
  2. /// <summary>
  3. /// 套接字发送和接收到的流都分别为两部分,前 500 字节为消息头,后面为消息体
  4. /// </summary>
  5. public const int HeaderLength = ;
  6.  
  7. // 文件或文字暂时定义为最长 4MB
  8. public const int FileLength = * * + HeaderLength;
  9. //const int MsgLength = 1024 * 2; // 消息文本最长字节数
  10.  
  11. /// <summary>
  12. /// 把数据类型作为 head,把文字或文件数据作为 body,返回两者 byte[] 的组合结果
  13. /// </summary>
  14. /// <param name="dataType">作为 head,指示 body 的数据类型</param>
  15. /// <param name="strPath">文件的路径,文字或文件只穿递一个,另一个为 null</param>
  16. /// <param name="strMsg">文字的内容</param>
  17. /// <returns></returns>
  18. public static byte[] ConvertDataToByte(DataType dataType, string strPath, string strMsg)
  19. {
  20. byte[] byteResult;
  21.  
  22. if (dataType.IsFile == true)
  23. {
  24. // 文件的后缀名
  25. dataType.Exten = Path.GetExtension(strPath);
  26.  
  27. // 文件的名称
  28. dataType.FileName = Path.GetFileNameWithoutExtension(strPath);
  29.  
  30. if (CommonHelper.Text_Length(dataType.Exten) > ) // 后缀名中包含一个 .
  31. {
  32. throw new Exception("文件的后缀名,长度不能超过20个汉字字符(40个英文字符)");
  33. }
  34.  
  35. if (CommonHelper.Text_Length(dataType.FileName) > )
  36. {
  37. throw new Exception("文件的名称,长度不能超过100个汉字字符(200个英文字符)");
  38. }
  39.  
  40. // 消息头
  41. string strHeader = CommonHelper.JsonSerializer<DataType>(dataType);
  42. byte[] byteHeader = Encoding.UTF8.GetBytes(strHeader + "<EOF>");
  43.  
  44. //通过文件流 读取文件内容
  45. using (FileStream fs = new FileStream(strPath, FileMode.OpenOrCreate))
  46. {
  47. // 消息体
  48. byte[] arrFile = new byte[FileLength];
  49.  
  50. //读取文件内容到字节数组,并 获得 实际文件大小
  51. int fileLength = fs.Read(arrFile, , arrFile.Length);
  52.  
  53. if (fileLength >= CommonHelper.FileLength)
  54. {
  55. throw new Exception("文件的尺寸必须小于 4 MB");
  56. }
  57.  
  58. byteResult = new byte[HeaderLength + fileLength];
  59.  
  60. // 拷贝字节数组的内容
  61. Buffer.BlockCopy(byteHeader, , byteResult, , byteHeader.Length);
  62. Buffer.BlockCopy(arrFile, HeaderLength, byteResult, HeaderLength, fileLength);
  63. }
  64. }
  65. else
  66. {
  67. // 消息头
  68. string strHeader = CommonHelper.JsonSerializer<DataType>(dataType);
  69.  
  70. // 添加 "<EOF>" 表示 head 的 json字符串结束
  71. byte[] byteHeader = Encoding.UTF8.GetBytes(strHeader + "<EOF>");
  72.  
  73. byte[] byteMsg = Encoding.UTF8.GetBytes(strMsg);
  74. byteResult = new byte[HeaderLength + byteMsg.Length];
  75.  
  76. Buffer.BlockCopy(byteHeader, , byteResult, , byteHeader.Length);
  77. Buffer.BlockCopy(byteMsg, , byteResult, HeaderLength, byteMsg.Length);
  78. }
  79.  
  80. return byteResult;
  81. }
  82.  
  83. /// <summary>
  84. /// 转换源 byte[] 数据内容,获取其中的 head 和 body 的实际内容
  85. /// </summary>
  86. /// <param name="byteSrc">数据源</param>
  87. /// <param name="dataType">指示 body 的数据类型返回结果</param>
  88. /// <param name="byteFile">文件内容返回结果</param>
  89. /// <param name="strMsg">文字内容返回结果</param>
  90. public static void ConvertByteToData(byte[] byteSrc, out DataType dataType, out byte[] byteFile, out string strMsg)
  91. {
  92. dataType = null;
  93. byteFile = null;
  94. strMsg = null;
  95.  
  96. // 初始化表示 head 的数组
  97. byte[] byteHeader = new byte[HeaderLength];
  98. Buffer.BlockCopy(byteSrc, , byteHeader, , HeaderLength);
  99.  
  100. // 获取 head 数组的 json 数据字符串
  101. string strHeader = Encoding.UTF8.GetString(byteHeader);
  102.  
  103. if (strHeader.Contains("<EOF>"))
  104. {
  105. int index = strHeader.IndexOf("<EOF>");
  106. string strHeaderValue = strHeader.Substring(, index);
  107.  
  108. // 把 json 字符串转换成 DataType 对象
  109. dataType = CommonHelper.JsonDeserialize<DataType>(strHeaderValue);
  110. if (dataType != null)
  111. {
  112. if (dataType.IsFile == true)
  113. {
  114. byteFile = new byte[byteSrc.Length - HeaderLength];
  115. Buffer.BlockCopy(byteSrc, HeaderLength, byteFile, , byteSrc.Length - HeaderLength);
  116. }
  117. else
  118. {
  119. byte[] byteMsg = new byte[byteSrc.Length - HeaderLength];
  120. Buffer.BlockCopy(byteSrc, HeaderLength, byteMsg, , byteSrc.Length - HeaderLength);
  121.  
  122. strMsg = Encoding.UTF8.GetString(byteMsg);
  123. strMsg = strMsg.Trim('\0');
  124. }
  125. }
  126. }
  127. }
  128.  
  129. #endregion

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

  1. #region 获取 PC 端的 IP 地址
  2. /// <summary>
  3. /// 获取本地的 IP 地址
  4. /// </summary>
  5. /// <returns></returns>
  6. public static string GetIPAddress()
  7. {
  8. System.Net.IPAddress addr;
  9. // 获得拨号动态分配IP地址
  10. addr = new System.Net.IPAddress(Dns.GetHostByName(Dns.GetHostName()).AddressList[].Address);
  11. return addr.ToString();
  12. }
  13.  
  14. public static string GetLocalIPAddress()
  15. {
  16. System.Net.IPAddress addr;
  17. // 获得本机局域网IP地址
  18. addr = new System.Net.IPAddress(Dns.GetHostByName(Dns.GetHostName()).AddressList[].Address);
  19. return addr.ToString();
  20. }
  21. #endregion

01、Windows Phone 套接字(Socket)实战之交互设计的更多相关文章

  1. 02、Windows Phone 套接字(Socket)实战之服务器端设计

    这里主要写 PC 服务器端的逻辑,UI 使用的是 WPF,因为 WPF 比普通的 WinForm 的流式布局 更容易控制,而且比 WinForm 美观一些,显示截图: 一.页面 UI MainWind ...

  2. 面向对象之套接字(socket)和黏包

    一丶套接字(socket) tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 基于UDP协议的socket server端: import socket udp_sk = socke ...

  3. 网络编程 套接字socket TCP UDP

    网络编程与套接字 网络编程 网络编程是什么: ​ 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 ​ 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...

  4. 第13讲 | 套接字Socket:Talk is cheap, show me the code

    第13讲 | 套接字Socket:Talk is cheap, show me the code 基于 TCP 和 UDP 协议的 Socket 编程.在讲 TCP 和 UDP 协议的时候,我们分客户 ...

  5. Linux进程间通信(八):流套接字 socket()、bind()、listen()、accept()、connect()、read()、write()、close()

    前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...

  6. Linux进程间通信(九):数据报套接字 socket()、bind()、sendto()、recvfrom()、close()

    前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套 ...

  7. 套接字socket 的地址族和类型、工作原理、创建过程

    注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料如<linux内核完全剖析>.<linux c 编程一站式学习>等,只是为了更好 ...

  8. [置顶] Java套接字Socket编程

    1)概念 网络编程基本模型就客户端到服务器的模型,也就是我们常见的C/S模型.简单的说就是两个进程间相互通信的过程.即通信双方一方作为服务器等待客户端提出请求并给以回应,另一方作为客户端向服务器提出请 ...

  9. Java网络编程--套接字Socket

    一.套接字Socket IP地址标志Internet上的计算机,端口号标志正在计算机上运行的进程(程序). 端口号被规定为一个16位的0--65535之间的整数,其中,0--1023被预先定义的服务通 ...

  10. 套接字编程,创建套接字socket

    1.套接字地址结构: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; 其中,成员sa_family表示套接字的协议族类型,对 ...

随机推荐

  1. ECMA-262,第 5 版 最新 JavaScript 规范

    ECMA-262,第 5 版 最新 JavaScript 规范 Rob Larsen, 界面架构师, IBM 简介: 了解 ECMAScript 规范的历史,查看它的众多重要新特性和新概念. 发布日期 ...

  2. stl map中的lower_bound和 upper_bound

    map中的lower_bound和upper_bound的意思其实很简单,就两句话: map::lower_bound(key):返回map中第一个大于或等于key的迭代器指针 map::upper_ ...

  3. jQuery调用ajax获取json格式数据

    <body> <div>点击按钮获取音乐列表</div> <input type="button" id="button&quo ...

  4. JS-获取图片地址

    var url=document.getElementById("a001").src; 或var url=document.getElementById("a001&q ...

  5. SELECT语句逻辑运行顺序,你知道吗?

    引言 这不是一个什么多深的技术问题.多么牛叉的编程能力.这跟一个人的开发能力也没有很必定的直接关系,可是知道这些会对你的SQL编写,排忧及优化上会有很大的帮助.它不是一个复杂的知识点.可是一个很基础的 ...

  6. vim 如何编辑 GB2312 编码的文件?

    vim 如何编辑 GB2312 编码的文件? 彻底搞清楚字符编码: ASCII, ISO_8859, GB2312,UCS, Unicode, U 结合file和iconv命令转换文件的字符编码类型 ...

  7. win7无法启动telnet服务

    右键我的电脑 —> 管理 —>服务,找到服务telnet, 右键—>属性—>依赖关系 就可以看到其他服务的依赖关系,打开相关依赖的服务即可解决服务不能启动的情况

  8. linux 打包和压缩文件

    打包成tar文件 tar -cf mydir.tar mydir/ 打包tar压缩成gz tar -czf mydir.tar.gz mydir/ 解压mydirtar文件 tar -xvf mydi ...

  9. [转发]jQuery Validation范例

    验证操作类formValidatorClass.js参照文件有: http://www.cnblogs.com/easyinsc/archive/2009/02/27/1407826.html htt ...

  10. 38、各Set实现类的性能分析

    HashSet和TreeSet是Set的两个典型实现,到底如何选择HashSet和TreeSet呢?HashSet的性能总是比TreeSet好(特别是最常用的添加.查询元素等操作),因为TreeSet ...