How to send messages to all clients in multithread chat server? - c#

Sorry for asking about the 'same' thing over and over again. It's yet another edition of the chat. With a lot of searching around I've finally found out how to pass the client list (or at least I hope so) to the Chat function.
However I don't know if this is even supposed to work:
Everytime a client connects :
clients.Add(clientSocket);
var ctThread = new System.Threading.Thread(() => Chat(clients));
where the Chat function hopefully correctly receives the clients via
public void Chat(List<TcpClient> clients)
and then writes this out
foreach (var client in clients)
{
writer.Write(message);
}
With the client having 2 threads (not sure if they can actually read/write at the same time)
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
Did I pass the client list to the function properly? and can it actually correctly send the messages? Because right now the server is not responding to anything that I type on the client.
Full code:
Server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;
namespace MultiServeris
{
class Multiserveris
{
static void Main(string[] args)
{
TcpListener ServerSocket = new TcpListener(1000);
ServerSocket.Start();
List<TcpClient> clients = new List<TcpClient>();
Console.WriteLine("Server started.");
while (true)
{
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
handleClient client = new handleClient();
clients.Add(clientSocket);
client.startClient(clientSocket,clients);
}
}
}
public class handleClient
{
TcpClient clientSocket;
public void startClient(TcpClient inClientSocket, List<TcpClient> clients)
{
this.clientSocket = inClientSocket;
var ctThread = new System.Threading.Thread(() => Chat(clients));
}
public void Chat(List<TcpClient> clients)
{
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
while (true)
{
string message = reader.ReadString();
foreach (var client in clients)
{
writer.Write(message);
}
}
}
}
}
Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace Klientas
{
class Klientas
{
public static void Write()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
string str = Console.ReadLine();
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
writer.Write(str);
}
}
public static void Read()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
string message = reader.ReadString();
Console.WriteLine(message);
}
}
static void Main(string[] args){
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
}
}
}

TCP is not design for broadcasting which is why you're having to loop through all your clients. A better approach would be to use a protocol that supports brodcast use the SignalR Framework or if you want baremetal access use UDP. Here's a great SignalR chat example.
https://dhavalupadhyaya.wordpress.com/tag/signalr-chat-example/

Related

c# windows forms app /client server /checking client input to do something is not working in

I'm creating a client server app using c# and what it does :
1- the client sends a msg then the server sends it back to the client
2- I want to check what the client send and do some process like if the client send "chrome"
the server checks it and open chrome.exe
I don't know why its not working here on my code:
when the client sends chrome it always shows client disconnected message
also i cant convert it to windows froms app its shows lots of errors
#server code!
using System;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net;
using System.Text;
namespace consoleTcpServer {
class Program {
class ConnectionHandler {
private Socket client;
private NetworkStream ns;
private StreamReader reader;
private StreamWriter writer;
private static int connections = 0;
//The constructor take the accepted client as argument
public ConnectionHandler(Socket client) {
this.client = client;
}
public void HandleConnection() {
try {
ns = new NetworkStream(client);
reader = new StreamReader(ns);
writer = new StreamWriter(ns);
connections++;
Console.WriteLine("New client accepted: {0} active connections",
connections);
writer.WriteLine("Welcome to my server");
writer.Flush();
string input;
while ((input = reader.ReadLine()).Length != 0) {
if (input.Contains("chrome")) {
System.Diagnostics.Process.Start("chrome.exe", "www.google.com");
} else if (input.Contains("fox")) {
System.Diagnostics.Process.Start("firefox.exe", "www.facebook.com");
}
Console.WriteLine(input);
writer.WriteLine(input);
writer.Flush();
}
// ns.Close();
// client.Close();
connections--;
Console.WriteLine("Client disconnected: {0} active connections", connections);
} catch (Exception) {
connections--;
Console.WriteLine("Client disconnected: {0} active connections",
connections);
}
}
}
static void Main(string[] args) {
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 9050);
server.Bind(localEP);
server.Listen(10);
Console.WriteLine("Waiting for a client");
while (true) {
try {
Socket client = server.Accept();
ConnectionHandler handler = new ConnectionHandler(client);
Thread thread = new Thread(new ThreadStart(handler.HandleConnection));
thread.Start();
} catch (Exception) {
Console.WriteLine("Connection failed ..");
}
//client.Close();
//server.Shutdown();
}
}
}
}
#client code
using System;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace consoleTcpClient {
class Program {
static void Main(string[] args) {
// Console.WriteLine("Hello World!");
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
client.Connect(remoteEP);
NetworkStream stream = new NetworkStream(client);
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
String input = reader.ReadLine();
Console.WriteLine(input);
String line = null;
while (true) {
Console.Write("Enter Message for Server <Enter to Stop >: ");
line = Console.ReadLine();
//writing for server
writer.WriteLine(line);
writer.Flush();
if (line.Length != 0) {
line = "Echo: " + reader.ReadLine();
Console.WriteLine(line);
}
}
// client.Close();
}
}
}

how to return a value back to a different class once Network stream recieves it c# (message chat)

I'm having a hard time naming this question and solving my problem.
I'm challenging myself in creating a chat server with multiple chat clients.
I've created a server that instantiates a new ClientManager object upon receiving a new connection via TCP. I'm missing various bits of management currently so ignore that as I'm focusing on figuring this bit out first:
Once the connection is established the ClientManager creates a new background worker for receiving streams. What I'm having trouble is once the stream is read, how can I forward that information back to my main server class to distribute amounts to the other clients (chat clients).
Once again, I'm missing a few management options such as threading, semaphores, etc but that will come as I build it out.
Here is how my server established a connection:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;
namespace ChatServer
{
class Program
{
private IPAddress localAddr;
private int serverPort;
private BackgroundWorker bwListener;
private List<ClientManager> clients;
private TcpListener server;
static void Main(string[] args)
{
Program progInstance = new Program();
progInstance.clients = new List<ClientManager>();
progInstance.server = null;
try
{
// Set the TcpListener on port 13000.
progInstance.serverPort = 13000;
progInstance.localAddr = IPAddress.Parse("127.0.0.1");
// Start to listen
progInstance.bwListener = new BackgroundWorker();
progInstance.bwListener.WorkerSupportsCancellation = true;
progInstance.bwListener.DoWork += new DoWorkEventHandler(progInstance.ServerListen);
progInstance.bwListener.RunWorkerAsync();
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
// Stop listening for new clients.
//progInstance.server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
Console.Read();
}
private void ServerListen(object sender, DoWorkEventArgs e) {
this.server = new TcpListener(this.localAddr, this.serverPort);
this.server.Start();
// Enter the listening loop.
while (true)
{
Console.Write("Waiting for a connection... \n");
Socket client = server.AcceptSocket();
this.AcceptClientConnection(client);
}
}
private void AcceptClientConnection(Socket client) {
ClientManager newClient = new ClientManager(client);
this.clients.Add(newClient);
}
private void DisplayClientConnections() {
}
}
}
And this is the ClientManager
using System;
using System.Net.Sockets;
using System.ComponentModel;
namespace ChatServer
{
class ClientManager
{
NetworkStream stream;
private Socket socket;
private BackgroundWorker bwReceiver;
//data = null;
public ClientManager(Socket socket)
{
this.socket = socket;
this.stream = new NetworkStream(this.socket);
this.bwReceiver = new BackgroundWorker();
this.bwReceiver.DoWork += new DoWorkEventHandler(StartReceive);
this.bwReceiver.RunWorkerAsync();
}
private void StartReceive(object sender, DoWorkEventArgs e) {
String data = null;
Byte[] bytes = new Byte[256];
int i;
while (this.socket.Connected) {
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
}
}
}
}
}
I was thinking that the server should somehow retrieve this information and then write it to the stream, or would I just do that would I just write back to the stream from the ClientManager and then have my actual clients listen and receive? I'm just super lost how to handle this portion of the chat server.

How Do I Read And Parse XML From A TCP Stream?

Thanks for taking the time to look at my question! This is my first post, so please excuse me if i did something wrong forum wise :)
EDIT: I appear to have solved my problem. I simply read the TCP stream 1 line at a time, and from there I use if statements to determine the reading start and stop spots.
Updated Code:
using System;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
using System.Text;
namespace RFLY_CMD
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
TcpListener server = new TcpListener(IPAddress.Parse("0.0.0.0"), 7362);
server.Start();
Console.WriteLine("Listening...");
Console.WriteLine();
while (true)
{
ClientWorking cw = new ClientWorking(server.AcceptTcpClient());
new Thread(new ThreadStart(cw.DoSomethingWithClient)).Start();
}
}
}
class ClientWorking
{
private Stream ClientStream;
private TcpClient Client;
public ClientWorking(TcpClient Client)
{
this.Client = Client;
ClientStream = Client.GetStream();
}
public void DoSomethingWithClient()
{
StreamWriter sw = new StreamWriter(ClientStream);
StreamReader sr = new StreamReader(sw.BaseStream);
string data;
bool cap = false;
string full_string = null;
try
{
while (true)
{
data = sr.ReadLine();
if (!string.IsNullOrEmpty(data) && !string.IsNullOrWhiteSpace(data) || cap)
{
if(data == "... start")
{
cap = true;
Console.WriteLine("---START FOUND!!!---");
}
if (cap)
{
Console.WriteLine(data);
full_string += data + "--:BREAK:--";
}
if (data == "... end")
{
cap = false;
Console.WriteLine("---END FOUND!!!---");
Console.WriteLine();
Console.WriteLine("-------------FULL STRING-------------");
Console.WriteLine(full_string);
}
}
}
}
finally
{
Console.Write("Ending...");
sw.Close();
}
}
}
}
Working Screenshot
End Edit.
Ok, so here is the problem... I have an application sending XML like so:
XML Screenshot
But, I only need the string of the text in the red box shown here:
Target Data
This is my first time dealing with XML and I have a little experience with TCP. Other than that, I am in WAY over my head.
Thanks in advance!
Here is my code:
using System;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
using System.Text;
using System.Xml;
namespace RFLY_CMD
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 7362);
server.Start();
Console.WriteLine("Started.");
while (true)
{
ClientWorking cw = new ClientWorking(server.AcceptTcpClient());
new Thread(new ThreadStart(cw.DoSomethingWithClient)).Start();
}
}
}
class ClientWorking
{
private Stream ClientStream;
private TcpClient Client;
public ClientWorking(TcpClient Client)
{
this.Client = Client;
ClientStream = Client.GetStream();
}
public void DoSomethingWithClient()
{
StreamWriter sw = new StreamWriter(ClientStream);
StreamReader sr = new StreamReader(sw.BaseStream);
string data;
try
{
while (true)
{
data = sr.ReadToEnd();
if (!string.IsNullOrEmpty(data) && !string.IsNullOrWhiteSpace(data))
{
//Console.Clear();
Console.WriteLine(data);
//Console.WriteLine("---------------");
}
sr.DiscardBufferedData();
}
}
finally
{
Console.Write("Ending...");
sw.Close();
}
}
}
}

C# Chat clients with multiple threads (read+write at the same time)

How do I make the client read from the stream (for messages of other clients sent to the stream) at the same time as being able to write to them?
I tried creating different threads on the client side (did I even do it right?), however I can still only write to the server, without any response. This is what I'm getting right now:
(Server-Client-Client)
Client:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace Klientas
{
class Klientas
{
public static void Write()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
string str = Console.ReadLine();
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
writer.Write(str);
}
}
public static void Read()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
Console.WriteLine(reader.ReadString());
}
}
static void Main(string[] args){
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
}
}
}
Server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;
namespace MultiServeris
{
class Multiserveris
{
static void Main(string[] args)
{
TcpListener ServerSocket = new TcpListener(1000);
ServerSocket.Start();
Console.WriteLine("Server started.");
while (true)
{
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
handleClient client = new handleClient();
client.startClient(clientSocket);
}
}
}
public class handleClient
{
TcpClient clientSocket;
public void startClient(TcpClient inClientSocket)
{
this.clientSocket = inClientSocket;
Thread ctThread = new Thread(Chat);
ctThread.Start();
}
private void Chat()
{
while (true)
{
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
Console.WriteLine(reader.ReadString());
}
}
}
}
It seems like you do not have any code on the server to send messages to the client. You need to maintain a list of connected clients and make the server send a message to the eligible clients when it receives a message. Also do not make the client a console app. Unlike most projects for a chat client it is actually harder to do it as a console app.
To keep a list of clients you declare a list of TCP Clients like this
static List<TcpClient> clients = new List<TcpClient>();
Then when a client connects you add it to the list
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
clients.Add(clientSocket);
Then when you receive a message you send it to all clients
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
while(true)
{
string message = reader.ReadString();
foreach(var client in clients)
{
//send message to client
}
}
Now remember that in practice you should handle things like disconnects and adding and removing clients from the list should be thread safe (locks and all).
Good starting point for socket client-server communication: demo application and library. Support for reconnecting, sudden client disconnect catch, message broadcasting and many more.

C# Connecting two tcp sockets together

I'm not sure if the title is all that informative.
I am trying to find/write a socket server that will accept a connection from the client (telnet) and then on behalf of the connected client, connect to one of four telnet servers inside the network.
Once connected I keep a counter of how many connections there are, and then if there are 4 total connections, disallow any new connections until one of the four is available.
I have written this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static int nodeCount = 4;
static int currentNode = 1;
static void Main(string[] args)
{
ServerProgram server = new ServerProgram();
}
class ServerProgram
{
private TcpListener tcpPrimaryListener;
private Thread listenThread;
public ServerProgram()
{
this.tcpPrimaryListener = new TcpListener(IPAddress.Any, 23);
Console.WriteLine("Telnet BBS Port Concentrator Server Started.");
Console.WriteLine("--------------------------------------------");
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpPrimaryListener.Start();
while (true)
{
TcpClient client = this.tcpPrimaryListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
if (currentNode <= nodeCount)
{
Console.WriteLine("Connection thread created.");
StreamWriter swStream;
StreamWriter swStream2;
StreamReader srStream;
StreamReader srStream2;
TcpClient tcpClient = (TcpClient)client;
NetworkStream tcpClientStream = tcpClient.GetStream();
TcpClient telnet = new TcpClient("192.168.100.5" + currentNode, 23);
NetworkStream telnetStream = telnet.GetStream();
currentNode++;
while (true)
{
srStream = new StreamReader(tcpClient.GetStream());
swStream2 = new StreamWriter(tcpClient.GetStream());
srStream2 = new StreamReader(telnet.GetStream());
swStream = new StreamWriter(telnet.GetStream());
swStream.Write(srStream.ReadToEnd());
swStream2.Write(srStream2.ReadToEnd());
}
}
}
}
}
}
I've changed this example multiple times, so I don't really know anymore what I have and have not tried. I'm willing to try anything.
The purpose is actually running this to allow one telnet port open through the firewall, and allowing connections into a small network of DOS machines running telnet fossil driver BBS software. I would just like to redirect telnet traffic to an available system using only one port.
The problem is that I cannot figure out how to actually connect the two sockets together and pass data between them as it happens. The incoming socket and the socket I created on behalf of the server to the server.
Thanks.
UPDATE:
This is what is working for me, I'm still looking over for bugs but it's working so far.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static int nodeCount = 2;
static int currentNode = 1;
static void Main(string[] args)
{
ServerProgram server = new ServerProgram();
}
class ServerProgram
{
private TcpListener tcpPrimaryListener;
private Thread listenThread;
public ServerProgram()
{
this.tcpPrimaryListener = new TcpListener(IPAddress.Any, 23);
Console.WriteLine("Telnet BBS Port Concentrator Server Started.");
Console.WriteLine("--------------------------------------------");
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpPrimaryListener.Start();
while (true)
{
TcpClient client = this.tcpPrimaryListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
string noNodes = "Sorry all nodes are occupied.";
if (currentNode <= nodeCount)
{
Console.WriteLine("Client connected.");
TcpClient tcpClient = (TcpClient)client;
NetworkStream tcpClientStream = tcpClient.GetStream();
TcpClient telnet = new TcpClient("10.24.9.11", 23);
//TcpClient telnet = new TcpClient("192.168.100.5" + currentNode, 23);
NetworkStream telnetStream = telnet.GetStream();
currentNode++;
ByPass linkedSockets = new ByPass(tcpClientStream, telnetStream);
}
else
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream tcpClientStream = tcpClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
tcpClientStream.Write(Encoding.ASCII.GetBytes(noNodes), 0, noNodes.Length);
}
}
}
public class ByPass
{
public ByPass(Stream s1, Stream s2)
{
var cTokenSource = new CancellationTokenSource();
var cToken = cTokenSource.Token;
Task.Factory.StartNew(() => Process(s1, s2, cToken, cTokenSource), cToken);
Task.Factory.StartNew(() => Process(s2, s1, cToken, cTokenSource), cToken);
cToken.Register(() => cancelNotification());
}
public void Process(Stream s1, Stream s2, CancellationToken ct, CancellationTokenSource cTokenSource)
{
byte[] buf = new byte[0x10000];
while (true)
{
if (ct.IsCancellationRequested)
{
break;
}
try
{
int len = s1.Read(buf, 0, buf.Length);
s2.Write(buf, 0, len);
}
catch
{
s1.Close(); s2.Close();
cTokenSource.Cancel();
break;
}
}
}
}
static void cancelNotification()
{
Console.WriteLine("Client disconnected.");
currentNode--;
}
}
}
I think, you can create a class similar to below to pass data between two streams
public class ByPass
{
public ByPass(Stream s1, Stream s2)
{
Task.Factory.StartNew(() => Process(s1, s2));
Task.Factory.StartNew(() => Process(s2, s1));
}
public void Process(Stream sIn, Stream sOut)
{
byte[] buf = new byte[0x10000];
while (true)
{
int len = sIn.Read(buf, 0, buf.Length);
sOut.Write(buf, 0, len);
}
}
}
I have made little changes and it works perfect on my side
public class StreamTransmitter
{
static TaskCompletionSource<bool> ts;
public static async Task Start(Stream s1, Stream s2, CancellationToken token)
{
ts = new TaskCompletionSource<bool>();
Process(s1, s2, token);
Process(s2, s1, token);
await ts.Task;
}
private static async Task Process(Stream sIn, Stream sOut, CancellationToken token)
{
byte[] buf = new byte[0x10000];
int len = 0;
do
{
len = await sIn.ReadAsync(buf, 0, buf.Length, token);
await sOut.WriteAsync(buf, 0, len, token);
}
while (len > 0 && !token.IsCancellationRequested);
ts.SetResult(true);
}
}

Categories

Resources