/// <summary> /// Call to disconnect from the server. /// </summary> /// <remarks>Always call disconnect before exiting the program. Failure to do so could cause a zombie thread. /// Also note that this sets the <see cref="Client.ClientNetwork.Username">Username</see> to blank and makes <see cref="Client.ClientNetwork.Connected">Connected</see> false.</remarks> /// <example>Always call this function on exit. /// <code>protected override void Dispose( bool disposing ) /// { /// // Always call this before exiting the program /// ClientNetwork.Disconnect(); /// }</code></example> public static void Disconnect(bool forced) { try { // Try to close the stream connectionStream.Close(); } catch {} username = ""; connected = false; if (forced) { Network.Header logoffHeader = new SDCSCommon.Network.Header(); logoffHeader.DataType = Network.DataTypes.Logout; logoffHeader.Encrypted = false; logoffHeader.Length = 0; DataReceivedEventArgs e = new DataReceivedEventArgs(); e.Header = logoffHeader; e.Data = System.Text.UnicodeEncoding.Unicode.GetBytes("Lost connection to server"); DataReceived(null, e); } try { // Try to kill the thread listeningThread.Abort(); } catch {} }
/// <summary> /// Sends the keepAlive signal to check if the network connection is still active /// </summary> private static void sendKeepAlive() { Network.Header pingHead = new SDCSCommon.Network.Header(); pingHead.DataType = Network.DataTypes.Ping; pingHead.Encrypted = false; pingHead.FromID = 0; pingHead.Length = 0; pingHead.ToID = -1; SendData(Network.headerToBytes(pingHead)); }
/// <summary> /// Function for the connection watching thread to live in. Basically is a message pump for the network /// </summary> private void connectionWatcherFunc() { // Records the last time a byte was received so we can check for a read timeout System.DateTime lastRead; // First send the random code for the client Network.Header sendHead = new SDCSCommon.Network.Header(); sendHead.DataType = Network.DataTypes.RandomPassCode; sendHead.FromID = -1; sendHead.ToID = 0; sendHead.Length = 32; // Create a random 32 byte unicode string randomCode = new byte[32]; Random rand = new Random(); rand.NextBytes(randomCode); // And send it to the client sendData(Network.headerToBytes(sendHead), randomCode); // Now loop forever while (true) { // Each while loop continually checks if the server is shutting down. // This is to make sure that we don't end up with zombie threads. while (conn.stream.DataAvailable == false) { if (ServerNetwork.ShuttingDown) return; if (BuddyListData.Count != 0) sendBuddyListData(); if (System.DateTime.Now - lastActivity > TimeSpan.FromMilliseconds(KEEP_ALIVE_TIME)) sendKeepAlive(); Thread.Sleep(100); } byte[] headerBuffer = new byte[Network.HEADER_SIZE]; lastRead = System.DateTime.Now; for (int i = 0; i < headerBuffer.Length; i++) { while (conn.stream.DataAvailable == false) { if (frmServer.ShuttingDown || (System.DateTime.Now - lastRead) > System.TimeSpan.FromMilliseconds(MAX_READ_WAIT_TIME)) return; Thread.Sleep(100); } headerBuffer[i] = (byte)conn.stream.ReadByte(); lastRead = System.DateTime.Now; } Network.Header head = Network.bytesToHeader(headerBuffer); // This is to prevent a user from spoofing themselves as another user head.FromID = conn.userID; // Read in exactly the amount of data sent byte[] data = new byte[head.Length]; lastRead = System.DateTime.Now; for (int i = 0; i < head.Length; i++) { while (conn.stream.DataAvailable == false) {if (frmServer.ShuttingDown || (System.DateTime.Now - lastRead) > System.TimeSpan.FromMilliseconds(MAX_READ_WAIT_TIME)) return; Thread.Sleep(100);} data[i] = (byte)conn.stream.ReadByte(); lastRead = System.DateTime.Now; } // We don't want to allow someone to do anything but log in until they are logged in if ((loggedIn && head.DataType != Network.DataTypes.LoginInformation) || head.DataType == Network.DataTypes.LoginInformation || head.DataType == Network.DataTypes.Ping) { switch (head.DataType) { case Network.DataTypes.InstantMessage: // Instant message data for (int i = 0; i < ServerNetwork.netStreams.Count; i++) { if (((ServerNetwork.connection)ServerNetwork.netStreams[i]).userID == head.ToID) { ((ServerNetwork.connection)ServerNetwork.netStreams[i]).watchingClass.sendData(Network.headerToBytes(head), data); } } break; case Network.DataTypes.WhiteBoard: // Whiteboard drawing information break; case Network.DataTypes.LoginInformation: // Initial login // We receive a double hased password from the client. We store the single hash in the user database. // First we convert the received username and double hash in to unicode strings. int usernameLength = BitConverter.ToInt32(data,0); string username = System.Text.UnicodeEncoding.Unicode.GetString(data, 4, usernameLength); string password = System.Text.UnicodeEncoding.Unicode.GetString(data, 4 + usernameLength, data.Length - (4 + usernameLength)); Network.Header confirmHead = new SDCSCommon.Network.Header(); confirmHead.DataType = Network.DataTypes.LoginStatus; confirmHead.FromID = -1; confirmHead.Length = 4; // This statement performs the second hash on the stored password and compares it with the password received from // the client. if (CryptoFunctions.getMD5Hash(String.Concat(ServerDatabase.getUserPass(username), System.Text.UnicodeEncoding.Unicode.GetString(randomCode))) == password) { // Login successful conn.userID = ServerDatabase.getUserID(username); conn.username = username; // Make sure we are only logged in from one place at a time lock (ServerNetwork.netStreams.SyncRoot) { for (int i = ServerNetwork.netStreams.Count - 1; i >= 0; i--) if (((ServerNetwork.connection)ServerNetwork.netStreams[i]).userID == conn.userID && ((ServerNetwork.connection)ServerNetwork.netStreams[i]).watchingClass.loggedIn) ((ServerNetwork.connection)ServerNetwork.netStreams[i]).watchingClass.Shutdown(); } confirmHead.ToID = conn.userID; // Send the Login OK message to let the client know they're authenticated sendData(Network.headerToBytes(confirmHead), BitConverter.GetBytes(Network.LoginOK)); loggedIn = true; // Let everyone know that the user is now online conn.userState = SDCSCommon.Network.UserState.Online; ServerNetwork.notifyBuddyStatus(conn.userID, conn.username, Network.UserState.Online); ServerNetwork.refreshBuddyList(conn); } else { // Login failed confirmHead.ToID = 0; sendData(Network.headerToBytes(confirmHead), BitConverter.GetBytes(Network.LoginBad)); Shutdown(); } break; case Network.DataTypes.Logout: Shutdown(); break; default: break; } } else Shutdown(); lastActivity = System.DateTime.Now; } }