/// <summary> /// Wird aufgerufen, wenn eine neue Nachricht empfangen wurde. /// An dieser Stelle werden NUR Nachrichten, die der Verbindungshaltung dienen, verarbeitet (ID kleiner als 9) /// </summary> /// <param name="msg">Die Nachricht</param> protected override void OnSMCPMessageReceived(SMCPMessage msg) { Log("Message Received: " + msg.ToString()); if (msg.ActionID <= 9) { switch ((SMCPAction)msg.ActionID) { case SMCPAction.ASSIGN_CLIENT_ID: LocalClient.ClientID = Serializer.ConvertToObject <short>(msg.Data); SMCPMessage reply = this.CreateMessage(LocalClient, false, SMCPAction.CLIENT_INFO); SendMessage(reply); break; case SMCPAction.ONLINE_CLIENTS: ConnectedClients = Serializer.ConvertToObject <Dictionary <short, ClientInfo> >(msg.Data); if (ConnectedClients.ContainsKey(0)) { if (ConnectedClients[0].IsServer) { ConnectedClients[0].TcpClient = Server.TcpClient; Server = ConnectedClients[0]; } } OnClientsChanged(); break; } } base.OnSMCPMessageReceived(msg); }
/// <summary> /// Wartet für neue SMCP-Nachrichten und leitet diese zur Verarbeitung weiter /// </summary> /// <param name="remClient">Der Client, bei dessen Stream auf Nachrichten gewartet werden soll</param> protected void ListenForSMCP(ClientInfo remClient) { NetworkStream clientStream = remClient.TcpClient.GetStream(); SMCPMessage message; //Wenn der EndPoint bald schließt --> aufhören auf Nachrichten zu warten! while (!IsClosing) { try { message = SMCPMessage.ReadNextMessage(clientStream); } catch (Exception e) { Console.WriteLine(e.Message); break; } if (message != null) { this.Invoke(new Action(() => { OnSMCPMessageReceived(message); })); } } //Verbindung schließen und angeben, dass die Verbindung verloren wurde (entsprechendes Event aufrufen) remClient.TcpClient.Close(); ConnectionLost(remClient); }
/// <summary> /// Startet den Anmeldeprozess eines neuen Clients /// </summary> /// <param name="newClient">Der neue Client</param> public void AcceptClient(ClientInfo newClient) { SMCPMessage msg = CreateMessage(newClient.ClientID, false, SMCPAction.ASSIGN_CLIENT_ID); SendMessage(msg, newClient); Log("New Client arrived"); }
/// <summary> /// Wenn ein Item gelöscht wurde, wird diese Aktion auch den anderen /// Teilnehmern mitgeteilt /// </summary> /// <param name="sender"></param> /// <param name="e">Das gelöschte Item</param> private void ScrumNetwork_ItemRemoved(object sender, ItemBase e) { if (IsConnected) { SMCPMessage msg = Connection.CreateMessage(e.ItemID, true, SMCPAction.REMOVE_ITEM); Connection.MulticastMessage(msg); } }
/// <summary> /// Wenn die Ansicht geändert wurde, wird die neu gewählte Ansicht /// den anderen Teilnehmern mitgeteilt /// </summary> /// <param name="sender"></param> /// <param name="e">Die ID der neuen Ansicht</param> private void ViewController_ViewChanged(object sender, GenericEventArgs <int> e) { if (IsConnected) { SMCPMessage msg = Connection.CreateMessage(e.Value, true, SMCPAction.VIEW_CHANGED); Connection.MulticastMessage(msg); } }
/// <summary> /// Verschickt eine SMCP-Nachricht an den angegebenen Client /// </summary> /// <param name="msg">Die Nachricht</param> /// <param name="client">Der Client</param> public void SendMessage(SMCPMessage msg, ClientInfo client) { if (client != null && client.TcpClient != null) { Log("Send: " + msg.Data.Length + " bytes, " + msg); msg.WriteMessage(client.TcpClient.GetStream()); } }
/// <summary> /// Wenn auf diesem Tisch der Editiermodus für ein Item aktiviert (oder beendet wurde) /// wird dies auch den anderen Teilnehmern mitgeteilt. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void Database_EditorStateChanged(object sender, ScrumTouchkit.Events.EditorStateEventArgs e) { if (IsConnected) { SMCPMessage msg = Connection .CreateMessage(e.Data, true, e.IsStarting ? SMCPAction.START_EDITING : SMCPAction.END_EDITING); Connection.MulticastMessage(msg); } }
/// <summary> /// Wenn die Eigenschaften eines Items geändert wurden, /// wird auch dies an dieser Stelle den anderen Teilnehmern mitgeteilt /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Database_ItemChanged(object sender, EventArgs e) { if (IsConnected) { ItemBase item = sender as ItemBase; SMCPMessage msg = Connection .CreateMessage(item, true, SMCPAction.UPDATE_ITEM); Connection.MulticastMessage(msg); } }
/// <summary> /// Wenn auf diesem Tisch ein Item in den Fokus gerückt wurde, /// wird dies an dieser Stelle den anderen Tischen mitgeteilt /// </summary> /// <param name="sender">Das Item, welches nun im Fokus ist</param> /// <param name="e"></param> private void Database_FocusRequested(object sender, EventArgs e) { if (IsConnected) { ItemBase item = (sender as ItemControl).Item; SMCPMessage msg = Connection .CreateMessage(item.ItemID, true, SMCPAction.FOCUS_ON_ITEM); Connection.MulticastMessage(msg); } }
protected virtual void OnSMCPMessageReceived(SMCPMessage msg) { if (msg.ActionID > 9) { if (SMCPMessageReceived != null) { SMCPMessageReceived(this, msg); } } }
/// <summary> /// Versendet eine SMCP-Nachricht an alle verbundenen Clients /// Dies hier ist die Server-Version! /// </summary> /// <param name="msg">Die Nachricht</param> public virtual void MulticastMessage(SMCPMessage msg) { foreach (ClientInfo ci in ConnectedClients.Values) { if (ci.ClientID != msg.SenderID && ci.ClientID != this.LocalClient.ClientID) { SendMessage(msg, ci); } } }
/// <summary> /// Verschickt eine Message, die alle verbundenen Clients beinhaltet /// </summary> /// <param name="receiver">Wenn angegeben, wird nur an diesen Client die NAchricht verschickt, ansonsten an alle</param> public void ShareConnectedClients(ClientInfo receiver = null) { SMCPMessage connectedClients = CreateMessage(ConnectedClients, true, SMCPAction.ONLINE_CLIENTS); if (receiver == null) { MulticastMessage(connectedClients); } else { SendMessage(connectedClients, receiver); } }
/// <summary> /// Erstellt eine SMCP-Nachricht und personalisiert diese für den /// lokalen Computer /// </summary> /// <param name="data">Das Object, welches verschickt weren soll (wird vorher serialisiert)</param> /// <param name="multicast">Gibt an, ob diese Nachricht an alle Client versendet werden soll</param> /// <param name="action">Die Aktion, die diese Nachricht aufruft</param> /// <returns>Gibt die fertige Nachricht zurück</returns> public SMCPMessage CreateMessage(object data, bool multicast, SMCPAction action) { SMCPMessage msg = new SMCPMessage(); msg.SenderID = LocalClient.ClientID; if (data != null) { msg.Data = Serializer.ObjectToByteArray(data); } else { msg.Data = new byte[0]; } msg.Multicast = multicast; msg.ActionID = (byte)action; return(msg); }
/// <summary> /// Teilt dem Netzwerk die aktuelle, lokale Item-Datenbank mit. /// Falls eine ClientInfo angegeben ist, wird nur diesem Client /// die Datenbank mitgeteilt /// </summary> /// <param name="ci">[Optional] Der Empfänger</param> private void ShareDatabase(ClientInfo ci = null) { if (IsConnected) { SendableDatabase sdb = new SendableDatabase(); sdb.Epics = Surface.Database.Epics; sdb.UserStories = Surface.Database.UserStories; SMCPMessage msg = Connection.CreateMessage(sdb, ci == null, SMCPAction.ALL_ITEMS); if (msg.Multicast) { Connection.MulticastMessage(msg); } else { Connection.SendMessage(msg, ci); } } }
/// <summary> /// Wenn eine neue UserStory erstellt wurde, wird diese automatisch /// mit dem Netzwerk geteilt. Mit diesem Programm können bisher <u>keine</u> /// neuen UserStories erstellt werden, daher wird es an dieser Stelle auch /// nicht betrachtet. /// Client: Item Teilen - Schritt 1 / 3 /// Server: Item Teilen - Schritt 1 / 2 /// </summary> /// <param name="sender"></param> /// <param name="e">Die neue UserStory</param> private void Surface_StoryCreated(object sender, ScrumTouchkit.Data.UserStory e) { if (IsConnected) { if (Connection.IsServer) { //Wenn der lokale Client ein Server ist, muss nicht erst der neue Server //kontaktiert werden, um an eine UserStoryID zu gelangen. e.ItemID = UserStory.NextUserStoryID++; ShareItemFinally(e); } else { WaitingForID.Enqueue(e); SMCPMessage msg = Connection.CreateMessage(null, false, SMCPAction.REQUEST_ITEM_ID); (Connection as ScrumMeetingClient).SendMessage(msg); } } }
/// <summary> /// Verarbeitet ankommende SMCP-Nachrichten /// Hier werden allerdings nur für die Verbindungsverwaltung wichtige /// Nachrichten (ID kleiner als 9) bearbeitet. /// Alle weiteren Nachrichten müssen außerhalb bearbeitet werden /// </summary> /// <param name="msg">Die ankommende Nachricht</param> protected override void OnSMCPMessageReceived(SMCPMessage msg) { Log("Message Received: " + msg.Data); if (msg.Multicast) { MulticastMessage(msg); } if (msg.ActionID <= 9) { switch ((SMCPAction)msg.ActionID) { case SMCPAction.CLIENT_INFO: { ClientInfo ci = Serializer.ConvertToObject <ClientInfo>(msg.Data); ci.TcpClient = ConnectedClients[ci.ClientID].TcpClient; ConnectedClients[ci.ClientID] = ci; OnClientsChanged(); OnClientAccepted(ci); } break; } } base.OnSMCPMessageReceived(msg); }
/// <summary> /// Wenn die neue UserStory eine ID erhalten hat, kann sie /// endgültig mit dem Netzwerk geteilt werden /// Client: Item Teilen - Schritt 3 / 3 /// Server: Item Teilen - Schritt 2 / 2 /// </summary> /// <param name="item">Das Item, das geteilt werden soll</param> private void ShareItemFinally(ItemBase item) { SMCPMessage msg = Connection.CreateMessage(item, true, SMCPAction.ADD_ITEM); Connection.MulticastMessage(msg); }
/// <summary> /// MultiCast Nachrichten werden von Clients zunächst an den Server geschickt /// </summary> /// <param name="msg">Die Nachricht</param> public override void MulticastMessage(SMCPMessage msg) { base.SendMessage(msg, Server); }
/// <summary> /// Verschickt eine Nachricht an den Server /// Direktes senden an andere Clients ist bei Clients nicht möglich, daher zunächst an den Server schicken. /// </summary> /// <param name="msg">Die Nachricht</param> public void SendMessage(SMCPMessage msg) { base.SendMessage(msg, Server); }
/// <summary> /// Verarbeitet ankommende SMCP-Nachrichten /// </summary> /// <param name="sender"></param> /// <param name="e">Die SMCP-Nachricht</param> private void Connection_SMCPMessageReceived(object sender, Protocol.SMCPMessage e) { switch ((SMCPAction)e.ActionID) { case SMCPAction.VIEW_CHANGED: ///Die Ansicht wurde im Netzwerk verändert -> auch auf diesem Client ändern ChangeView(e.GetData <int>(this.Connection)); break; case SMCPAction.REQUEST_ITEM_ID: { // Einer der Clients hat eine neue Item-ID angefragt -> Falls es sich hier um einen Server handelt, // schicke eine neue ID zurück if (this.Connection.IsServer) { SMCPMessage respone = Connection.CreateMessage(UserStory.NextUserStoryID++, false, SMCPAction.ASSIGN_ITEM_ID); Connection.SendMessage(respone, Connection.ConnectedClients[e.SenderID]); } } break; case SMCPAction.ASSIGN_ITEM_ID: //Neue Item-ID wurde angefragt und erhalten -> weise sie einem der wartenden Items zu //Client: Item Teilen - Schritt 2 / 3 ItemBase item = WaitingForID.Dequeue(); if (item != null) { item.ItemID = e.GetData <short>(this.Connection); ShareItemFinally(item); } break; case SMCPAction.ADD_ITEM: { //Neues Item erhalten, zu den Items der Oberfläche hinzufügen AddItem(e.GetData <ItemBase>(this.Connection)); } break; case SMCPAction.START_EDITING: //In den (Readonly)-Editiermodus eines Items wechseln ChangeEditorState(e.GetData <ItemBase>(this.Connection).ItemID, true); break; case SMCPAction.END_EDITING: //Den Editiermodus wieder beenden ChangeEditorState(e.GetData <ItemBase>(this.Connection).ItemID, false); break; case SMCPAction.UPDATE_ITEM: //Die Daten eines Items aktualisieren (wurde von einem anderem Teilnehmer initiiert) UpdateItem(e.GetData <ItemBase>(this.Connection)); break; case SMCPAction.FOCUS_ON_ITEM: //Ein Item in den Fokus aller Teilnehmer rücken (wurde von einem anderem Teilnehmer initiiert) RequestFocus(e.GetData <short>(this.Connection)); break; case SMCPAction.REMOVE_ITEM: //Ein Item wurde von einem anderen Teilnehmer gelöscht RemoveItem(e.GetData <short>(this.Connection)); break; case SMCPAction.ALL_ITEMS: //Liste von Items erhalten -> Aktuelle Datenbank durch Internet Datenbank ersetzen Surface.Invoke(() => { SendableDatabase sdb = e.GetData <SendableDatabase>(this.Connection); sdb.LoadIntoDB(Surface.Database); }); break; } }