//Construct public VersionExchange(ParticipantModel participant) { m_RemoteParticipant = participant; //Check local role using (Synchronizer.Lock(PresenterModel.TheInstance.Participant.SyncRoot)) { if (PresenterModel.TheInstance.Participant.Role is InstructorModel) { this.m_LocalNodeIsInstructor = true; } } //Check and subscribe for changes to remote role using (Synchronizer.Lock(m_RemoteParticipant.SyncRoot)) { m_RemoteHumanName = m_RemoteParticipant.HumanName; if (m_RemoteParticipant.Role is InstructorModel) { m_RemoteNodeIsInstructor = true; } m_RemoteParticipant.Changed["Role"].Add(new PropertyEventHandler(RoleChangedHandler)); } //Flag instructor nodes to initiate the exchange by sending the request message. if (m_LocalNodeIsInstructor) { m_SendRequest = true; } }
/// <summary> /// Called when a VersionRequestMessage is received /// </summary> /// <param name="remoteParticipant"></param> /// <param name="version"></param> public void ReceiveVersionRequest(ParticipantModel remoteParticipant, VersionRequestMessage requestMessage) { Trace.WriteLine("VersionExchangeModel.ReceiveVersionRequest started. remoteParticipant=" + remoteParticipant.Guid.ToString() + "; requesterID= " + requestMessage.RequesterId.ToString()); VersionExchange ve; //Make a VersionExchange object if needed. using (Synchronizer.Lock(this.SyncRoot)) { if (m_VersionExchangeDictionary.ContainsKey(remoteParticipant.Guid)) { ve = m_VersionExchangeDictionary[remoteParticipant.Guid]; } else { //This probably should not happen, but if it does we are covered. ve = new VersionExchange(remoteParticipant); m_VersionExchangeDictionary.Add(remoteParticipant.Guid, ve); m_VersionExchanges.Add(ve); Trace.WriteLine("Created a VersionExchange in response to Version Request from participant: " + remoteParticipant.Guid.ToString()); } } //Let the VersionExchange object process the inbound message ve.ReceiveRequest(requestMessage.RequesterVersion); }
public RoleSynchronizer(SlideViewer sv, ParticipantModel pm) { this.m_SlideViewer = sv; this.m_Participant = pm; this.m_Participant.Changed["Role"].Add(new PropertyEventHandler(this.onRoleChange)); this.onRoleChange(this, null); }
public TCPClient(IPEndPoint remoteEP, ParticipantModel participant, NetworkModel network, Guid serverID) { m_Connected = false; m_Network = network; m_Participant = participant; m_LastMsgReceived = DateTime.MaxValue; m_ServerId = serverID; m_ClientTimeout = TimeSpan.FromMilliseconds(TCP_HEARTBEAT_TIMEOUT_DEFAULT_MS); this.m_RemoteEP = remoteEP; m_Socket = null; m_ServerParticipant = null; m_ReceiveQueue = new Queue(); this.m_Encoder = new Chunk.ChunkEncoder(); m_NetworkStatus = new NetworkStatus(ConnectionStatus.Disconnected, ConnectionProtocolType.TCP, TCPRole.Client, 0); this.m_Network.RegisterNetworkStatusProvider(this, true, m_NetworkStatus); //Find out if client-side bridging is enabled m_BridgeEnabled = false; string enableBridge = System.Configuration.ConfigurationManager.AppSettings[this.GetType().ToString() + ".EnableBridge"]; if (enableBridge != null) { bool enable = false; if (bool.TryParse(enableBridge, out enable)) { Trace.WriteLine("Unicast to Multicast Bridge enabled=" + enable.ToString(), this.GetType().ToString()); m_BridgeEnabled = enable; } } }
/// <summary> /// Removes the specified <see cref="ParticipantModel"/> from the collection, if present. /// </summary> public void Remove(ParticipantModel value) { List.Remove(value); if (PresenterModel.TheInstance != null) { PresenterModel.TheInstance.VersionExchange.RemoveVersionExchange(value); } }
public ClientData(Socket s, Guid id, ParticipantModel part) { Socket = s; Id = id; Participant = part; ConnectionState = ConnectionState.Connected; Timeout = DateTime.MaxValue; }
public TCPHandshakeMessage(ParticipantModel participant, IPEndPoint ep) { this.ParticipantId = participant.Guid; this.EndPoint = ep; using (Synchronizer.Lock(participant.SyncRoot)) { this.HumanName = participant.HumanName; } LastMessageSequence = LastChunkSequence = 0; }
public RtpLocalParticipant(ParticipantModel participant) : base(participant.Guid.ToString(), null) { this.m_Participant = participant; // Enable broadcast of the application name and version. this.SetTool(true); this.m_Participant.Changed["HumanName"].Add(new PropertyEventHandler(this.HandleHumanNameChanged)); this.HandleHumanNameChanged(this, null); }
public PresentationModel(Guid id, ParticipantModel owner, string humanName, bool isUntitledPresentation) { this.m_Id = id; this.m_DeckTraversals = new DeckTraversalCollection(this, "DeckTraversals"); this.m_Participants = new ParticipantCollection(this, "Participants"); this.m_Owner = owner; this.m_HumanName = humanName; this.m_QuickPoll = null; this.m_IsUntitledPresentation = isUntitledPresentation; CurrentPresentation = this; }
public ParticipantNetworkService(SendingQueue sender, PresenterModel model, ParticipantModel participant) { this.m_Sender = sender; this.m_Model = model; this.m_Participant = participant; this.m_RoleChangedDispatcher = new EventQueue.PropertyEventDispatcher(this.m_Sender, new PropertyEventHandler(this.HandleRoleChanged)); this.m_Participant.Changed["Role"].Add(this.m_RoleChangedDispatcher.Dispatcher); this.m_RoleChangedDispatcher.Dispatcher(this, null); this.m_GroupCollectionHelper = new GroupCollectionHelper(this); }
public CXPCapabilityMessageReceiver(CXPCapabilityMessageSender sender, uint ssrc, PresenterModel model, ClassroomModel classroom, ParticipantModel participant) { this.m_Model = model; this.m_Sender = sender; this.m_RemoteSSRC = ssrc; this.m_Context = new ReceiveContext(model, classroom, participant); this.m_ContextArgs = new object[] { this.m_Context }; this.m_Queue = new MessageProcessingQueue(this); this.m_Assembler = new ChunkAssembler(); this.m_Assembler.Nack += new ChunkAssembler.NackDelegate(this.HandleAssemblerNack); }
/// <summary> /// When a participant is removed from a classroom, clean up the version exchange /// </summary> /// <param name="value"></param> internal void RemoveVersionExchange(ParticipantModel participant) { if ((participant.Guid.Equals(Guid.Empty)) || (participant.Guid.Equals(PresenterModel.ParticipantId)) || (!m_VersionExchangeDictionary.ContainsKey(participant.Guid))) { return; } using (Synchronizer.Lock(this.SyncRoot)) { Trace.WriteLine("Removing VersionExchange for participant: " + participant.Guid.ToString()); VersionExchange ve = m_VersionExchangeDictionary[participant.Guid]; m_VersionExchanges.Remove(ve); m_VersionExchangeDictionary.Remove(participant.Guid); } }
public RTPMessageReceiver(RTPMessageSender sender, RtpStream stream, PresenterModel model, ClassroomModel classroom, ParticipantModel participant) { this.m_Model = model; this.m_Sender = sender; this.m_RtpStream = stream; this.m_Context = new ReceiveContext(model, classroom, participant); this.m_ContextArgs = new object[] { this.m_Context }; this.m_Queue = new MessageProcessingQueue(this); this.m_Serializer = new BinaryFormatter(); this.m_Assembler = new ChunkAssembler(); this.m_Assembler.Nack += new ChunkAssembler.NackDelegate(this.HandleAssemblerNack); this.m_RtpStream.FrameReceived += new RtpStream.FrameReceivedEventHandler(this.HandleFrameReceived); }
public PresenterModel() { this.m_Stylus = null; this.m_CurrentResult = null; this.m_Network = new NetworkModel(); this.m_VersionExchange = new VersionExchangeModel(); /// Note: We currently assume that the ParticipantModel Guid will be different for each application invocation. /// (In particular TCP reconnection relies on this assumption.) If we need an identifer that persists across /// sessions, we'd need to create a new identifier for this. ParticipantId = Guid.NewGuid(); this.m_Participant = new ParticipantModel(ParticipantId, System.Windows.Forms.SystemInformation.UserName); this.m_Workspace = new WorkspaceModel(); this.m_Undo = new UndoModel(); this.m_ViewerState = new ViewerStateModel(); this.m_PenState = new PenStateModel(); TheInstance = this; }
/// <summary> /// If the participant is not identical to the local node, does not have a Guid.Empty identifier, and /// and if a VersionExchange has not already been created, create one here. In some cases /// (notably if the local node is an instructor) this will cause the exchange to begin. /// </summary> /// <param name="participant"></param> public void CreateVersionExchange(ParticipantModel participant) { if ((participant.Guid.Equals(Guid.Empty)) || (participant.Guid.Equals(PresenterModel.ParticipantId)) || (m_VersionExchangeDictionary.ContainsKey(participant.Guid))) { return; } using (Synchronizer.Lock(this.SyncRoot)) { if (!m_VersionExchangeDictionary.ContainsKey(participant.Guid)) { VersionExchange ve = new VersionExchange(participant); m_VersionExchangeDictionary.Add(participant.Guid, ve); //Adding to the collection here triggers an event in the corresponding Network Service m_VersionExchanges.Add(ve); } } }
private void SetNetworkAssociation(ParticipantModel participant) { // Remove any old event listeners. if (this.m_NetworkAssociation != null) { this.m_NetworkAssociation.Changed["Role"].Remove(this.m_NetworkAssociationRoleChangedDispatcher.Dispatcher); } this.m_NetworkAssociation = participant; // Watch for changes to the participant's Role, so we can know if/when we're associated with an Instructor. if (this.m_NetworkAssociation != null) { this.m_NetworkAssociation.Changed["Role"].Add(this.m_NetworkAssociationRoleChangedDispatcher.Dispatcher); this.m_NetworkAssociationRoleChangedDispatcher.Dispatcher(this, null); } else { this.SetNetworkAssociationRole(null); } }
/// <summary> /// Called when a VersionResponseMessage is received /// </summary> /// <param name="participantModel"></param> /// <param name="versionResponseMessage"></param> internal void ReceiveVersionResponse(ParticipantModel remoteParticipant, VersionResponseMessage versionResponseMessage) { Trace.WriteLine("VersionExchangeModel.ReceiveVersionResponse started. remoteParticipant=" + remoteParticipant.Guid.ToString() + ";responderID=" + versionResponseMessage.ResponderId.ToString()); VersionExchange ve = null; //Look up the VersionExchange for this remote participant using (Synchronizer.Lock(this.SyncRoot)) { if (m_VersionExchangeDictionary.ContainsKey(versionResponseMessage.ResponderId)) { ve = m_VersionExchangeDictionary[versionResponseMessage.ResponderId]; } } //Let the VersionExchange handle the inbound message. if (ve != null) { ve.ReceiveResponse(versionResponseMessage); } else { Trace.WriteLine("Warning: Failed to find a pending version exchange to match inbound response message from participant: " + remoteParticipant.Guid.ToString()); } }
/// <summary> /// Check to see if the given ParticipantModel is contained in the collection /// </summary> /// <param name="value">The ParticipantModel to check for</param> /// <returns>Returns true if the collection contains the ParticipantModel, false otherwise</returns> public bool Contains( ParticipantModel value ) { return List.Contains(value); }
/// <summary> /// Constructor for the PresentationModel /// </summary> /// <param name="id">The unique identifier for this presentation</param> /// <param name="owner">The ParticipantModel of the owner of this presentation</param> public PresentationModel(Guid id, ParticipantModel owner) : this(id, owner, null,true) { }
/* public void HandleEnabledOnRoleChange(object sender, PropertyEventArgs args) { using(Synchronizer.Lock(this.m_Model.ViewerState.SyncRoot)) { switch(this.m_Model.ViewerState.iRole) { //Natalie - figure out a case 0: //Disconnected this.Association = null; case 1: //Viewer case 2: //Presenter this.Association = nulll; case 3: //Public Display } } HandleEnabled(); } */ public void HandleEnabledAndAssociation(ParticipantModel association) { /*if (this.InvokeRequired) { this.Invoke(new HandleEnabledDelegate(HandleEnabled)); } else { HandleEnabled(); } */ this.Enabled = false; this.Association = association; HandleEnabled(); }
/// <summary> /// Inserts a <see cref="ParticipantModel"/> object into the collection at the specified index. /// </summary> public void Insert(int index, ParticipantModel value) { List.Insert(index, value); }
/// <summary> /// Adds a <see cref="ParticipantModel"/> object to the collection. /// </summary> /// <remarks> /// This method should not be used except by a <c>ConnectionManager</c>. /// </remarks> public int Add(ParticipantModel value) { return(List.Add(value)); }
/// <summary> /// Retrieves the location of the specified <see cref="ParticipantModel"/> object in the collection. /// </summary> public int IndexOf(ParticipantModel value) { return(List.IndexOf(value)); }
/// <summary> /// Inserts a ParticipantModel into the given index in the collection /// </summary> /// <param name="index">The index to insert into</param> /// <param name="value">The ParticipantModel to insert</param> public void Insert( int index, ParticipantModel value ) { List.Insert(index, value); }
/// <summary> /// Maintain the connection to the server endpoint. /// </summary> private void ConnectThread() { while (!m_Disposed) { if ((m_Socket == null) || (!m_Socket.Connected) || (!m_Connected)) { Socket s = (Socket)Interlocked.Exchange(ref m_Socket, null); if (s != null) { s.Close(); } try { if (m_Disposed) break; using (Synchronizer.Lock(this.SyncRoot)) { this.SetPublishedProperty("Connected", ref this.m_Connected, false); NetworkStatus newStatus = m_NetworkStatus.Clone(); newStatus.ConnectionStatus = ConnectionStatus.TryingToConnect; this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus); } Trace.WriteLine("Attempting connection to server.", this.GetType().ToString()); m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); m_Socket.ReceiveTimeout = 20000; //Temporarily set to 20 seconds for the handshaking phase. //In case the server endpoint changed: UpdateRemoteEndpoint(); m_Socket.Connect(this.m_RemoteEP); m_NetworkStream = new NetworkStream(m_Socket); //NetworkStream does not own the socket. //Receive handshake BinaryFormatter bf = new BinaryFormatter(); object o = bf.Deserialize(m_NetworkStream); if (o is TCPHandshakeMessage) { Trace.WriteLine("Handshake received from " + ((TCPHandshakeMessage)o).ParticipantId.ToString() + " ep=" + ((TCPHandshakeMessage)o).EndPoint.ToString()); //send a handshake TCPHandshakeMessage handshake = new TCPHandshakeMessage(m_Participant, (IPEndPoint)m_Socket.LocalEndPoint); lock (this.m_ReceiveQueue) { //If this is a reconnect, these values tell the server where we left off handshake.LastMessageSequence = m_LastMsgSequence; handshake.LastChunkSequence = m_LastChunkSequence; } MemoryStream ms = new MemoryStream(); bf.Serialize(ms, handshake); m_NetworkStream.Write(ms.GetBuffer(), 0, (int)ms.Length); Trace.WriteLine("Handshake sent.", this.GetType().ToString()); //The first time we connect to a server we create a new ParticipantModel to represent the server. if (m_ServerParticipant == null) { TCPHandshakeMessage h = (TCPHandshakeMessage)o; m_ServerId = h.ParticipantId; m_ServerParticipant = new ParticipantModel(m_ServerId, h.HumanName); } else { //In reconnect scenarios we keep the same server ParticipantModel, but the Guid could //change if the server was restarted. In this case we just want to update the Guid. //Notice that we can't create a new ParticipantModel here without breaking some things. if (!m_ServerId.Equals(((TCPHandshakeMessage)o).ParticipantId)) { m_ServerId = ((TCPHandshakeMessage)o).ParticipantId; m_ServerParticipant.Guid = m_ServerId; } } } else { throw new ApplicationException("Invalid handshake received: " + o.GetType().ToString()); } m_Socket.ReceiveTimeout = 0; //Reset socket to infinite timeout. m_ClientTimeout = SetClientTimeout(); using (Synchronizer.Lock(this.SyncRoot)) { //Setting this property allows the ReceiveThread to begin: this.SetPublishedProperty("Connected", ref this.m_Connected, true); NetworkStatus newStatus = m_NetworkStatus.Clone(); newStatus.ConnectionStatus = ConnectionStatus.Connected; this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus); } lock (this.m_ReceiveQueue) { //This enables the client timeout: this.m_LastMsgReceived = DateTime.Now; } Trace.WriteLine("Connected.", this.GetType().ToString()); } catch (SocketException se) { if (se.ErrorCode == 10060) { Trace.WriteLine("ConnectThread SocketException 10060: remote host failed to respond."); } else if (se.ErrorCode == 10038) { Trace.WriteLine("ConnectThread SocketException 10038: operation attempted on non-socket."); } else { Trace.WriteLine("ConnectThread SocketException " + se.ErrorCode.ToString() + ": " + se.Message); } } catch (IOException ioe) { Trace.WriteLine("ConnectThread IOException: " + ioe.Message); if ((ioe.InnerException != null) && (ioe.InnerException is SocketException)) { Trace.WriteLine(" InnerException: SocketException " + ((SocketException)ioe.InnerException).ErrorCode.ToString()); } } catch (Exception e) { Trace.WriteLine("ConnectThread exception: " + e.ToString()); } } for (int i=0; ((i<10) && (!m_Disposed)); i++) Thread.Sleep(100); } Trace.WriteLine("ConnectThread is ending.", this.GetType().ToString()); }
/// <summary> /// Remove the given ParticipantModel /// </summary> /// <param name="value">The ParticipantModel to remove</param> public void Remove( ParticipantModel value ) { List.Remove(value); }
/// <summary> /// Creates a <see cref="ParticipantModel"/> and <see cref="RTPMessageReceiver"/> /// whenever a new stream is connected to the venue. The <see cref="ParticipantModel"/> /// is added to the current <see cref="ClassroomModel"/>. /// </summary> private void HandleStreamAdded(object sender, RtpEvents.RtpStreamEventArgs args) { using(Synchronizer.Lock(this.m_Sender.m_Classroom.SyncRoot)) { using(Synchronizer.Lock(this)) { if(this.m_Disposed) throw new ObjectDisposedException("RTPMessageSender"); RtpStream stream = args.RtpStream; // Ignore streams that are not part of our session. if(this.m_Sender.m_RtpSession.ContainsStream(stream)) { // Ignore streams that are not DynamicPresentations. if(stream.PayloadType == PayloadType.dynamicPresentation) { // Ignore our own stream, but create a listener for all others. if(stream.SSRC != this.m_Sender.m_RtpSender.SSRC) { // If we've not seen this client before, create a new ParticipantModel, // identified by the participant's CName, which, if the participant // is running Classroom Presenter, is a Guid string. string cname = stream.Properties.CName; // It's possible for a participant to generate more than one Rtp stream, // so we need to keep a different table for m_Participants than m_Receivers. ParticipantModel participant = ((ParticipantModel) this.m_Participants[cname]); if(participant == null) { // Also get the remote client's HumanName from the participant data. RtpParticipant client = this.m_Sender.m_RtpSession.Participants[cname]; participant = new ParticipantModel(new Guid(cname), client.Name); // Add the participant to our table. this.m_Participants.Add(cname, participant); } // Add the participant to the classroom if it is not already a member. if(!this.m_Sender.m_Classroom.Participants.Contains(participant)) this.m_Sender.m_Classroom.Participants.Add(participant); // Create a receiver for this specific stream (there may be more than one stream per participant) // and add it to the table of receivers so it can be disposed when the stream is removed. RTPMessageReceiver receiver = new RTPMessageReceiver(this.m_Sender, stream, this.m_Sender.m_Model, this.m_Sender.m_Classroom, participant); this.m_Receivers.Add(stream, receiver); } } } } } }
/// <summary> /// Returns the index of the given object /// </summary> /// <param name="value">The ParticipantModel to find the index of</param> /// <returns>The index of the object, or -1 otherwise</returns> public int IndexOf( ParticipantModel value ) { return List.IndexOf(value); }
internal ReceiveContext(PresenterModel model, ClassroomModel classroom, ParticipantModel participant) { this.Model = model; this.Classroom = classroom; this.Participant = participant; }
/// <summary> /// Determines if the specified <see cref="ParticipantModel"/> is in the collection. /// </summary> public bool Contains(ParticipantModel value) { return(List.Contains(value)); }
/// <summary> /// Set the SequenceNumber property for the specified item, then enqueue it. /// </summary> /// <param name="sp"></param> public void Enqueue(SendParameters sp, ParticipantModel participant) { lock (this) { sp.SequenceNumber = m_NextSequenceNumber; m_NextSequenceNumber++; if (m_QueueList.ContainsKey(sp.Id)) { ((ClientQueue)m_QueueList[sp.Id]).Enqueue(sp); } else { ClientQueue cq = new ClientQueue(sp.Id, participant); cq.Enqueue(sp); m_QueueList.Add(sp.Id, cq); } //Trace.WriteLine("Enqueue guid=" + sp.Tags.SlideID.ToString() + "; seq =" + sp.SequenceNumber.ToString(), this.GetType().ToString()); } }
public ClientQueue(Guid id, ParticipantModel participant) { m_Participant = participant; m_IsPublicNode = false; m_Participant.Changed["Role"].Add(new PropertyEventHandler(OnRoleChanged)); using (Synchronizer.Lock(m_Participant.SyncRoot)) { if (m_Participant.Groups.Contains(Group.AllPublic)) { m_IsPublicNode = true; } } this.m_Id = id; this.m_List = new LinkedList<SendParameters>(); this.m_SlideQueues = new Hashtable(); this.m_ReconnectBuffer = new ReconnectBuffer(); }
/// <summary> /// After a client reconnect, we may have a new ParticipantModel, so resubscribe for Role changes. /// </summary> /// <param name="participant"></param> internal void UpdateParticipant(ParticipantModel participant) { this.m_Participant.Changed["Role"].Remove(new PropertyEventHandler(OnRoleChanged)); m_Participant = participant; m_IsPublicNode = false; m_Participant.Changed["Role"].Add(new PropertyEventHandler(OnRoleChanged)); using (Synchronizer.Lock(m_Participant.SyncRoot)) { if (m_Participant.Groups.Contains(Group.AllPublic)) { m_IsPublicNode = true; } } }
protected override void OnClick(EventArgs e) { base.OnClick(e); this.clicked = true; if (this.m_Association != null) { // Set the role accordingly using (Synchronizer.Lock(this.m_Model.Participant)) { if (this.m_Model.Participant.Role is InstructorModel || this.m_Model.Participant.Role == null) this.m_Model.Participant.Role = new StudentModel(Guid.NewGuid()); } // Set the network association using (Synchronizer.Lock(this.m_Model.Network.SyncRoot)) { this.m_Model.Network.Association = this.Association; } } else { // Instead Start an Empty Presentation StartJoinButton2.StartEmptyPresentation(this.m_Model); } }
/// <summary> /// Constructor for the PresentationModel /// </summary> /// <param name="id">The unique identifier for this presentation</param> /// <param name="owner">The ParticipantModel of the owner of this presentation</param> /// <param name="humanName">The friendly name for this presentation</param> public PresentationModel(Guid id, ParticipantModel owner, string humanName) : this(id,owner,humanName,false) { }