WP7应用开发笔记(5) 通信设计
WP7支持的通信方式
1. HTTP协议
主要是由WebClient或HttpWebRequest两个类提供,直接封装HTTP协议访问Web站点。最常用的通信方式。
2. WCF
WCF作为MS大力推广的通信方案非常强大,但是到了WP7上就变成了太监,只支持简单的BasicHttpBinding而且还有非常多的限制。
3 Socket
7.1SDK里新增的通信方式,支持TCP和UDP但只能使用异步的SocketAsyncEventArgs事件方式,也有不少限制。
选择通信方式
因为同时需要考虑到服务器端的实现,在服务器端尽量精简,最好不要有什么IIS之类大型依赖。
对应是服务器端实现方式如下:
HTTP协议 | HttpListener |
WCF | Wcf应用程序宿主 |
Socket | Socket |
由于我使用的是Win7 HttpListener和BasicHttpBinding在监听外网IP时都需要管理员身份验证,这点很不友好,我每次开程序都提示一下,不爽。所以决定使用Socket。
SOCKET协议选择
而Socket分为TCP和UDP,为了稳定选择了有重传功能和回复的TCP(其实UDP也不是什么问题)。
TCP需要3次握手来维持链接,但是按照“偶尔连接”(Occasionally Connect)的设计准则,我不能让手机一直保持连接,而且断线检查也是个麻烦事。
所以决定采用HTTP1.0类似的短链接方式,就是连上、发一条消息、然后断开的流程(暂时没有回复)
应用层消息报文设计
这个程序比较简单设计的报文也很简单,
因为内网传输不需要考虑身份验证,加密、完整性等,而且短链接不会遇到粘包和拆包之类的,真是太轻松了。
报文结构如下
Length | 长度 | 4字节 int |
Action | 动作 | 32字节 UTF8编码 |
Context | 内容 | 长度由Length描述 UTF8编码 |
Length表示Context的长度,报文本身长度为Length+4+32
Action目前只有”TAP”按键 一种
Context表示按键的类型 有2种分类
- 一种是 Keyboard 直接对应键盘的键名称
- 另一种是 Media 媒体控制用 包括TogglePlayPause(播放暂停)、Forward(前进)、Backward(后退)IncreaseVolume(提高音量)等
表示方式是“Media.TogglePlayPause”
Media 协议表

namespace VirtualKeyboard.Contract
{
public static class KeyMethods
{
public const string KeyboardPackage = "Keyboard";
public const string MediaPackage = "Media";
public static class Keyboard
{
private const string KeyboardPre = KeyboardPackage + ".";
/// <summary>
/// Enter
/// </summary>
public const string Enter = KeyboardPre + "Enter";
/// <summary>
/// Esc
/// </summary>
public const string Esc = KeyboardPre + "Esc";
}
public static class Media
{
private const string MediaPre = MediaPackage + ".";
/// <summary>
/// 切换播放暂停
/// </summary>
public const string TogglePlayPause = MediaPre + "TogglePlayPause";
/// <summary>
/// 快进
/// </summary>
public const string FastForward = MediaPre + "FastForward";
/// <summary>
/// 快退
/// </summary>
public const string FastBackward = MediaPre + "FastBackward";
/// <summary>
/// 大步快进
/// </summary>
public const string VeryFastForward = MediaPre + "VeryFastForward";
/// <summary>
/// 大步快退
/// </summary>
public const string VeryFastBackward = MediaPre + "VeryFastBackward";
/// <summary>
/// 前进
/// </summary>
public const string Forward = MediaPre + "Forward";
/// <summary>
/// 前退
/// </summary>
public const string Backward = MediaPre + "Backward";
/// <summary>
/// 提高音量
/// </summary>
public const string IncreaseVolume = MediaPre + "IncreaseVolume";
/// <summary>
/// 降低音量
/// </summary>
public const string DecreaseVolume = MediaPre + "DecreaseVolume";
/// <summary>
/// 静音
/// </summary>
public const string MuteVolume = MediaPre + "MuteVolume";
/// <summary>
/// 上一集
/// </summary>
public const string PreviousTrack = MediaPre + "PreviousTrack";
/// <summary>
/// 下一集
/// </summary>
public const string NextTrack = MediaPre + "NextTrack ";
/// <summary>
/// 停止
/// </summary>
public const string Stop = MediaPre + "Stop";
/// <summary>
/// 全屏
/// </summary>
public const string FullScreen = MediaPre + "FullScreen";
}
}
}
客户端通信代码实现

public class SendCommandCompletedEventArgs : EventArgs
{
public bool Success { get; internal set; }
public string ErrMessage { get; internal set; }
}
public class Client
{
const int ActionSize = 32;
const int BufferSize = 1024;
public event EventHandler<SendCommandCompletedEventArgs> SendCommandCompleted;
private void OnSendCommandCompleted(SendCommandCompletedEventArgs e)
{
EventHandler<SendCommandCompletedEventArgs> handler = SendCommandCompleted;
if (handler != null) handler(this, e);
}
public Client()
{
}
public void SendCommandAsync(EndPoint endPoint, string action, string command)
{
var actionBuffer = Encoding.UTF8.GetBytes(action);
var contextBuffer = Encoding.UTF8.GetBytes(command);
var bufferSize = contextBuffer.Length + ActionSize + 4;
var lengthBuffer = BitConverter.GetBytes(contextBuffer.Length);
var buffer = new byte[BufferSize];
int offset = 0;
Array.Copy(lengthBuffer, 0, buffer, offset, lengthBuffer.Length);
offset += 4;
Array.Clear(buffer, offset, ActionSize);
Array.Copy(actionBuffer, 0, buffer, offset, actionBuffer.Length);
offset += ActionSize;
Array.Copy(contextBuffer, 0, buffer, offset, contextBuffer.Length);
var socketAsyncEventArgs = new SocketAsyncEventArgs
{
UserToken =
new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp)
};
socketAsyncEventArgs.Completed += AsyncCompleted;
socketAsyncEventArgs.SetBuffer(buffer, 0, bufferSize);
socketAsyncEventArgs.RemoteEndPoint = endPoint;
ProcessConnect(socketAsyncEventArgs);
}
#region Private
private void ProcessConnect(SocketAsyncEventArgs e)
{
var socket = (Socket)e.UserToken;
bool willRaiseEvent = socket.ConnectAsync(e);
if (!willRaiseEvent)
{
ConnectCompleted(e);
}
}
private void ProcessSend(SocketAsyncEventArgs e)
{
var socket = (Socket)e.UserToken;
bool willRaiseEvent = socket.SendAsync(e);
if (!willRaiseEvent)
{
SendCompleted(e);
}
}
private void AsyncCompleted(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
ConnectCompleted(e);
break;
case SocketAsyncOperation.Send:
SendCompleted(e);
break;
case SocketAsyncOperation.Receive:
ReceiveCompleted(e);
break;
default:
return;
}
}
private void ReceiveCompleted(SocketAsyncEventArgs e)
{
}
private void ConnectCompleted(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
ProcessSend(e);
}
else
{
OnSendCommandCompleted(new SendCommandCompletedEventArgs { Success = false, ErrMessage = "接收器无法连接" });
}
}
private void SendCompleted(SocketAsyncEventArgs e)
{
var socket = (Socket)e.UserToken;
socket.Close();
if (e.SocketError == SocketError.Success)
{
OnSendCommandCompleted(new SendCommandCompletedEventArgs { Success = true });
}
else
{
OnSendCommandCompleted(new SendCommandCompletedEventArgs { Success = false, ErrMessage = "发送命令失败" });
}
}
#endregion
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库