在函数中合并套接字请求/响应

本文关键字:请求 响应 套接字 合并 函数 | 更新日期: 2023-09-27 17:56:32

我正在尝试实现通过套接字通信的客户端/服务器应用程序。沟通工作正常,我可以匹配和执行不同的功能。大多数数据包来自请求/响应类型,并与持久序列号匹配。

我现在要说的问题是,我无法将响应引回最初发送请求的方法。我想实现这一点以使整体使用更简单。

下面是请求/响应通信的序列图:通信示例

这是我目前(不起作用)的方法:

发起函数调用:

public bool Login(string username, string password) {
    bool success = State.Send(new LoginPacket(username, password));
    return success;
}

状态是具有打开套接字和发送/接收函数的通信对象。

收到答案后,Send-方法返回响应中的值。

状态对象的 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 ();
            }
        ...
    }

承诺类:

public class Promise<T>
{
    private ManualResetEvent m_ResetEvent = new ManualResetEvent (false);
    private T m_ReturnValue {get; set;}
}

我对这种方法的问题是,我无法将 Promisses 存储在字典中,因为它们来自不同的类型:

    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 s 而不是您自己的类。它们非常灵活,允许您非常轻松地使用异步代码 - 更不用说它们为您正确处理同步;我展示的代码实际上并不完全是线程安全的。要使其完全安全并不容易 - 只需坚持Task,他们已经安全地处理了所有这些。