假如我们要做一个C/S型的程序设计,服务端和客户端使用TCP通信,这时就需要在TCP协议之上,选择一个合适的应用层协议,如果不喜欢已有的协议,那就需要自己去实现一个协议规程,现在我们就要去完成一个图1所示的协议。
图1
1.定义传输的消息格式
该协议基本类似于简单邮件传输协议SMTP,不过我们需要做一下改变:信息传输不局限于ASCII码,要能够传输任何对象,这里采用了传输实体类,将其序列化为二进制流进行传输,代码1是定义的消息MessageEntity,注意它使用了Serializable标记,否则无法对它进行序列化,另外,传输的数据也必须被Serializable标记。
[Serializable]
public class MessageEntity
{
private CommandType _command;
/// <summary>
/// 命令类型
/// </summary>
public CommandType Command
{
get { return _command; }
set { _command = value; }
}
private object _data;
/// <summary>
/// 消息数据
/// </summary>
public object Data
{
get { return _data; }
set { _data = value; }
}
public MessageEntity(CommandType command, object data)
{
this._command = command;
this._data = data;
}
public enum CommandType
{
OK,
#region Server's Command
/// <summary>
/// 服务器就绪
/// </summary>
ServerReady,
/// <summary>
/// 允许客户断开
/// </summary>
AgreeDisconnect,
#endregion
#region Client's Command
/// <summary>
/// 呼叫服务器
/// </summary>
HelloServer,
/// <summary>
/// 数据消息
/// </summary>
Data,
/// <summary>
/// 请求退出
/// </summary>
Quit
#endregion
}
}
代码1
2.实现服务器
现在知道了通信的协议流程和通信消息体,那么就可以开始编程了,首先完成服务器端的程序。对服务器的要求是能允许多客户连接。那么,每次当服务器接受一个连接后,就新开一个线程,由该线程继续去处理和客户端的通信事宜,而服务器回到监听状态,继续等待新的连接。如代码2所示。
Socket client = serverSocket.Accept();
ThreadPool.QueueUserWorkItem(ClientProcess, client);
代码2
这里使用了线程池ThreadPool去建立新的线程,免去了自己新建线程要多写的代码。线程池的方法QueueUserWorkItem的第一个参数是一个带object参数的委托,该委托完成线程里要做的事情,ClientProcess方法中就是服务器和客户通信的操作;第二个参数就是委托方法是使用的参数,这里是将建立的socket作为参数传递给ClientProcess。代码3是ClinetProcess的实现,然而ClientProcees中并没有实现具体的通信,而只是执行了一个新的委托ConnectionEstablished,该事件参数SocketEventArgs直接继承自Even