491 lines
21 KiB
C#
491 lines
21 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
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;
|
|
using BS.Shared.Extensions;
|
|
|
|
|
|
namespace BeWoChatServer
|
|
{
|
|
public class Server
|
|
{
|
|
// Fehlercodes zwischen 1000 und 1499
|
|
|
|
private static readonly object _Lock = string.Empty;
|
|
|
|
private static readonly ConcurrentDictionary<Socket, LoginData> clientList = new ConcurrentDictionary<Socket, LoginData>();
|
|
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);
|
|
|
|
var couldAdd = clientList.TryAdd(workerSocket, new LoginData(0, "", DateTime.Now));
|
|
|
|
if(!couldAdd)
|
|
{
|
|
Console.WriteLine("Socket war schon in der clientListe enthalten und konnte nicht hinzugefügt werden");
|
|
}
|
|
|
|
Console.WriteLine(DateTime.Now + ": Client hinzugefügt (" + workerSocket.RemoteEndPoint + ")");
|
|
|
|
WaitForClientData(workerSocket, "", 0, 0);
|
|
|
|
serverSocket.BeginAccept(OnClientConnected, null);
|
|
|
|
LogAllClients();
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
private static void LogAllClients()
|
|
{
|
|
Console.BackgroundColor = ConsoleColor.Black;
|
|
Console.ForegroundColor = ConsoleColor.DarkMagenta;
|
|
|
|
Console.WriteLine("Folgende Benutzer sind angemeldet:");
|
|
var abc = "";
|
|
clientList.DoForEach(c =>abc += "\t" + c.Key.RemoteEndPoint.ToString() + "; Angemeldete Person: " + c.Value.PersonOid + "; MAC: " + c.Value.MacAddress + "; Zeitpunkt: " + c.Value.LoginTime.ToString("dd.MM.yyyy HH:mm:ss.ffff") + "\n");
|
|
|
|
Console.Write(abc);
|
|
|
|
Console.BackgroundColor = ConsoleColor.Black;
|
|
Console.ForegroundColor = ConsoleColor.Gray;
|
|
}
|
|
|
|
public static void OnReceiveData(IAsyncResult asyncResult)
|
|
{
|
|
var dataPacket = (Packet) asyncResult.AsyncState;
|
|
|
|
Console.WriteLine(DateTime.Now + ": Empfange Nachricht");
|
|
|
|
LogAllClients();
|
|
|
|
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 + " mit MAC: " + dataPacket.ChatMessage);
|
|
Monitor.Enter(_Lock);
|
|
|
|
var endPoint = dataPacket.Socket.RemoteEndPoint;
|
|
|
|
var endPointCollection = clientList.Select(s => s.Key.RemoteEndPoint).ToList();
|
|
|
|
var istEnthalten = endPointCollection.Contains(endPoint);
|
|
|
|
var alteLogins = clientList.Values.Any(a => a.MacAddress.Equals(dataPacket.ChatMessage));
|
|
|
|
if(alteLogins)
|
|
{
|
|
var alt = clientList.Where(w => w.Value.MacAddress.Equals(dataPacket.ChatMessage) && !w.Key.Equals(dataPacket.Socket));
|
|
clientList.RemoveRange(alt);
|
|
}
|
|
|
|
if(istEnthalten)
|
|
{
|
|
clientList[dataPacket.Socket].PersonOid = dataPacket.SenderPersonOid;
|
|
clientList[dataPacket.Socket].MacAddress = dataPacket.ChatMessage;
|
|
}
|
|
|
|
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. Alte Logins enthalten: " + alteLogins);
|
|
foreach (var client in clientList.Where(w => !w.Value.PersonOid.Equals(dataPacket.SenderPersonOid)))
|
|
{
|
|
client.Key.Send(notificationPacket.GetDataStream());
|
|
}
|
|
|
|
var successfulLoginNotification = new Packet(dataPacket.Socket, dataPacket.ClientName, dataPacket.SenderPersonOid, 0) {DataIdentifier = DataIdentifier.LoginSuccess, Tenant = dataPacket.Tenant};
|
|
dataPacket.Socket.Send(successfulLoginNotification.GetDataStream());
|
|
|
|
// Boradcast
|
|
var abc = clientList.Where(w => w.Value.PersonOid != dataPacket.SenderPersonOid && w.Value.PersonOid > 0).Select(s => s.Value.PersonOid).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());
|
|
}
|
|
|
|
LogAllClients();
|
|
}
|
|
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};
|
|
|
|
LoginData wtf = null;
|
|
var wasAbleToRemoveClient = clientList.TryRemove(dataPacket.Socket, out wtf);
|
|
|
|
if(!wasAbleToRemoveClient)
|
|
{
|
|
Console.WriteLine("Konnte den Client nicht aus der Liste entfernen.");
|
|
}
|
|
|
|
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.DarkYellow;
|
|
Console.WriteLine(DateTime.Now + ": Chat-Nachricht erhalten: " + dataPacket.ChatMessage);
|
|
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 = DateTime.Now,
|
|
InsUser = dataPacket.ClientName,
|
|
IsDelivered = dataPacket.IsDelivered
|
|
};
|
|
|
|
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.Values.Any(a => a.PersonOid.Equals(dataPacket.RecipientPersonOid)))
|
|
{
|
|
var recipients = clientList.Where(w => w.Value.PersonOid.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:
|
|
LoginData ld;
|
|
var wasAbleToRemoveClient2 = clientList.TryRemove(dataPacket.Socket, out ld);
|
|
if(!wasAbleToRemoveClient2)
|
|
{
|
|
Console.WriteLine("Konnte den Client nicht aus der Liste entfernen.");
|
|
}
|
|
|
|
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.PersonOid.Equals(dataPacket.SenderPersonOid)))
|
|
{
|
|
client.Key.Send(notificationPacketLogout.GetDataStream());
|
|
}
|
|
|
|
LogAllClients();
|
|
|
|
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<Team>(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.PersonOid != dataPacket.SenderPersonOid && personOids.Contains(w.Value.PersonOid)))
|
|
{
|
|
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;
|
|
|
|
case DataIdentifier.Null:
|
|
|
|
var asfdabc = dataPacket.ChatMessage;
|
|
|
|
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))
|
|
{
|
|
LoginData loginData;
|
|
var wasAbleToRemoveClient3 = clientList.TryRemove(sender, out loginData);
|
|
if(!wasAbleToRemoveClient3)
|
|
{
|
|
Console.WriteLine("Konnte den Client nicht aus der Liste entfernen.");
|
|
}
|
|
|
|
LogAllClients();
|
|
|
|
Console.WriteLine(DateTime.Now + ": Leite Logout an die anderen Clients weiter");
|
|
foreach(var client in clientList)
|
|
{
|
|
client.Key.Send(notificationPacketLogout.GetDataStream());
|
|
}
|
|
|
|
sender.Close();
|
|
}
|
|
}
|
|
catch(SocketException se)
|
|
{
|
|
WriteErrorMessageToConsole(se.Message, se.StackTrace);
|
|
}
|
|
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.PersonOid != 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;
|
|
}
|
|
}
|
|
|
|
public class LoginData
|
|
{
|
|
public long PersonOid { get; set; }
|
|
public string MacAddress { get; set; }
|
|
public DateTime LoginTime { get; set; }
|
|
|
|
public LoginData(long pPersonOid, string pMacAddress, DateTime pLoginTime)
|
|
{
|
|
PersonOid = pPersonOid;
|
|
MacAddress = pMacAddress;
|
|
LoginTime = pLoginTime;
|
|
}
|
|
}
|
|
} |