Last active
August 24, 2020 09:12
-
-
Save cirocorvino/eebe5a16c2004524d5b7c09f936d52f9 to your computer and use it in GitHub Desktop.
C# simple chat server example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace ChatServerExample | |
{ | |
/// <summary> | |
/// Console app that allows telnet client to connect and chat on the port 10000. | |
/// <para>Minimum sdk requirements: C# 5 and .Net 4.5 </para> | |
/// </summary> | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
StartServer(); | |
} | |
static void StartServer() { | |
List<Socket> clientSockets = new List<Socket>(); | |
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |
server.Bind(new IPEndPoint(IPAddress.Any, 10000)); | |
server.Listen(backlog: 15); | |
Console.WriteLine($"Chat server is listening on port: {((IPEndPoint)server.LocalEndPoint).Port}"); | |
var cts = new CancellationTokenSource(); | |
Task.Run(() => { | |
while (true) | |
{ | |
Console.WriteLine("Waiting for accept new client..."); | |
var client = server.Accept(); | |
if (!client.Connected) | |
{ | |
Console.WriteLine("Client not connected"); | |
continue; | |
} | |
Console.WriteLine($"Client connected from {client.ClientOrigin()}"); | |
clientSockets.Add(client); | |
ClientCommunicationHandler(client); | |
} | |
}, cts.Token); | |
//dispatch every message to all connected clients | |
//Task.Run(async () => | |
Task.Run(() => | |
{ | |
while (true) | |
{ | |
var newMessages = Chat.NewMessages; | |
if (newMessages == null || newMessages.Count == 0) | |
continue; | |
BroadcastMessages(clientSockets, newMessages); | |
} | |
}, cts.Token); | |
Console.WriteLine("Press Return to exit"); | |
Console.ReadLine(); | |
cts.Cancel(); | |
} | |
static async Task ClientCommunicationHandler(Socket clientSocket) { | |
try | |
{ | |
using (var stream = new NetworkStream(clientSocket, true)) { | |
using (var reader = new StreamReader(stream, Encoding.ASCII, false, 10000, true)) { | |
using (var writer = new StreamWriter(stream, Encoding.ASCII, 10000, true)) | |
{ | |
writer.AutoFlush = true; | |
await writer.WriteLineAsync($"connected to chat server"); | |
while (true) { | |
var msgFromClient = await reader.ReadLineAsync(); | |
Console.WriteLine($"client from {clientSocket.ClientOrigin()} --> {msgFromClient}"); | |
if(string.Compare(msgFromClient, "quit", ignoreCase: true) == 0) | |
{ | |
break; | |
} | |
await Chat.AddNewMessage($"client from {clientSocket.ClientOrigin()} --> {msgFromClient}"); | |
} | |
Console.WriteLine($"client from {clientSocket.ClientOrigin()} disconnected"); | |
} | |
} | |
} | |
} | |
catch (Exception ex){ | |
Console.WriteLine(ex.Message); | |
} | |
} | |
static void BroadcastMessages(List<Socket> clientSockets, List<Guid> newMessages) | |
{ | |
foreach (var client in clientSockets) | |
{ | |
using (var stream = new NetworkStream(client, false)) | |
{ | |
using (var writer = new StreamWriter(stream, Encoding.ASCII, 10000, true)) | |
{ | |
writer.AutoFlush = true; | |
foreach (var msgId in newMessages) | |
{ | |
var msg = Chat.GetMessageByGuid(msgId); | |
if (msg.Contains(client.ClientOrigin())) { | |
continue; | |
} | |
writer.WriteLine(msg); | |
Chat.SetMessageAsBroadcasted(msgId); | |
} | |
} | |
} | |
} | |
} | |
} | |
public static class Chat { | |
private class Message { | |
public Guid id; | |
public string message; | |
public bool isNewMsg; | |
} | |
private static List<Message> chatMessage; | |
private static object m_lockChatMessage = new object(); | |
public static List<Guid> NewMessages { | |
get { | |
lock (m_lockChatMessage) | |
{ | |
if (chatMessage == null) | |
{ | |
return null; | |
} | |
return chatMessage.Where(m => m != null && m.isNewMsg).Select(m => m.id).ToList(); | |
} | |
} | |
} | |
public static string GetMessageByGuid(Guid id) { | |
lock (m_lockChatMessage) | |
{ | |
return chatMessage.FirstOrDefault(m => m.id == id)?.message; | |
} | |
} | |
public static async Task AddNewMessage(string p_message) | |
{ | |
lock (m_lockChatMessage) | |
{ | |
if(chatMessage == null) | |
{ | |
chatMessage = new List<Message>(); | |
} | |
chatMessage.Add(new Message | |
{ | |
id = Guid.NewGuid(), | |
message = p_message, | |
isNewMsg = true | |
}); | |
} | |
} | |
public static void SetMessageAsBroadcasted(Guid id) | |
{ | |
lock (m_lockChatMessage) | |
{ | |
var msg = chatMessage.FirstOrDefault(m => m.id == id); | |
if (msg != null) | |
{ | |
msg.isNewMsg = false; | |
} | |
} | |
} | |
} | |
public static class ExtMethods | |
{ | |
public static string ClientOrigin(this Socket client) | |
{ | |
return $"address: {((IPEndPoint)client.RemoteEndPoint).Address} - port: {((IPEndPoint)client.RemoteEndPoint).Port}"; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment