using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using BeWo.Data; using BeWo.Data.Access; using BeWo.Data.Entities; using BS.Shared; namespace BeWoChatServer { public class Server { // Fehlercodes zwischen 1000 und 1499 private static readonly object _Lock = string.Empty; private static readonly Dictionary clientList = new Dictionary(); protected static Socket serverSocket; private static AsyncCallback asyncCallback; private static int clientCount; public static void Main(string[] args) { try { Console.WriteLine(DateTime.Now + ": Starte Server"); serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var server = new IPEndPoint(IPAddress.Any, 5000); serverSocket.Bind(server); serverSocket.Listen(4); serverSocket.BeginAccept(OnClientConnected, null); while (true) { } } catch (Exception e) { WriteErrorMessageToConsole(e.Message, e.StackTrace); } } public static void OnClientConnected(IAsyncResult asyncResult) { try { var workerSocket = serverSocket.EndAccept(asyncResult); Interlocked.Increment(ref clientCount); clientList.Add(workerSocket, 0); Console.WriteLine(DateTime.Now + ": Client hinzugefügt"); WaitForClientData(workerSocket, "", 0, 0); serverSocket.BeginAccept(OnClientConnected, null); } catch (SocketException e) { WriteErrorMessageToConsole(e.Message, e.StackTrace); } } public static void WaitForClientData(Socket socket, string clientName, long oid, long recipientPersonOid) { try { if (asyncCallback == null) { asyncCallback = OnReceiveData; } var packet = new Packet(socket, clientName, oid, recipientPersonOid); socket.BeginReceive(packet.DataBuffer, 0, packet.DataBuffer.Length, SocketFlags.None, asyncCallback, packet); } catch (Exception e) { WriteErrorMessageToConsole(e.Message, e.StackTrace); } } public static void OnReceiveData(IAsyncResult asyncResult) { var dataPacket = (Packet) asyncResult.AsyncState; Console.WriteLine(DateTime.Now + ": Empfange Nachricht"); try { var responseByteCount = dataPacket.Socket.EndReceive(asyncResult); var chars = new char[responseByteCount]; var decoder = Encoding.UTF8.GetDecoder(); decoder.GetChars(dataPacket.DataBuffer, 0, responseByteCount, chars, 0); if(chars.Count(f => f.Equals(';')) >= 4) { var message = new string(chars); dataPacket.GetData(message); } switch(dataPacket.DataIdentifier) { case DataIdentifier.Login: try { Console.WriteLine(DateTime.Now + ": Login von " + dataPacket.ClientName); Monitor.Enter(_Lock); var endPoint = dataPacket.Socket.RemoteEndPoint; var endPointCollection = clientList.Select(s => s.Key.RemoteEndPoint).ToList(); var istEnthalten = endPointCollection.Contains(endPoint); if(istEnthalten) { clientList[dataPacket.Socket] = dataPacket.SenderPersonOid; } var notificationPacket = new Packet(dataPacket.Socket, dataPacket.ClientName, dataPacket.SenderPersonOid, 0) {DataIdentifier = DataIdentifier.StatusNotificationLogin, Tenant = dataPacket.Tenant}; Console.WriteLine(DateTime.Now + ": Benachrichtige die anderen angemeldeten Clients"); foreach (var client in clientList.Where(w => !w.Value.Equals(dataPacket.SenderPersonOid))) { client.Key.Send(notificationPacket.GetDataStream()); } // Boradcast var abc = clientList.Where(w => w.Value != dataPacket.SenderPersonOid && w.Value > 0).Select(s => s.Value).Distinct().ToList(); var msg = ""; foreach(var oid in abc) { msg += oid; if(abc.IndexOf(oid) != abc.Count - 1) { msg += ","; } } if(msg.Length > 0) { var packet = new Packet {ChatMessage = msg, ClientName = dataPacket.ClientName, DataIdentifier = DataIdentifier.StatusNotificationLoginBroadcast, MessageTimeStamp = DateTime.Now, Tenant = dataPacket.Tenant, SenderPersonOid = dataPacket.SenderPersonOid}; dataPacket.Socket.Send(packet.GetDataStream()); } } finally { Monitor.Exit(_Lock); } break; case DataIdentifier.Message: if(responseByteCount == 0) { try { Console.WriteLine(DateTime.Now + ": Chat-Nachricht ist leer und sorgt dafür, dass der Client ausgeloggt wird"); Monitor.Enter(_Lock); var notificationPacketLogout2 = new Packet(dataPacket.Socket, dataPacket.ClientName, dataPacket.SenderPersonOid, 0) {DataIdentifier = DataIdentifier.StatusNotificationLogout}; clientList.Remove(dataPacket.Socket); Console.WriteLine(DateTime.Now + ": Leite Logout an die anderen Clients weiter"); foreach(var client in clientList) { client.Key.Send(notificationPacketLogout2.GetDataStream()); } dataPacket.Socket.Close(); } finally { Monitor.Exit(_Lock); } return; } else { try { Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(DateTime.Now + ": Chat-Nachricht erhalten"); Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.Gray; Monitor.Enter(_Lock); DefaultHibernateSessionManager.CurrentTenant = dataPacket.Tenant; var chatMessage = new ChatMessage { SenderPersonOid = dataPacket.SenderPersonOid, ChatText = dataPacket.ChatMessage, EmpfängerPersonOid = dataPacket.RecipientPersonOid, Uhrzeit = dataPacket.MessageTimeStamp, InsUser = dataPacket.ClientName }; Console.WriteLine(DateTime.Now + ": Speichere die Chat-Nachricht in die Datenbank"); DAOFactory.GenericDAO.Insert(chatMessage); // Testweise die Id einfügen if(chatMessage.Oid != null) { Console.WriteLine(DateTime.Now + ": Chat-Nachricht erfolgreich mit Oid " + chatMessage.Oid.Value + " gespeichert"); dataPacket.ServerseitigeOid = chatMessage.Oid.Value; } if(dataPacket.RecipientPersonOid > 0) { if (clientList.ContainsValue(dataPacket.RecipientPersonOid)) { var recipients = clientList.Where(w => w.Value.Equals(dataPacket.RecipientPersonOid)); Console.WriteLine(DateTime.Now + ": Leite die Chat-Nachricht an die Empfänger (Derselbe User angemeldet an verschiedenen Clients) weiter"); foreach(var recipient in recipients) { recipient.Key.Send(dataPacket.GetDataStream()); } } var packet = new Packet { ClientseitigeOid = dataPacket.ClientseitigeOid, ServerseitigeOid = dataPacket.ServerseitigeOid, DataIdentifier = DataIdentifier.MessageResponse }; Console.WriteLine(DateTime.Now + ": Sende Datenbank-Insert-Bestätigung an den Absender, damit die Nachricht später nicht doppelt auf den Client heruntergeladen wird"); dataPacket.Socket.Send(packet.GetDataStream()); } } finally { Monitor.Exit(_Lock); } } break; case DataIdentifier.Logout: clientList.Remove(dataPacket.Socket); var notificationPacketLogout = new Packet(dataPacket.Socket, dataPacket.ClientName, dataPacket.SenderPersonOid, 0) {DataIdentifier = DataIdentifier.StatusNotificationLogout}; Console.WriteLine(DateTime.Now + ": Sende Logout an alle anderen Clients"); foreach (var client in clientList.Where(w => !w.Value.Equals(dataPacket.SenderPersonOid))) { client.Key.Send(notificationPacketLogout.GetDataStream()); } break; case DataIdentifier.Team: try { Console.WriteLine(DateTime.Now + ": Team-Nachricht erhalten"); Monitor.Enter(_Lock); DefaultHibernateSessionManager.CurrentTenant = dataPacket.Tenant; var chatMessage = new ChatMessage { SenderPersonOid = dataPacket.SenderPersonOid, ChatText = dataPacket.ChatMessage, EmpfängerPersonOid = dataPacket.RecipientPersonOid, Uhrzeit = dataPacket.MessageTimeStamp, InsUser = dataPacket.ClientName, TeamOid = dataPacket.RecipientPersonOid }; var team = DAOFactory.GenericDAO.LoadByID(dataPacket.RecipientPersonOid); Console.WriteLine(DateTime.Now + ": Speichere Team-Nachricht in der Datenbank"); DAOFactory.GenericDAO.Insert(chatMessage); var personOids = team.MemberList.Select(s => s.Person.Oid.Value); if (chatMessage.Oid != null) { Console.WriteLine(DateTime.Now + ": Team-Nachricht erfolgreich mit Oid " + chatMessage.Oid.Value + " gespeichert"); dataPacket.ServerseitigeOid = chatMessage.Oid.Value; } Console.WriteLine(DateTime.Now + ": Leite die Team-Nachricht an die Empfänger weiter"); foreach(var recipient in clientList.Where(w => w.Value != dataPacket.SenderPersonOid && personOids.Contains(w.Value))) { recipient.Key.Send(dataPacket.GetDataStream()); } var packet = new Packet { ClientseitigeOid = dataPacket.ClientseitigeOid, ServerseitigeOid = dataPacket.ServerseitigeOid, DataIdentifier = DataIdentifier.MessageResponse }; Console.WriteLine(DateTime.Now + ": Sende Datenbank-Insert-Bestätigung an den Absender, damit die Nachricht später nicht doppelt auf den Client heruntergeladen wird"); dataPacket.Socket.Send(packet.GetDataStream()); } finally { Monitor.Exit(_Lock); } break; case DataIdentifier.StatusNotificationBeschäftigt: SendNotificationPacket(dataPacket, DataIdentifier.StatusNotificationBeschäftigt); break; case DataIdentifier.StatusNotificationOnline: SendNotificationPacket(dataPacket, DataIdentifier.StatusNotificationOnline); break; case DataIdentifier.StatusNotificationOffline: SendNotificationPacket(dataPacket, DataIdentifier.StatusNotificationOffline); break; } WaitForClientData(dataPacket.Socket, dataPacket.ClientName, dataPacket.SenderPersonOid, dataPacket.RecipientPersonOid); } catch (Exception e) { Console.WriteLine(DateTime.Now + ": Ein Fehler ist aufgetreten oder die App ist abgestürzt. Der Client wird ausgeloggt"); var sender = dataPacket.Socket; var notificationPacketLogout = new Packet(dataPacket.Socket, dataPacket.ClientName, dataPacket.SenderPersonOid, 0) { DataIdentifier = DataIdentifier.StatusNotificationLogout }; try { Monitor.Enter(_Lock); if(clientList.ContainsKey(sender)) { clientList.Remove(sender); Console.WriteLine(DateTime.Now + ": Leite Logout an die anderen Clients weiter"); foreach(var client in clientList) { client.Key.Send(notificationPacketLogout.GetDataStream()); } sender.Close(); } } finally { Monitor.Exit(_Lock); } WriteErrorMessageToConsole(e.Message, e.StackTrace); } } private static void SendNotificationPacket(Packet dataPacket, DataIdentifier dataIdentifier) { var notificationPacket = new Packet(dataPacket.Socket, dataPacket.ClientName, dataPacket.SenderPersonOid, 0) { DataIdentifier = DataIdentifier.StatusNotificationLogout }; Console.WriteLine(DateTime.Now + ": Sende Statusänderung(" + dataIdentifier + ") an alle anderen Clients"); foreach (var client in clientList.Where(w => w.Value != dataPacket.SenderPersonOid)) { client.Key.Send(notificationPacket.GetDataStream()); } } private static void WriteErrorMessageToConsole(string message, string stackTrace) { Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(DateTime.Now + ": " + message); Console.WriteLine(stackTrace); Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.Gray; } } }