在C/S模式程序开发中需要实现一个函数,比如:
string GetUserName(int id)
{
}
它的实现需求是通过 TCP 向 Server 端请求指定用户 Id 所对应的用户名。
通常实现 socket 通讯的步骤是,我们先构造一个请求包,包含请求的指令和参数。
然后把它 Send() 出去。
Server 端接收到这个请求,查询符合条件的信息,并向客户端发送请求结果。
客户端的接收线程接收到 Server 端发来的信息,对其进行解析,并执行相应的操作。
这样的话,我势必需要在 GetUserName 函数中实现请求,并通过另一个函数去获取查询结果。
因为对于网络通讯来说,发起和获取可以说是异步的,这对于 GetUserName 函数的使用者来说显然太麻烦了。
那么怎么才能实现 string username=GetUserName(10); 这样的调用呢?
考虑过在 GetUserName 函数中做 while 循环,去不断检测所需的数据是否已返回,但这样开销太大。
于是想到了采用 AutoResetEvent。
AutoResetEvent autoEvent = new AutoResetEvent(false); //将初始状态设置为非终止
/// <summary>
/// 根据用户 ID 查询用户名
/// </summary>
/// <param name="id">用户 ID</param>
/// <returns>用户名</returns>
public string GetUserName(int id)
{
/*
* 这里放置请求封包构造代码
*/
byte[] data=packet.GetData();
s.Send(data);
if (autoEvent.WaitOne(5000, false)) //这里定义了 5 秒的超时时间
{
return result; //这个是示例,result 从 RecvUserName 中进行赋值
}
else
{
throw new Exception("服务器查询超时!");
}
}
/*
*当 socket 读取线程读取到 Server 返回的用户名信息的封包头信息后调用 RecvUserName 函数
* socket 读取线程部分代码略
*/
/// <summary>
/// 接收服务器返回信息
/// </summary>
private void RecvUserName()
{
/*
* 这里编写封包拆解代码
*/
result = username;
autoEvent.Set(); //将事件状态设置为终止状态,允许等待线程继续。
}
搞定!
这样的话 GetUserName 函数在执行 s.Send(data); 后会被挂起,直到超时或者 autoEvent 的事件状态被设置为终止状态。
然后才会执行之后的代码。
这样就能通过
string username=GetUserName(10); 这样的调用方式来返回用户名信息了。
这里只是简单说了下实现方法,其它具体的逻辑关系处理大家自己去想了,呵呵!