如何在通过 ChannelFactory 创建的 WCF 服务上设置回调通道?

Dan*_*iel 5 .net c# wcf client-server callback

我需要我的 WCF 服务来向客户端引发事件。我已经读到通过回调通道发生这种情况,并且我已经通过以下方式实现了它:服务接口:

public interface IServiceCallback
{
    [OperationContract(IsOneWay = true)]
    void OnNewAlert(Alert a);
    [OperationContract(IsOneWay = true)]
    void OnProductEdited(Product p);
    [OperationContract(IsOneWay = true)]
    void OnHighlightChanged(Dictionary<User, List<Product>> highlighted);
    [OperationContract(IsOneWay = true)]
    void OnCatalogUpdated();


    event EventHandler NewAlert;
    event EventHandler ProductEdited;
    event EventHandler HighlightChanged;
    event EventHandler CatalogUpdated;
}
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
public interface IService : IDisposable
{
    [OperationContract]
    List<Product> GetProducts(Predicate<Product> match = null, int limit = 0, string username = null);
    [OperationContract]
    Product GetProduct(Predicate<Product> match, string username = null);
    [OperationContract]
    Product GetRandomProduct(Predicate<Product> match = null, string username = null);
    [OperationContract]
    int GetFlagIndex(string flagName);
    [OperationContract]
    void SetFlag(string pid, string flagName, bool value);
    [OperationContract]
    List<Alert> GetAlerts(string username);
    [OperationContract]
    void DismissAlert(Alert alert, String username);
    [OperationContract]
    void HighlightProduct(List<string> pids, string user);
    [OperationContract]
    void EditProduct(string pid, Dictionary<string, object> fieldValues, string username = null);
    [OperationContract]
    void AttachModule(IModule m);
    [OperationContract]
    void Ping();

    event EventHandler NewAlert;
    event EventHandler ProductEdited;
    event EventHandler HighlightChanged;
    event EventHandler CatalogUpdated;
}
Run Code Online (Sandbox Code Playgroud)

服务实施:

namespace Service
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class ServiceInstance : IService
{
    List<IServiceCallback> callbackChannels = new List<IServiceCallback>();
    //other vars

    public ServiceInstance()
    {
            //lots of stuff here
    }

    private User SignalUser(string username)
    {
        if (username == null)
            return null;

        IServiceCallback channel = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
        if (!callbackChannels.Contains(channel)) //if CallbackChannels not contain current one.
        {
            callbackChannels.Add(channel);
        }

        User user = knownUsers.Find(p => p.username == username);
        if (user == null)
        {
            user = new User();
            user.username = username;
            user.highlighColor = Color.FromArgb(r.Next(0, 128), r.Next(0, 128), r.Next(0, 128));
            knownUsers.Add(user);
            foreach (KeyValuePair<Alert, List<User>> kvp in alerts)
            {
                kvp.Value.Add(user);
            }
        }
        user.lastOnline = DateTime.Now;
        if(!onlineUsers.Contains(user))
            onlineUsers.Add(user);

        return user;
    }

    //lots of other things here
}
}
Run Code Online (Sandbox Code Playgroud)

客户端回调实现:

class ServiceEventHandler : IServiceCallback
{
    public event EventHandler NewAlert;
    public event EventHandler ProductEdited;
    public event EventHandler HighlightChanged;
    public event EventHandler CatalogUpdated;

    public void OnCatalogUpdated()
    {
        CatalogUpdated?.BeginInvoke(null, null, null, null);
    }

    public void OnHighlightChanged(Dictionary<User, List<Product>> highlighted)
    {
        HighlightChanged?.BeginInvoke(highlighted, EventArgs.Empty, null, null);
    }

    public void OnNewAlert(Alert a)
    {
        NewAlert?.BeginInvoke(a, EventArgs.Empty, null, null);
    }

    public void OnProductEdited(Product p)
    {
        ProductEdited?.BeginInvoke(p, EventArgs.Empty, null, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

但这是我的问题: 在客户端,我应该像这样将它传递给服务:

EventHandler eventHandler = new EventHandler();
MyServiceClient client = new MyServiceClient(new InstanceContext(eventHandler));
Run Code Online (Sandbox Code Playgroud)

根据这个 StackOverflow 答案:https ://stackoverflow.com/a/1143777/2018696

但是我并没有像这样连接到我的服务,因为我的客户端不知道服务的实现,它只知道两个接口!所以我像这样连接:

    public static IService GetService(string serviceAddress)
    {
        Uri service_uri = new Uri(serviceAddress);
        var endpoint = new EndpointAddress(service_uri, new[] { AddressHeader.CreateAddressHeader(settings["username"], "", "") });
        IService service = ChannelFactory<IService>.CreateChannel(new BasicHttpBinding(), endpoint);
        return service;
    }
Run Code Online (Sandbox Code Playgroud)

那么如何让回调工作呢?

更新:

好的,正如评论所建议的那样,我用 DuplexChannelFactory 替换了 ChannelFactory,用 WsDualHTTPBinding 替换了 BasicHTTPBinding,但我没有得到服务器的响应。如果我划伤回调处理程序,我确实会收到 BasicHTTPBinding 的响应。所以本质上:

[ServiceContract]
BasicHttpBinding();
ChannelFactory<IService>.CreateChannel(binding, endpoint);
Run Code Online (Sandbox Code Playgroud)

^ 这有效

[ServiceContract(CallbackContract = typeof(IServiceCallback))]
WSDualHttpBinding(WSDualHttpSecurityMode.None);
DuplexChannelFactory<IService>.CreateChannel(new InstanceContext(handler), binding, endpoint);
Run Code Online (Sandbox Code Playgroud)

^ 这不是。

它适用于本地主机,但不适用于 LAN 或 Internet。服务器和客户端上的防火墙都已关闭。当我尝试联系服务器时出现 60 秒超时。