协议处理类的实现
和上面一章一样,在开始编写实际的服务端客户端代码之前,我们首先要编写处理协议的类,它需要提供这样两个功能:1、方便地帮 我们获取完整的协议信 息,因为前面我们说过,服务端可能将客户端的多次独立请求拆分或合并。比如,客户端连续发送了两条控制 信息到服务端,而服务端将它们合并了,那么则需要先 拆开再分别处理。2、方便地获取我们所想要的属性信息,因为协议是 XML 格 式,所以还需要一个类专门对 XML 进行处理,获得字符串的属性值。
1. ProtocalHandler 辅助类
我们先看下 ProtocalHandler,它与上一篇中的 RequestHandler 作用相同。需要注意的是必须将它声明为实例的,而非静态 的,这是 因为每个 TcpClient 都需要对应一个 ProtocalHandler,因为它内部维护的 patialProtocal 不能共享,在协议发送 不完整的情况下,这个 变量用于临时保存被截断的字符串。
public class ProtocolHandler {
private string partialProtocal; // 保存不完整的协议
public ProtocolHandler() {
partialProtocal = "";
}
public string[] GetProtocol(string input) {
return GetProtocol(input, null);
}
// 获得协议
private string[] GetProtocol(string input, List<string> outputList) {
if (outputList == null)
outputList = new List<string>();
if (String.IsNullOrEmpty(input))
return outputList.ToArray();
if (!String.IsNullOrEmpty(partialProtocal))
input = partialProtocal + input;
string pattern = "(^<protocol>.*?</protocol>)";
// 如果有匹配,说明已经找到了,是完整的协议
if (Regex.IsMatch(input, pattern)) {
// 获取匹配的值
string match = Regex.Match(input, pattern).Groups[0].Value;
outputList.Add(match);
partialProtocal = "";
// 缩短 input 的长度
input = input.Substring(match.Length);
// 递归调用
GetProtocol(input, outputList);
} else {
// 如果不匹配,说明协议的长度不够,
// 那么先缓存,然后等待下一次请求
partialProtocal = input;
}
return outputList.ToArray();
}
}
因为现在它已经不是本文的重点了,所以我就不演示对于它的测试了,本文所附带的代码中含有它的测试代码(我在 ProtocolHandler 中添加了一个静态类 Test())。
2. FileRequestType 枚举和 FileProtocol 结构
因为 XML 是以字符串的形式在进行传输,为了方便使用,我们最好构建一个强类型来对它们进行操作,这样会方便很多。我们首先可 以定义 FileRequestMode 枚举,它代表是发送还是接收文件:
public enum FileRequestMode {
Send = 0,
Receive
}
接下来我们再定义一个 FileProtocol 结构,用来为整个协议字符串提供强类型的访问,注意这里覆盖了基类的 ToString()方法,这样在 客户端我们就不需要再手工去编写 XML,只要在结构值上调用 ToString()就 OK 了,会方便很多。
public struct FileProtocol {
private readonly FileRequestMode mode;
private readonly int port;
private readonly string fileName;
public FileProtocol
(FileRequestMode mode, int port, string fileName) {
this.mode = mode;
this.port = port;
this.fileName = fileName;
}
public FileRequestMode Mode {
get { return mode; }
}
public int Port {
get { return port; }
}
public string FileName {
get { return fileName; }
}
public override string ToString() {
return String.Format("<protocol><file name=\"{0}\" mode=\"{1}\" port=\"{2}\" /></protocol>", fileName, mode, port);
}
}
3. ProtocolHelper 辅助类
这个类专用于将 XML 格式的协议映射为我们上面定义的强类型对象,这里我没有加入 try/catch 异常处理,因为协议对用户来说是不可 见的,而且客户端应该总是发送正确的协议,我觉得这样可以让代码更加清晰:
public class ProtocolHelper {
private XmlNode fileNode;
private XmlNode root;
public ProtocolHelper(string protocol) {
XmlDocument doc = new XmlDocument();
doc.LoadXml(protocol);
root = doc.DocumentElement;
fileNode = root.SelectSingleNode("file");
}
// 此时的 protocal 一定为单条完整 protocal
private FileRequestMode GetFileMode() {
string mode = fileNode.Attributes["mode"].Value;
mode = mode.ToLower();
if (mode == "send")
return FileRequestMode.Send;
else
return FileRequestMode.Receive;
}
// 获取单条协议包含的信息
public FileProtocol GetProtocol() {
FileRequestMode mode = GetFileMode();
string fileName = "";
int port = 0;
fileName = fileNode.Attributes["name"].Value;
port = Convert.ToInt32(fileNode.Attributes["port"].Value);
return new FileProtocol(mode, port, fileName);
}
}
OK,我们又耽误了点时间,下面就让我们进入正题吧。