在函数中合并套接字 request/response
Merging socket request/response in a function
我正在尝试实现一个通过套接字进行通信的 client/server 应用程序。通信工作正常,我可以匹配和执行不同的功能。大多数数据包来自请求/响应类型,并与持久序列号匹配。
我现在要说明的问题是我无法将响应引回到最初发送请求的方法。我想实现这一点,使整体使用更简单。
这是 request/response 通信的时序图:
Communication sample
这是我目前(无效)的方法:
发起函数调用:
public bool Login(string username, string password) {
bool success = State.Send(new LoginPacket(username, password));
return success;
}
State是打开socket的通信对象,send/receive函数
收到答复后,发送方法returns响应中的值。
State-object 的 Send 和 HandleReceive 方法:
public virtual T Send<T>(RequestResponsePacket<T> sp) {
...
try {
m_Socket.BeginSend (buffer, 0, length, SocketFlags.None, m_OnSend, m_Socket);
m_SendDone.WaitOne ();
Promise<T> promise = new Promise<T> ();
m_UnansweredRequests.Add (sequenceNr, promise);
promise.ResetEvent.WaitOne ();
sp.OnSend ();
return promise.ReturnValue;
} catch (Exception ex) {
TraceException (ex);
Dispose (false);
}
...
}
public bool HandleReceive() {
ByteQueue buffer = Buffer;
...
ushort reqid = buffer.GetRequestID ();
Promise<?> p;//Doesn't work
m_UnansweredRequests.TryGetValue (reqid, out promise);
if (promise != null) {
promise.ReturnValue = buffer.GetReturnValue();
promise.ResetEvent.Set ();
}
...
}
承诺class:
public class Promise<T>
{
private ManualResetEvent m_ResetEvent = new ManualResetEvent (false);
private T m_ReturnValue {get; set;}
}
我对这种方法的问题是,我无法将 Promise 存储在字典中,因为它们来自不同的类型:
private Dictionary<ushort, Promise<?>> m_UnansweredPackets; //Doesn't work
是否有其他方法可以实现我的目标?我错过了什么吗?
简单的解决方案是在将 Promise
添加到字典时忽略泛型参数。这可以通过继承或接口轻松完成,具体取决于您的需要(例如,Task<T>
继承自 Task
)。与 Java 不同,您不能像您尝试的那样简单地使用 Promise<?>
。
例如:
public interface IPromise
{
void Complete(ByteQueue data);
}
public class Promise<T> : IPromise
{
public ManualResetEvent ResetEvent = new ManualResetEvent (false);
public T ReturnValue {get; private set;}
private object syncObject = new object();
public void Complete(ByteQueue data)
{
ReturnValue = data.GetReturnValue();
ResetEvent.Set();
}
}
这允许您只在字典中使用 IPromise
,并在收到响应时调用 Complete(buffer)
,而无需了解真实类型。
但是,您可能仍想使用 Task
而不是您自己的 class。它们非常灵活,允许您很容易地使用异步代码——更不用说它们为您正确处理同步;我展示的代码实际上 并不是 完全线程安全的。让它完全安全并不容易 - 只要坚持 Task
s,他们已经安全地处理了所有这些。
我正在尝试实现一个通过套接字进行通信的 client/server 应用程序。通信工作正常,我可以匹配和执行不同的功能。大多数数据包来自请求/响应类型,并与持久序列号匹配。
我现在要说明的问题是我无法将响应引回到最初发送请求的方法。我想实现这一点,使整体使用更简单。
这是 request/response 通信的时序图: Communication sample
这是我目前(无效)的方法:
发起函数调用:
public bool Login(string username, string password) {
bool success = State.Send(new LoginPacket(username, password));
return success;
}
State是打开socket的通信对象,send/receive函数
收到答复后,发送方法returns响应中的值。
State-object 的 Send 和 HandleReceive 方法:
public virtual T Send<T>(RequestResponsePacket<T> sp) {
...
try {
m_Socket.BeginSend (buffer, 0, length, SocketFlags.None, m_OnSend, m_Socket);
m_SendDone.WaitOne ();
Promise<T> promise = new Promise<T> ();
m_UnansweredRequests.Add (sequenceNr, promise);
promise.ResetEvent.WaitOne ();
sp.OnSend ();
return promise.ReturnValue;
} catch (Exception ex) {
TraceException (ex);
Dispose (false);
}
...
}
public bool HandleReceive() {
ByteQueue buffer = Buffer;
...
ushort reqid = buffer.GetRequestID ();
Promise<?> p;//Doesn't work
m_UnansweredRequests.TryGetValue (reqid, out promise);
if (promise != null) {
promise.ReturnValue = buffer.GetReturnValue();
promise.ResetEvent.Set ();
}
...
}
承诺class:
public class Promise<T>
{
private ManualResetEvent m_ResetEvent = new ManualResetEvent (false);
private T m_ReturnValue {get; set;}
}
我对这种方法的问题是,我无法将 Promise 存储在字典中,因为它们来自不同的类型:
private Dictionary<ushort, Promise<?>> m_UnansweredPackets; //Doesn't work
是否有其他方法可以实现我的目标?我错过了什么吗?
简单的解决方案是在将 Promise
添加到字典时忽略泛型参数。这可以通过继承或接口轻松完成,具体取决于您的需要(例如,Task<T>
继承自 Task
)。与 Java 不同,您不能像您尝试的那样简单地使用 Promise<?>
。
例如:
public interface IPromise
{
void Complete(ByteQueue data);
}
public class Promise<T> : IPromise
{
public ManualResetEvent ResetEvent = new ManualResetEvent (false);
public T ReturnValue {get; private set;}
private object syncObject = new object();
public void Complete(ByteQueue data)
{
ReturnValue = data.GetReturnValue();
ResetEvent.Set();
}
}
这允许您只在字典中使用 IPromise
,并在收到响应时调用 Complete(buffer)
,而无需了解真实类型。
但是,您可能仍想使用 Task
而不是您自己的 class。它们非常灵活,允许您很容易地使用异步代码——更不用说它们为您正确处理同步;我展示的代码实际上 并不是 完全线程安全的。让它完全安全并不容易 - 只要坚持 Task
s,他们已经安全地处理了所有这些。