/// <summary> /// Add a host to the list of known hosts to communicate with and raise the <see cref="SCHost.OnAdd"/> event. /// If there is already a host with the same IP address, then its nickname will be updated, no host will actually added and the <see cref="SCHost.OnAdd"/> event will not be raised. /// </summary> /// <param name="info">Information about the host to be added.</param> /// <param name="notifyConflict">If <code>true</code> detects nickname conflicts if any (raising the <see cref="SCHost.OnConflictNotification"/> and sending a conflict notification PDU to the other host).</param> /// <returns><code>true</code> if a host has actually been added, <code>false</code> if a nickname has been updated.</returns> public bool Add(SCHostInfo info, bool notifyConflict = true) { bool alreadyInserted = false; for (int i = 0; i < this.others.Count; i++) { if (this.others[i].Ip.Equals(info.Ip)) { alreadyInserted = true; this.others[i] = info; } else if (notifyConflict && this.others[i].Nickname == info.Nickname) { SCPdu notification = new SCPdu(this.myself.ChatID, SCPduType.NicknameConflictNotification, Encoding.Default, Encoding.Default.GetBytes(info.Ip.ToString())); this.ManualSend(this.others[i].Ip, notification); notification.Payload = Encoding.Default.GetBytes(this.others[i].Ip.ToString()); this.ManualSend(info.Ip, notification); } } if (notifyConflict && info.Nickname == this.myself.Nickname) { SCPdu notification = new SCPdu(this.myself.ChatID, SCPduType.NicknameConflictNotification, Encoding.Default, Encoding.Default.GetBytes(IPAddress.Any.ToString())); this.ManualSend(info.Ip, notification); if (this.OnConflictNotification != null) { this.OnConflictNotification(this.myself, info); } } if (!alreadyInserted) { this.others.Add(info); } return(!alreadyInserted); }
/// <summary> /// Add a host to the list of known hosts to communicate with and raise the <see cref="SCHost.OnAdd"/> event. /// If there is already a host with the same IP address, then its nickname will be updated, no host will actually added and the <see cref="SCHost.OnAdd"/> event will not be raised. /// </summary> /// <param name="info">Information about the host to be added.</param> /// <param name="notifyConflict">If <code>true</code> detects nickname conflicts if any (raising the <see cref="SCHost.OnConflictNotification"/> and sending a conflict notification PDU to the other host).</param> /// <returns><code>true</code> if a host has actually been added, <code>false</code> if a nickname has been updated.</returns> public bool Add(SCHostInfo info, bool notifyConflict = true) { bool alreadyInserted = false; for (int i = 0; i < this.others.Count; i++) { if (this.others[i].Ip.Equals(info.Ip)) { alreadyInserted = true; this.others[i] = info; } else if (notifyConflict && this.others[i].Nickname == info.Nickname) { SCPdu notification = new SCPdu(this.myself.ChatID, SCPduType.NicknameConflictNotification, Encoding.Default, Encoding.Default.GetBytes(info.Ip.ToString())); this.ManualSend(this.others[i].Ip, notification); notification.Payload = Encoding.Default.GetBytes(this.others[i].Ip.ToString()); this.ManualSend(info.Ip, notification); } } if (notifyConflict && info.Nickname == this.myself.Nickname) { SCPdu notification = new SCPdu(this.myself.ChatID, SCPduType.NicknameConflictNotification, Encoding.Default, Encoding.Default.GetBytes(IPAddress.Any.ToString())); this.ManualSend(info.Ip, notification); if (this.OnConflictNotification != null) { this.OnConflictNotification(this.myself, info); } } if (!alreadyInserted) { this.others.Add(info); } return !alreadyInserted; }
/// <summary>Initializes a new instance of the <see cref="SCHost"/> class.</summary> /// <param name="nickname">The nickname to be associated with this host.</param> /// <param name="chatID">The chat ID of the communication this host will take part into.</param> /// <param name="key">The encryption key used in this communication.</param> /// <param name="broadcast">The broadcast address of the local network.</param> /// <param name="port">The port to be used in this communication.</param> /// <param name="firstHello">If <code>true</code>, makes the host send a broadcast hello PDU once initialized.</param> public SCHost(string nickname, string chatID, byte[] key, IPAddress broadcast = null, int port = 4412, bool firstHello = true) { if (key.Length != 16) { throw new ScedaException("A SCEDA key must be 16 bytes long."); } this.myself = new SCHostInfo(); this.myself.Nickname = nickname; this.myself.ChatID = chatID; this.myself.Port = port; this.key = key; this.others = new List<SCHostInfo>(); this.socket = new UdpClient(this.myself.Port); this.socket.Client.EnableBroadcast = true; this.myself.Ip = IPAddress.Loopback; if (broadcast == null) { this.broadcast = IPAddress.Broadcast; } else { this.broadcast = broadcast; } this.disposed = false; this.remainingMalformedNotifications = 4; this.malformedTimer = new System.Timers.Timer(600000); this.malformedTimer.Elapsed += delegate { this.remainingMalformedNotifications = 4; }; this.listener = new Thread(delegate() { for (; ; ) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, this.myself.Port); byte[] binaryPdu = this.socket.Receive(ref endPoint); bool local = endPoint.Address.Equals(IPAddress.Loopback); foreach (IPAddress item in Dns.GetHostAddresses(Dns.GetHostName())) { if (endPoint.Address.Equals(item)) { local = true; } } if (!local && SCPdu.CheckChatID(binaryPdu, this.myself.ChatID)) { SCHostInfo info = new SCHostInfo(endPoint.Address, endPoint.Port, this.GetNicknameFromIp(endPoint.Address), this.myself.ChatID); try { SCPdu pdu = SCPdu.FromBinary(binaryPdu, this.key); switch (pdu.Type) { case SCPduType.Hello: { info.Nickname = pdu.Encoding.GetString(pdu.Payload); this.ManualSend(endPoint.Address, new SCPdu(this.myself.ChatID, SCPduType.Welcome, Encoding.Default, Encoding.Default.GetBytes(this.myself.Nickname))); if (this.Add(info, true) && this.OnHello != null) { this.OnHello(info); } break; } case SCPduType.Welcome: { info.Nickname = pdu.Encoding.GetString(pdu.Payload); if (this.Add(info, true) && this.OnWelcome != null) { this.OnWelcome(info); } break; } case SCPduType.Leave: { this.others.RemoveAll(item => item.Ip.Equals(info.Ip)); if (this.OnLeave != null) { this.OnLeave(info); } break; } case SCPduType.Message: { if (this.OnReceive != null) { this.OnReceive(info, pdu, pdu.Encoding.GetString(pdu.Payload)); } break; } case SCPduType.MalformedPduNotification: { if (this.OnMalformedNotification != null) { this.OnMalformedNotification(info, pdu.Payload); } break; } case SCPduType.NicknameConflictNotification: { if (this.OnConflictNotification != null) { this.OnConflictNotification(info, new SCHostInfo(IPAddress.Parse(pdu.Encoding.GetString(pdu.Payload)), this.myself.Port, this.myself.Nickname, this.myself.ChatID)); } break; } } } catch (MalformedPduException) { this.remainingMalformedNotifications--; if (this.remainingMalformedNotifications > -1) { this.ManualSend(endPoint.Address, new SCPdu(this.myself.ChatID, SCPduType.Hello, Encoding.Default, new byte[0])); this.malformedTimer.Start(); } if (this.OnMalformedReceived != null) { this.OnMalformedReceived(info, binaryPdu); } } } } }); this.listener.Start(); if (firstHello) { this.Hello(); } }
static void myself_OnWelcome(SCHostInfo info) { int left; int top; ConsoleInsertLine(out left, out top); ConsoleWriteInColor(info.Nickname + " is online.", ConsoleColor.Blue); Console.SetCursorPosition(left, top); }
/// <summary>Initializes a new instance of the <see cref="SCHost"/> class.</summary> /// <param name="nickname">The nickname to be associated with this host.</param> /// <param name="chatID">The chat ID of the communication this host will take part into.</param> /// <param name="key">The encryption key used in this communication.</param> /// <param name="broadcast">The broadcast address of the local network.</param> /// <param name="port">The port to be used in this communication.</param> /// <param name="firstHello">If <code>true</code>, makes the host send a broadcast hello PDU once initialized.</param> public SCHost(string nickname, string chatID, byte[] key, IPAddress broadcast = null, int port = 4412, bool firstHello = true) { if (key.Length != 16) { throw new ScedaException("A SCEDA key must be 16 bytes long."); } this.myself = new SCHostInfo(); this.myself.Nickname = nickname; this.myself.ChatID = chatID; this.myself.Port = port; this.key = key; this.others = new List <SCHostInfo>(); this.socket = new UdpClient(this.myself.Port); this.socket.Client.EnableBroadcast = true; this.myself.Ip = IPAddress.Loopback; if (broadcast == null) { this.broadcast = IPAddress.Broadcast; } else { this.broadcast = broadcast; } this.disposed = false; this.remainingMalformedNotifications = 4; this.malformedTimer = new System.Timers.Timer(600000); this.malformedTimer.Elapsed += delegate { this.remainingMalformedNotifications = 4; }; this.listener = new Thread(delegate() { for (; ;) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, this.myself.Port); byte[] binaryPdu = this.socket.Receive(ref endPoint); bool local = endPoint.Address.Equals(IPAddress.Loopback); foreach (IPAddress item in Dns.GetHostAddresses(Dns.GetHostName())) { if (endPoint.Address.Equals(item)) { local = true; } } if (!local && SCPdu.CheckChatID(binaryPdu, this.myself.ChatID)) { SCHostInfo info = new SCHostInfo(endPoint.Address, endPoint.Port, this.GetNicknameFromIp(endPoint.Address), this.myself.ChatID); try { SCPdu pdu = SCPdu.FromBinary(binaryPdu, this.key); switch (pdu.Type) { case SCPduType.Hello: { info.Nickname = pdu.Encoding.GetString(pdu.Payload); this.ManualSend(endPoint.Address, new SCPdu(this.myself.ChatID, SCPduType.Welcome, Encoding.Default, Encoding.Default.GetBytes(this.myself.Nickname))); if (this.Add(info, true) && this.OnHello != null) { this.OnHello(info); } break; } case SCPduType.Welcome: { info.Nickname = pdu.Encoding.GetString(pdu.Payload); if (this.Add(info, true) && this.OnWelcome != null) { this.OnWelcome(info); } break; } case SCPduType.Leave: { this.others.RemoveAll(item => item.Ip.Equals(info.Ip)); if (this.OnLeave != null) { this.OnLeave(info); } break; } case SCPduType.Message: { if (this.OnReceive != null) { this.OnReceive(info, pdu, pdu.Encoding.GetString(pdu.Payload)); } break; } case SCPduType.MalformedPduNotification: { if (this.OnMalformedNotification != null) { this.OnMalformedNotification(info, pdu.Payload); } break; } case SCPduType.NicknameConflictNotification: { if (this.OnConflictNotification != null) { this.OnConflictNotification(info, new SCHostInfo(IPAddress.Parse(pdu.Encoding.GetString(pdu.Payload)), this.myself.Port, this.myself.Nickname, this.myself.ChatID)); } break; } } } catch (MalformedPduException) { this.remainingMalformedNotifications--; if (this.remainingMalformedNotifications > -1) { this.ManualSend(endPoint.Address, new SCPdu(this.myself.ChatID, SCPduType.Hello, Encoding.Default, new byte[0])); this.malformedTimer.Start(); } if (this.OnMalformedReceived != null) { this.OnMalformedReceived(info, binaryPdu); } } } } }); this.listener.Start(); if (firstHello) { this.Hello(); } }
static void myself_OnReceive(SCHostInfo info, SCPdu pdu, string message) { int left; int top; ConsoleInsertLine(out left, out top); if (info.Nickname == "") { ConsoleWriteInColor("unknown: ", ConsoleColor.Yellow); } else { ConsoleWriteInColor(info.Nickname + ": ", ConsoleColor.Cyan); } Console.Write(message); Console.SetCursorPosition(left, top); }
private static void myself_OnMalformed(SCHostInfo info, byte[] pdu) { int left; int top; ConsoleInsertLine(out left, out top); ConsoleWriteInColor("Problem while communicating with " + info.Nickname, ConsoleColor.Red); if (info.Nickname == "") { ConsoleWriteInColor("unknown", ConsoleColor.Yellow); } ConsoleWriteInColor("!", ConsoleColor.Red); Console.SetCursorPosition(left, top); }
static void myself_OnLeave(SCHostInfo info) { int left; int top; ConsoleInsertLine(out left, out top); ConsoleWriteInColor(info.Nickname + " is offline!", ConsoleColor.Green); Console.SetCursorPosition(left, top); }
static void myself_OnHello(SCHostInfo info) { int left; int top; ConsoleInsertLine(out left, out top); ConsoleWriteInColor(info.Nickname + " has joined the chat!", ConsoleColor.Blue); Console.SetCursorPosition(left, top); }
static void myself_OnConflictNotification(SCHostInfo informerInfo, SCHostInfo rivalInfo) { if (!conflictNotified) { conflictNotified = true; int left; int top; ConsoleInsertLine(out left, out top); ConsoleWriteInColor("Nickname conflict detected!", ConsoleColor.Red); Console.SetCursorPosition(left, top); } }