///////////////////////////////////////////////////////////////////// // CONTROL (LOOP) THREAD ///////////////////////////////////////////////////////////////////// private void ControlThread(object nobjs) { var networkObjects = nobjs as object[]; var tcpListener = networkObjects[0] as TcpListener; var announcer = networkObjects[1] as UdpClient; var clientThreads = new List <Thread>(); var flushTimer = new Marzersoft.Timer(); var announceTimer = new Marzersoft.Timer(); var announceEndpoints = new List <IPEndPoint>(); while (running) { Thread.Sleep(1); /* * COMP7722: Incoming connection requests are dispatched to their own managing thread. */ //accept the connection while (tcpListener.Pending()) { //get client TcpClient tcpClient = null; if (CaptureException(() => { tcpClient = tcpListener.AcceptTcpClient(); })) { continue; } //create client thread clientThreads.Add(new Thread(ClientThread)); clientThreads.Last().Name = "OTEX Server ClientThread"; clientThreads.Last().IsBackground = false; clientThreads.Last().Start(tcpClient); } //announce if (startParams.Public && announceTimer.Seconds >= 1.0) { CaptureException(() => { //create endpoints if they don't exist if (announceEndpoints.Count == 0) { for (int i = AnnouncePorts.First; i <= AnnouncePorts.Last; ++i) { announceEndpoints.Add(new IPEndPoint(IPAddress.Broadcast, i)); } } //serialize a server description of the current state var announceData = (new ServerDescription(this)).Serialize(); //broadcast to the port range foreach (IPEndPoint ep in announceEndpoints) { announcer.Send(announceData, announceData.Length, ep); } }); announceTimer.Reset(); } /* * COMP7722: File contents is synchronized to disk periodically (every 15 seconds) */ //flush file contents to disk periodically if (startParams.FilePath.Length > 0 && flushTimer.Seconds >= 15.0) { lock (stateLock) { CaptureException(() => { SyncFileContents(); }); } flushTimer.Reset(); } } //stop listeners and announcer CaptureException(() => { tcpListener.Stop(); }); if (announcer != null) { CaptureException(() => { announcer.Close(); }); } //wait for client threads to close foreach (Thread thread in clientThreads) { thread.Join(); } //final flush to disk (don't need a lock this time, all client threads have stopped) if (startParams.FilePath.Length > 0) { CaptureException(() => { SyncFileContents(); }); } }
///////////////////////////////////////////////////////////////////// // CONTROL (LOOP) THREAD ///////////////////////////////////////////////////////////////////// private void ControlThread(object o) { var objs = o as object[]; var client = objs[0] as TcpClient; var stream = objs[1] as PacketStream; var lastOpsRequestTimer = new Marzersoft.Timer(); while (!clientSideDisconnection && stream.Connected) { Thread.Sleep(1); //listen for packets first //(returns true if the server has asked us to disconnect) if (Listen(stream)) { //override clientSideDisconnection so we don't send //unnecessarily send a disconnection to the server clientSideDisconnection = false; break; } //send periodic requests for new operations if (!awaitingOperationList && lastOpsRequestTimer.Seconds >= updateInterval) { lock (operationsLock) { /* * COMP7722: step 1 & 2 of HOB: all outgoing operations sent to the server, * clearing the local outgoing operation buffer. */ //perform SLOT(OB,CIB) (1) if (outgoingOperations.Count > 0 && incomingOperations.Count > 0) { Operation.SymmetricLinearTransform(outgoingOperations, incomingOperations); } //send request (2) if (CaptureException(() => { stream.Write(ID, new OperationList(outgoingOperations.Count > 0 ? outgoingOperations : null)); })) { break; } awaitingOperationList = true; //clear outgoing packet list (1) if (outgoingOperations.Count > 0) { outgoingOperations.Clear(); } //apply any incoming operations (also clears list) InvokeRemoteOperations(incomingOperations); } lastOpsRequestTimer.Reset(); } //send off any pending metadata updates if (pendingMetadata != null) { lock (pendingMetadataLock) { if (pendingMetadata != null) { if (CaptureException(() => { stream.Write(ID, new ClientMetadata(ID, pendingMetadata)); })) { break; } pendingMetadata = null; } } } } //disconnect connected = false; if (clientSideDisconnection) //tell the server the user is disconnecting client-side { CaptureException(() => { stream.Write(ID, new DisconnectionRequest()); }); } stream.Dispose(); client.Close(); outgoingOperations.Clear(); incomingOperations.Clear(); pendingMetadata = null; //fire event OnDisconnected?.Invoke(this, !clientSideDisconnection); clientSideDisconnection = false; }
///////////////////////////////////////////////////////////////////// // CONTROL (LOOP) THREAD ///////////////////////////////////////////////////////////////////// private void ControlThread(object uc) { var udpClient = uc as UdpClient; var checkTimer = new Marzersoft.Timer(); while (!isDisposed) { Thread.Sleep(250); //read all waiting packets while (udpClient.Available > 0) { //read packet IPEndPoint senderAnnounceEndpoint = null; byte[] packetData = null; if (CaptureException(() => { packetData = udpClient.Receive(ref senderAnnounceEndpoint); })) { continue; } //deserialize packet ServerDescription packet = null; IPEndPoint senderEndPoint = null; if (CaptureException(() => { packet = packetData.Deserialize <ServerDescription>(); senderEndPoint = new IPEndPoint(senderAnnounceEndpoint.Address, packet.Port); })) { continue; } //process lock (activeServers) { ServerDescription knownServer = null; if (!activeServers.TryGetValue(senderEndPoint, out knownServer)) { activeServers[senderEndPoint] = knownServer = new ServerDescription(senderEndPoint, packet); OnServerAdded?.Invoke(this, knownServer); } else { CaptureException(() => { knownServer.Update(senderEndPoint, packet); }); } } } //periodically check server inactive state and update pings if (checkTimer.Seconds >= 1.0) { lock (activeServers) { //cull inactive servers var inactiveServers = activeServers .Where((kvp) => { return(kvp.Value.LastUpdated >= 10.0); }) .ToList(); foreach (var inactive in inactiveServers) { activeServers.Remove(inactive.Key); inactive.Value.Active = false; //invokes event } //update pings if (autoPing) { foreach (var active in activeServers) { if (active.Value.LastPinged >= 10.0) { active.Value.UpdatePing(); } } } } checkTimer.Reset(); } } //close listener CaptureException(() => { udpClient.Close(); }); }