I have async tcp server that can accept multiple clients at the same time. The scenarios where client requests data from server are served well. Now I am trying to implement the situation in which server has to find a particular client and send some data to it i.e. the client is connected but has not requested data but server wants to send some data to it. How can I find the thread that is already running between the server and client and place data on it?
Here is server code:
public async void RunServerAsync()
{
tcpListener.Start();
while (true)
{
try
{
var client = await tcpListener.AcceptTcpClientAsync();
Accept(client);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
private async void Accept(TcpClient client)
{
//get client information
String clientEndPoint = client.Client.RemoteEndPoint.ToString();
Console.WriteLine("Client connected at " + clientEndPoint );
await Task.Yield ();
try
{
using (client)
using (NetworkStream stream = client.GetStream())
{
byte[] dataReceived = new byte[100];
while (true) //read input stream
{
try
{
int x = await stream.ReadAsync(dataReceived, 0, dataReceived.Length);
if (x != 0)
{
//pass on data for processing
byte[] dataToSend = await ProcessData(dataReceived);
//send response,if any, to the client
if (dataToSend != null)
{
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
ConsoleMessages.DisplayDataSent(dataReceived, dataToSend);
}
}
}
catch (ObjectDisposedException)
{
stream.Close();
}
}
}
} //end try
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}//end Accept(TcpClient client)
You need to keep track of your clients. For example:
ConcurrentDictionary<Guid, TcpClient> _clients = new ConcurrentDictionary<Guid, TcpClient>();
public async void RunServerAsync()
{
tcpListener.Start();
while (true)
{
try
{
var client = await tcpListener.AcceptTcpClientAsync();
var clientId = Guid.NewGuid();
Accept(clientId, client);
_clients.TryAdd(clientId, client);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public Task SendAsync(Guid clientId, Byte[] buffer, Int32 offset, Int32 count)
{
TcpClient client;
if (_clients.TryGetValue(clientId, out client))
return client.GetStream().WriteAsync(buffer, offset, count);
// client disconnected, throw exception or just ignore
return Task.FromResult<Object>(null);
}
public Boolean TryGetClient(Guid clientId, out TcpClient client)
{
return _clients.TryGetValue(clientId, out client);
}
private async void Accept(Guid clientId, TcpClient client)
{
//get client information
String clientEndPoint = client.Client.RemoteEndPoint.ToString();
Console.WriteLine("Client connected at " + clientEndPoint);
await Task.Yield();
try
{
using (client)
using (NetworkStream stream = client.GetStream())
{
byte[] dataReceived = new byte[100];
while (true) //read input stream
{
try
{
int x = await stream.ReadAsync(dataReceived, 0, dataReceived.Length);
if (x != 0)
{
//pass on data for processing
byte[] dataToSend = await ProcessData(dataReceived);
//send response,if any, to the client
if (dataToSend != null)
{
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
ConsoleMessages.DisplayDataSent(dataReceived, dataToSend);
}
}
}
catch (ObjectDisposedException)
{
stream.Close();
}
}
}
} //end try
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// deregister client
_clients.TryRemove(clientId, out client);
}
}//end Accept(TcpClient client)
TCP is bi-directional, so either side can send data whenever they want.
What you need to do is store a reference to the TcpClient on Accept, something like:
//Class Level
List<TcpClient> connectedClients = List<TcpClient>();
private async void Accept(TcpClient client)
{
connectedClients.Add(client);
...
}
public SendMessage(int index, String message)
{
//pseudo-code
connectedClients[index].Send(message);
}
Normally I will raise an event on connect so whoever is using the class can get the new client's index. The send message code is pseudo-code, there are good examples on MSDN of how to actually perform the send.
Related
I'm trying to create a C# Socket multi-user server (and eventually client).
I want this server to be able to accept multiple clients (who all join and leave at different random moments) and at the same time displays their data (that the server received).
Here is my SocketMultiServer code:
using System.Net.Sockets;
using System.Text;
using System;
using System.Threading;
// Threading
Thread MainThread = Thread.CurrentThread;
Console.Title = "Multi Server";
// Aanmaken van server
TcpListener listener = new TcpListener(System.Net.IPAddress.Any, 6969);
listener.Start(5);
Console.WriteLine("Server online...");
// 2 threads
Thread ListenerThread = new Thread(o => Listener(listener));
Thread ReceiverThread = new Thread(o => Receiver());
//Thread ReceiverThread = new Thread(o => Receiver(NetworkStream stream));
ListenerThread.Start();
ReceiverThread.Start();
static void Listener(TcpListener listener)
{
Console.WriteLine("TEST 2");
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Client connected...");
NetworkStream stream = client.GetStream();
}
static void Receiver(NetworkStream stream)
{
while (true)
{
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
int recv = 0;
foreach (byte b in buffer)
{
if (b != 0)
{
recv++;
}
}
string request = Encoding.UTF8.GetString(buffer, 0, recv);
Console.WriteLine(request);
}
}
I'm having the error: "Error CS7036 There is no argument given that corresponds to the required formal parameter 'stream' of 'Receiver(NetworkStream)' " at l17
Thread ReceiverThread = new Thread(o => Receiver());
My Client code (work in progress)
using System.Net.Sockets;
using System.Text;
using System;
TcpClient client = new TcpClient("127.0.0.1", 6969);
Console.WriteLine("Connected...");
Console.Write("Username > ");
string username = Console.ReadLine();
string username_ = username + (": ");
while (true)
{
Console.WriteLine("Message to send > ");
string msg = Console.ReadLine();
if (msg == "exit()")
{
break;
}
else
{
// msg converten
int byteCount = Encoding.ASCII.GetByteCount(msg + 1);
byte[] sendData = new byte[byteCount];
sendData = Encoding.ASCII.GetBytes(username_ + msg);
NetworkStream stream = client.GetStream();
stream.Write(sendData, 0, sendData.Length);
}
}
Does someone have a fix? I'm quite new to C#
Thanks in advance!
Your primary issue is that you need to pass NetworkStream to Receiver.
However, this is not the way to do a multi-receive server anyway. You are best off using Task and async.
CancellationTokenSource cancellation = new CancellationTokenSource();
static async Task Main()
{
Console.Title = "Multi Server";
Console.WriteLine("Press ENTER to stop server...");
var task = Task.Run(RunServer);
Console.ReadLine(); // wait for ENTER
cancellation.Cancel();
await task;
}
static async Task RunServer()
{
TcpListener listener;
try
{
listener = new TcpListener(IPAddress.Any, 6969);
listener.Start(5);
Console.WriteLine("Server online...");
while (!cancellation.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync();
Console.WriteLine("Client connected...");
Task.Run(async () => await Receiver(client), cancellation.Token);
}
}
catch (OperationCancelException)
{ //
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (listener?.Active == true)
listener.Stop();
}
}
static async Task Receiver(TcpClient client)
{
try
{
using client;
using var stream = client.GetStream();
var buffer = new byte[1024];
while (!cancellation.IsCancellationRequested)
{
var bytesReceived = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesReceived == 0)
break;
string request = Encoding.UTF8.GetString(buffer, 0, bytesReceived);
Console.WriteLine(request);
}
}
catch (OperationCancelException)
{ //
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
You should have similar async code for the client. Don't forget to dispose everything with using
I am trying to create a async socket server but it does not wait for a client to connect and exits
class Program
{
IPAddress ipadd;
int port;
TcpListener tcplistener;
public async void startListeningForConnection(IPAddress ip = null, int dport = 23000)
{
if (ip == null)
{
ip = IPAddress.Any;
}
if (dport <= 0)
{
dport = 23000;
}
ipadd = ip;
port = dport;
tcplistener = new TcpListener(ipadd, port);
try
{
tcplistener.Start();
Console.WriteLine("Ready to accept client request.");
while (true)
{
TcpClient clientResponse =await tcplistener.AcceptTcpClientAsync();
HandleClientRequest(clientResponse);//method displays the message from client
}
}
catch (Exception ex)
{
Console.WriteLine("Exception occured: {0}", ex.ToString());
}
}
private void HandleClientRequest(TcpClient acceptReturn)
{
NetworkStream stream = null;
StreamReader reader = null;
try
{
stream = acceptReturn.GetStream();
reader = new StreamReader(stream);
char[] buff = new char[64];
Console.WriteLine("***Ready to read");
while (true)
{
int nRet = reader.Read(buff, 0, buff.Length);
if (nRet == 0)
{
Console.WriteLine("Socket disconnected");
break;
}
string receivedText = new string(buff);
Console.Write(receivedText);
Array.Clear(buff, 0, buff.Length);
stream.Flush();
}
stream.Close();
}
catch (Exception ex)
{
Console.WriteLine("Unknown Exception: {0}", ex.ToString());
}
finally
{
tcplistener.Stop();
Console.WriteLine("Exiting");
}
}
When i run startListeningForConnection(), I get this:
Output:
Ready to accept client request.
Press any key to continue . . .
it does not wait for a client to connect. If I remove the async and await then the program works as intended. Can anyone help me as it is my first socket program.
So I am working on creating my own proxy server for my game server.
Whats happening so far is that I try to connect to my Terraria server and it says
Connecting..
Then I start my server application which accepts incoming requests on that specific IP & port and it prompts a MessageBox saying"Connected" and then the game goes from "Connecting..." to "Connecting to server..." but it gets stuck there, this is most likely because I am not redirecting the traffic from my proxy server to my server.. Right?
I've been trying to .Write() to the stream but I think I am writing to the wrong stream, do I write to the stream that accepts connections or do I create a new stream for outgoing traffic?
public partial class MainWindow : Window
{
public static IPAddress remoteAddress = IPAddress.Parse("127.0.0.1");
public TcpListener remoteServer = new TcpListener(remoteAddress, 7777);
public TcpClient client = default(TcpClient);
public TcpClient RemoteClient = new TcpClient("terraria.novux.ru", 7777);
public MainWindow()
{
InitializeComponent();
}
private void BtnListen_OnClick(object sender, RoutedEventArgs e)
{
if (StartServer())
{
client = remoteServer.AcceptTcpClient();
MessageBox.Show("Connected");
var receivedBuffer = new byte[1024];
//Should I write to this one instead?
var clientStream = client.GetStream();
var stream = RemoteClient.GetStream();
while (client.Connected)
if (client.Connected)
if (client.ReceiveBufferSize > 0)
{
receivedBuffer = new byte[1024];
stream.Write(receivedBuffer, 0, receivedBuffer.Length);
}
}
}
private bool StartServer()
{
try
{
remoteServer.Start();
MessageBox.Show("Server Started...");
return true;
}
catch (Exception exception)
{
MessageBox.Show(exception.ToString());
throw;
}
}
}
A simplified implementation could look like this.
public class Program
{
public static void Main(string[] args)
{
StartTcpListener("localhost", 9000);
}
private static byte[] SendReceiveRemoteServer(string host, int port, byte[] data)
{
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
var client = new TcpClient(host, port);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
var stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Console.WriteLine("Sent to server: {0}", Encoding.ASCII.GetString(data));
// Receive the TcpServer.response.
// Read the first batch of the TcpServer response bytes.
var bytes = new byte[256];
var allBytes = new List<byte>();
var i = stream.Read(bytes, 0, bytes.Length);
// Loop to receive all the data sent by the client.
while (i != 0)
{
allBytes.AddRange(bytes);
bytes = new Byte[256];
i = stream.DataAvailable ? stream.Read(bytes, 0, bytes.Length) : 0;
}
Console.WriteLine("Received from server: {0}", Encoding.ASCII.GetString(data));
// Close everything.
stream.Close();
client.Close();
return allBytes.ToArray();
}
catch (ArgumentNullException e)
{
Console.WriteLine("ArgumentNullException: {0}", e);
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
Console.WriteLine("\n Press Enter to continue...");
return new byte[0];
}
private static void StartTcpListener(string host, int port)
{
TcpListener server = null;
try
{
var ipHostInfo = Dns.GetHostEntry(host);
var ipAddress = ipHostInfo.AddressList[0];
// TcpListener server = new TcpListener(port);
server = new TcpListener(ipAddress, port);
// Start listening for client requests.
server.Start();
// Enter the listening loop.
while (true)
{
Console.WriteLine("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
var client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
var stream = client.GetStream();
// Buffer for reading data
var bytes = new Byte[256];
var allBytes = new List<byte>();
var i = stream.Read(bytes, 0, bytes.Length);
// Loop to receive all the data sent by the client.
while (i != 0)
{
allBytes.AddRange(bytes);
bytes = new Byte[256];
i = stream.DataAvailable ? stream.Read(bytes, 0, bytes.Length) : 0;
}
if (allBytes.Count > 0)
{
Console.WriteLine("Received from client: {0}", Encoding.ASCII.GetString(allBytes.ToArray()));
var received = SendReceiveRemoteServer("localhost", 11000, allBytes.ToArray());
// Send back a response.
stream.Write(received, 0, received.Length);
Console.WriteLine("Sent to client: {0}", Encoding.ASCII.GetString(received));
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
}
}
Although improvements should be made:
make it async
make it work with multiple TcpClients at the same time
I am trying to listen for the client sending data multiple times. I do not know how to make the server listen to the client when the data is sent by the client a second time. For now i can send data by the client multiple times but the server will only accept the client data once. Please help.
Client
//the client
private TcpClient client = new TcpClient();
//constructor
public ClientConnect(string Ip, int Port)
{
IP = Ip;
PORT = Port;
}
//write the data to the server
public void Connect(byte[] clientId)
{
//try to connect to the server
Thread t = new Thread(() =>
{
while (true)
{
try
{
client.Connect(new IPEndPoint(IPAddress.Parse(IP), PORT));
break;
}
catch (SocketException)
{
Console.Clear();
Console.WriteLine("Trying to connect to the server...");
//pass if the connection failed
}
}
client.GetStream().Write(clientId, 0, (int)clientId.Length);
});
t.Start();
}
public void SendData(byte[] data)
{
Thread t2 = new Thread(() => {
while (true)
{
try
{
client.GetStream().Write(data, 0, (int)data.Length);
break;
}
catch (System.InvalidOperationException)
{
//pass
}
}
});
t2.Start();
}
Server:
while (true)
{
//Listen for a potential client
TcpClient client = tcpServer.AcceptTcpClient();
Console.WriteLine(client);
Console.WriteLine("Client connection accepted from " + client.Client.RemoteEndPoint + ".");
byte[] buffer = new byte[client.ReceiveBufferSize];
int bytesRead = client.GetStream().Read(buffer, 0, (int)buffer.Length);
byte[] buffer2 = new ArraySegment<byte>(buffer, 0, bytesRead).ToArray();
//do something with the data
}
i'm trying to write simple tcp\ip client-server.
here is server code:
internal class Program
{
private const int _localPort = 7777;
private static void Main(string[] args)
{
TcpListener Listener;
Socket ClientSock;
string data;
byte[] cldata = new byte[1024];
Listener = new TcpListener(_localPort);
Listener.Start();
Console.WriteLine("Waiting connections [" + Convert.ToString(_localPort) + "]...");
try
{
ClientSock = Listener.AcceptSocket();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
int i = 0;
if (ClientSock.Connected)
{
while (true)
{
try
{
i = ClientSock.Receive(cldata);
}
catch
{
}
try
{
if (i > 0)
{
data = Encoding.ASCII.GetString(cldata).Trim();
ClientSock.Send(cldata);
}
}
catch
{
ClientSock.Close();
Listener.Stop();
Console.WriteLine(
"Server closing. Reason: client offline. Type EXIT to quit the application.");
}
}
}
}
}
And here is client code:
void Main()
{
string data; // Юзерская дата
byte[] remdata ={ };
TcpClient Client = new TcpClient();
string ip = "127.0.0.1";
int port = 7777;
Console.WriteLine("\r\nConnecting to server...");
try
{
Client.Connect(ip, port);
}
catch
{
Console.WriteLine("Cannot connect to remote host!");
return;
}
Console.Write("done\r\nTo end, type 'END'");
Socket Sock = Client.Client;
while (true)
{
Console.Write("\r\n>");
data = Console.ReadLine();
if (data == "END")
break;
Sock.Send(Encoding.ASCII.GetBytes(data));
Sock.Receive(remdata);
Console.Write("\r\n<" + Encoding.ASCII.GetString(remdata));
}
Sock.Close();
Client.Close();
}
When i'm sending to my server i cannt receive data back answer. Sock.Receive(remdata) returns nothing! Why?
You're trying to receive to an empty buffer. You should allocate the buffer with a sensible size, and then take note of the amount of data received:
byte[] buffer = new byte[1024];
...
int bytesReceived = socket.Receive(buffer);
string text = Encoding.ASCII.GetString(buffer, 0, bytesReceived);
(It's somewhat unconventional to use PascalCase for local variables, by the way. I'd also urge you not to just catch Exception blindly, and not to swallow exceptions without logging them.)