Mon code au moment ressemble à ceci:
côté serveur:Le client d'un service duplex WCF (liaison TCP) peut-il envoyer et recevoir en même temps?
#region IClientCallback interface
interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveWcfElement(WcfElement wcfElement);
}
#endregion
#region IService interface
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
interface IService
{
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void ReadyToReceive(string userName, int source, string ostatniTypWiadomosci);
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)]
bool SendWcfElement(WcfElement wcfElement);
[OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
List<int> Login(Client name, string password, bool isAuto, bool isSuperMode);
}
#endregion
#region Public enums/event args
public delegate void WcfElementsReceivedFromClientEventHandler(object sender, WcfElementsReceivedFromClientEventArgs e);
public class WcfElementsReceivedFromClientEventArgs : EventArgs
{
public string UserName;
}
public class ServiceEventArgs : EventArgs
{
public WcfElement WcfElement;
public Client Person;
}
#endregion
#region Service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService
{
#region Instance fields
//thread sync lock object
private static readonly Object SyncObj = new Object();
//callback interface for clients
IClientCallback _callback;
//delegate used for BroadcastEvent
public delegate void ChatEventHandler(object sender, ServiceEventArgs e);
public static event ChatEventHandler ChatEvent;
private ChatEventHandler _myEventHandler;
//holds a list of clients, and a delegate to allow the BroadcastEvent to work
//out which chatter delegate to invoke
static readonly Dictionary<Client, ChatEventHandler> Clients = new Dictionary<Client, ChatEventHandler>();
//current person
private Client _client;
#endregion
#region Helpers
private bool CheckIfPersonExists(string name)
{
return Clients.Keys.Any(p => p.UserName.Equals(name, StringComparison.OrdinalIgnoreCase));
}
private ChatEventHandler getPersonHandler(string name)
{
foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
ChatEventHandler chatTo;
Clients.TryGetValue(c, out chatTo);
return chatTo;
}
return null;
}
private Client GetPerson(string name)
{
return Clients.Keys.FirstOrDefault(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase));
}
#endregion
#region IService implementation
public List<int> Login(Client client, string password, bool isAuto, bool isSuperMode)
{
if (client.ElementsVersions == null)
{
client.ElementsVersions = new WcfElement(WcfElement.RodzajWiadomosci.VersionControl, client.UserName);
}
//create a new ChatEventHandler delegate, pointing to the MyEventHandler() method
_myEventHandler = MyEventHandler;
lock (SyncObj)
{
if (!CheckIfPersonExists(client.UserName))
{
_client = client;
Clients.Add(client, _myEventHandler);
}
else
{
_client = client;
foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(client.UserName)))
{
ChatEvent -= Clients[c];
Clients.Remove(c);
break;
}
Clients[client] = _myEventHandler;
}
_client.LockObj = new object();
}
_callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
ChatEvent += _myEventHandler;
var rValue = isAuto ? bazaDanych.Login(client.UserName, isSuperMode) : bazaDanych.Login(client.UserName, password);
return rValue;
}
public void PerformDataSync(Client c)
{
WcfElement wcfDelete = null;
WcfElement wcfUpdate = null;
//...
//this method prepares elements for client
//when done it adds them to clients queue (List<WcfElement)
try
{
var counter = 0;
if (wcfDelete != null)
{
foreach (var wcf in WcfElement.SplitWcfElement(wcfDelete, false))//split message into small ones
{
c.AddElementToQueue(wcf, counter++);
}
}
if (wcfUpdate != null)
{
foreach (var wcf in WcfElement.SplitWcfElement(wcfUpdate, true))
{
c.AddElementToQueue(wcf, counter++);
}
}
SendMessageToGui(string.Format("Wstępna synchronizacja użytkownika {0} zakończona.", c.UserName));
c.IsSynchronized = true;
}
catch (Exception e)
{
}
}
private void SendMessageToClient(object sender, EventArgs e)
{
var c = (Client) sender;
if (c.IsReceiving || c.IsSending)
{
return;
}
c.IsReceiving = true;
var wcfElement = c.GetFirstElementFromQueue();
if (wcfElement == null)
{
c.IsReceiving = false;
return;
}
Clients[c].Invoke(this, new ServiceEventArgs { Person = c, WcfElement = wcfElement });
}
public void ReadyToReceive(string userName)
{
var c = GetPerson(userName);
c.IsSending = false;
c.IsReceiving = false;
if (c.IsSynchronized)
{
SendMessageToClient(c, null);
}
else
{
PerformDataSync(c);
}
}
public bool SendWcfElement(WcfElement wcfElement)
{
var cl = GetPerson(wcfElement.UserName);
cl.IsSending = true;
if (wcfElement.WcfElementVersion != bazaDanych.WcfElementVersion) return false;
//method processes messages and if needed creates creates WcfElements which are added to every clients queue
return ifSuccess;
}
#endregion
#region private methods
private void MyEventHandler(object sender, ServiceEventArgs e)
{
try
{
_callback.ReceiveWcfElement(e.WcfElement);
}
catch (Exception ex)
{
}
}
#endregion
}
#endregion
côté client dans un moment
#region Client class
[DataContract]
public class Client
{
#region Instance Fields
/// <summary>
/// The UserName
/// </summary>
[DataMember]
public string UserName { get; set; }
[DataMember]
public WcfElement ElementsVersions { get; set; }
private bool _isSynchronized;
public bool IsSynchronized
{
get { return _isSynchronized; }
set
{
_isSynchronized = value;
}
}
public bool IsSending { get; set; }
public bool IsReceiving { get; set; }
private List<WcfElement> ElementsQueue { get; set; }
public object LockObj { get; set; }
public void AddElementToQueue(WcfElement wcfElement, int position = -1)
{
try
{
lock (LockObj)
{
if (ElementsQueue == null) ElementsQueue = new List<WcfElement>();
if (position != -1 && position <= ElementsQueue.Count)
{
try
{
ElementsQueue.Insert(position, wcfElement);
}
catch (Exception e)
{
}
}
else
{
try
{
//dodaje na koncu
ElementsQueue.Add(wcfElement);
}
catch (Exception e)
{
}
}
}
}
catch (Exception e)
{
}
}
public WcfElement GetFirstElementFromQueue()
{
if (ElementsQueue == null) return null;
if (ElementsQueue.Count > 0)
{
var tmp = ElementsQueue[0];
ElementsQueue.RemoveAt(0);
return tmp;
}
return null;
}
#endregion
#region Ctors
/// <summary>
/// Assign constructor
/// </summary>
/// <param name="userName">The userName to use for this client</param>
public Client(string userName)
{
UserName = userName;
}
#endregion
}
#endregion
ProxySingletion:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
public sealed class ProxySingleton : IClientCallback
{
#region Instance Fields
private static ProxySingleton _singleton;
public static bool IsConnected;
private static readonly object SingletonLock = new object();
private ServiceProxy _proxy;
private Client _myPerson;
private delegate void HandleDelegate(Client[] list);
private delegate void HandleErrorDelegate();
//main proxy event
public delegate void ProxyEventHandler(object sender, ProxyEventArgs e);
public static event ProxyEventHandler ProxyEvent;
//callback proxy event
public delegate void ProxyCallBackEventHandler(object sender, ProxyCallBackEventArgs e);
public static event ProxyCallBackEventHandler ProxyCallBackEvent;
#endregion
#region Ctor
/// <summary>
/// Blank constructor
/// </summary>
private ProxySingleton()
{
}
#endregion
#region Public Methods
#region IClientCallback implementation
public void ReceiveWcfElement(WcfElement wcfElement)
{
//process received data
//...
ReadyToReceive();
}
#endregion
public void ReadyToReceive()
{
try
{
if (bazaDanych.Dane.Client.IsSending) return;
var w = bazaDanych.Dane.Client.GetFirstElementFromQueue();
if (w != null)
{
SendWcfElement(w);
return;
}
_proxy.ReadyToReceive(bazaDanych.Dane.Client.UserName, source, ostatniTypWiadomosci);
}
catch (Exception)
{
IsConnected = false;
}
}
public static WcfElement CurrentWcfElement;
public bool SendWcfElement(WcfElement wcfElement)
{
if (bazaDanych.Dane.Client.IsReceiving)
{
bazaDanych.Dane.Client.AddElementToQueue(wcfElement);
return true;
}
bazaDanych.Dane.Client.IsSending = true;
foreach (var wcfElementSplited in WcfElement.SplitWcfElement(wcfElement, true))
{
CurrentWcfElement = wcfElementSplited;
try
{
var r = _proxy.SendWcfElement(wcfElementSplited);
CurrentWcfElement = null;
}
catch (Exception e)
{
IsConnected = false;
return false;
}
}
bazaDanych.Dane.Client.IsSending = false;
ReadyToReceive();
return true;
}
public void ListenForConnectOrReconnect(EventArgs e)
{
SendWcfElement(WcfElement.GetVersionElement());//send wcfelement for perform PerformDataSync
ReadyToReceive();
}
public static bool IsReconnecting;
public bool ConnectOrReconnect(bool shouldRaiseEvent = true)
{
if (IsReconnecting)
{
return IsConnected;
}
if (IsConnected) return true;
IsReconnecting = true;
bazaDanych.Dane.Client.IsReceiving = false;
bazaDanych.Dane.Client.IsSending = false;
bazaDanych.Dane.Client.IsSynchronized = false;
try
{
var site = new InstanceContext(this);
_proxy = new ServiceProxy(site);
var list = _proxy.Login(bazaDanych.Dane.Client, bazaDanych.Dane.UserPassword, bazaDanych.Dane.UserIsAuto, bazaDanych.Dane.UserIsSuperMode);
bazaDanych.Dane.UserRights.Clear();
bazaDanych.Dane.UserRights.AddRange(list);
IsConnected = true;
if (shouldRaiseEvent) ConnectOrReconnectEvent(null);
}
catch (Exception e)
{
IsConnected = false;
}
IsReconnecting = false;
return IsConnected;
}
}
#endregion
Au moment de mes œuvres app comme ceci: Après connexion réussie chaque client envoie WcfElements (qui contient tas de lis t avec des identifiants et des versions d'éléments). Ensuite, il envoie un message à sens unique ReadyToReceive qui après la connexion déclenche la méthode performingync. Cette méthode prépare les données pour le client et envoie le premier en utilisant la méthode de réception unidirectionnelle. S'il y a plus d'un wcfelement à envoyer alors seulement le dernier est marqué comme dernier. Le client répond avec ReadyToReceive après chaque réception réussie du serveur. Tout jusqu'à ce point fonctionne très bien. Le problème commence plus tard. La plupart du temps, les paquets sont perdus (méthode receiveWcfElement). Le serveur a marqué que le client reçoit et peut-être traite le message et attend le paquet readytoreceive, qui ne sera jamais envoyé à cause d'un élément perdu.
Je l'ai fait comme ça parce que, autant que je sache, le client ne peut pas envoyer et recevoir en même temps. J'ai essayé ceci et j'ai eu ce problème: Si le client envoie wcfElement avec la méthode SendWcfElement et le serveur en raison du traitement de cet élément créé un autre élément qui était supposé être retourné au client alors le client aurait un proxy défectueux si le callback était envoyé avant sendWcfElement retourné vrai indiquant que la méthode a été complétée.
Maintenant, je me demande s'il est possible pour le client d'envoyer et de recevoir en même temps en utilisant des méthodes bidirectionnelles? Je me suis retrouvé avec des services (deux connexions)
Pourriez-vous s'il vous plaît fournir un code? Et formatez votre question afin qu'un humain puisse la lire s'il vous plait. – Felix
Je viens d'ajouter du code et j'ai essayé de résumer mon problème mieux. J'espère que c'est plus clair maintenant. – Gwynnbleid1