Multiple socket connections using asynchronous sockets - c#

I have the following code that makes a TCP socket connection to multiple end points as such:
private async void button1_Click(object sender, EventArgs e)
{
var listofIps = new List<string> { "192.168.168.193", "192.168.168.221" };
foreach (var ip in listofIps)
{
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ip), 4001);
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
sockets.Add(client);
await client.ConnectTaskAsync(remoteEP);
await ReadAsync(client);
}
}
async Task ReadAsync(Socket s)
{
var args = new SocketAsyncEventArgs();
args.SetBuffer(new byte[1024], 0, 1024);
var awaitable = new SocketAwaitable(args);
while (true)
{
await s.ReceiveAsync(awaitable);
int bytesRead = args.BytesTransferred;
if (bytesRead <= 0) break;
var data = new ArraySegment<byte>(args.Buffer, 0, bytesRead);
AppendLog("RX: " + data.DumpHex());
}
}
public static string DumpHex(this ArraySegment<byte> data)
{
return string.Join(" ", data.Select(b => b.ToString("X2")));
}
public static Task ConnectTaskAsync(this Socket socket, EndPoint endpoint)
{
return Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, endpoint, null);
}
However, the loop doesn't iterate pass the first IP address, because of the await ReadAsync.
Qn 1) How do I modify this code such that it iterates through my whole list of IPs without "awaiting" for the data be received completely. Do I remove the await keyword from await ReadAsync(client)?
Qn 2) How do I modify this code such that it connects to all the IPs at once. (is this possible?)

How do I modify this code such that it iterates through my whole list of IPs
Your code will iterate the whole list. But it will get to the second address only after it receives all data from the first address.
How do I modify this code such that it connects to all the IPs at once.
If you encapsulate the code for a single address into a separate async method, you can then first call it for each address and then await all the returned Tasks. Or you could use LINQ:
private async void button1_Click(object sender, EventArgs e)
{
var listOfIps = new List<string> { "192.168.168.193", "192.168.168.221" };
await Task.WhenAll(listOfIps.Select(ip => ReadFromAddress(ip)));
}
private async Task ReadFromAddress(string address)
{
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ip), 4001);
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
sockets.Add(client);
await client.ConnectTaskAsync(remoteEP);
await ReadAsync(client);
}

Related

Multiple TCPServer/Client C#

I am trying to learn C# programming language. I have a very simple question here. I wanted to make TCPServer and TCPClient. but I couldn't quite do what I wanted to do. What I want is to have 4 clients on 1 server and they can talk to each other. I want the names of the people speaking to be determined individually. Please can anyone help with this? (Sorry for bad english)
/Server Code/
namespace SimpleTcpSrvr
{
class Program
{
//private static object mTcpClient;
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
int asd;
byte[] data = new byte[1024];
IPEndPoint asd2 = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1302);
Socket newsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newsocket.Bind(asd2);
newsocket.Listen(100);
Console.WriteLine("waiting for connection...");
Socket client = newsocket.Accept();
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
var socket = (Socket)client;
Console.WriteLine((string.Format("New connection: " + socket.RemoteEndPoint.ToString())));
string welcome = "Chat Server";
data = Encoding.UTF8.GetBytes(Welcome my chat server);
client.Send(data, data.Length, SocketFlags.None);
string input;
while (true)
{
data = new byte[1024];
asd = client.Receive(data);
if (asd == 0)
break;
Console.WriteLine("TCPClient: " + Encoding.UTF8.GetString(data, 0, asd));
input = Console.ReadLine();
Console.SetCursorPosition(0, Console.CursorTop - 1);
Console.WriteLine("You: " + input);
client.Send(Encoding.UTF8.GetBytes(input));
}
Console.WriteLine("{0}'sinin bağlantısı kesildi.", clientep.Address);
client.Close();
newsocket.Close();
Console.ReadLine();
}
/Client Code/
namespace TCPClient
{
public class TcpClient
{
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.ASCII;
byte[] data = new byte[1024];
string input, stringData;
IPEndPoint asd2 = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1302);
Socket newsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
newsocket.Connect(asd2);
}
catch (SocketException e)
{
Console.WriteLine("cant connect server");
Console.WriteLine(e.ToString());
return;
}
int asd = newsocket.Receive(data);
stringData = Encoding.UTF8.GetString(data, 0, asd);
Console.WriteLine(stringData);
while (true)
{
input = Console.ReadLine();
Console.SetCursorPosition(0, Console.CursorTop - 1);
Console.WriteLine("You: " + input);
newsocket.Send(Encoding.UTF8.GetBytes(input));
data = new byte[1024];
asd = newsocket.Receive(data);
stringData = Encoding.UTF8.GetString(data, 0, asd);
byte[] utf8string = Encoding.UTF8.GetBytes(stringData);
Console.WriteLine("TCPServer:" + stringData);
}
Console.WriteLine("connection lost from server...");
newsocket.Shutdown(SocketShutdown.Both);
newsocket.Close();
Console.WriteLine("disconnected!");
Console.ReadLine();
}
}
}
Firstly, it has to be said, there are a million-and-one edge cases when working with sockets - unless you are actually writing a new transport-protocol to run over TCP, you'd be much better off investing your time into learning an existing transport such as HTTP or gRPC.
With that disclaimer out of the way:
Use TcpListener() instead of Socket() in your server.
Use TcpClient() instead of Socket() in your client.
Your server needs to be able to simultaneously handle multiple clients, for this, use the Task-based Asyncronous Pattern (TAP).
So in your server, you need an instance of TcpClient() for every connection that your server accepts().
Start with a place to store all of your Tasks, e.g.,
static List<Task> Tasks = new();
Next you need a TcpListener which accepts incomming connections and spawns a Task to manage the TcpClient associated with that connection e.g.,
static async Task RunServerAsync()
{
TcpListener tcpListener = new(IPAddress.Loopback, 9999);
tcpListener.Start();
while (true)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
Tasks.Add(Task.Run(() => RunClientAsync(tcpClient)));
}
}
A simple TcpClient Task would look something like this
static async Task RunClientAsync(TcpClient tcpClient)
{
Console.WriteLine($"Connection from: [{tcpClient.Client.RemoteEndPoint}]");
var reader = new StreamReader(tcpClient.GetStream());
while (true)
{
var line = await reader.ReadLineAsync();
Console.WriteLine($"{tcpClient.Client.RemoteEndPoint}: {line}");
}
}
You can tie this together in a Main() like this:
static async Task Main(string[] args)
{
await Task.Run(() => RunServerAsync());
}
Now you have a very simple echo-line server that will accept connections from as many clients as you can throw at it.
To wrap it up, you could use the TAP to create clients, for your testing, maybe something like:
static async Task RunClientAsync(string message)
{
var tcpClient = new TcpClient("127.0.0.1", 9999);
StreamWriter sw = new(tcpClient.GetStream());
tcpClient.NoDelay = true;
while(true)
{
await sw.WriteLineAsync(message);
await sw.FlushAsync();
await Task.Delay(1000);
}
}
And of course you'd need to update your Main to support this, e.g.,
static async Task Main(string[] args)
{
_ = Task.Run(() => RunServerAsync());
await Task.Delay(1000); // give the server a sec to start
_ = Task.Run(() => RunClientAsync("This is from client1"));
_ = Task.Run(() => RunClientAsync("Client2 is here!!"));
_ = Task.Run(() => RunClientAsync("And I am client3"));
await Task.Delay(int.MaxValue);
}
Of course in a real application you'd never fire and forget your Tasks like that.
Full code for the sake of completeness:
class Program
{
static List<Task> Tasks = new();
static async Task RunClientAsync(TcpClient tcpClient)
{
Console.WriteLine($"Connection from: [{tcpClient.Client.RemoteEndPoint}]");
var reader = new StreamReader(tcpClient.GetStream());
while (true)
{
var line = await reader.ReadLineAsync();
Console.WriteLine($"{tcpClient.Client.RemoteEndPoint}: {line}");
}
}
static async Task RunServerAsync()
{
TcpListener tcpListener = new(IPAddress.Loopback, 9999);
tcpListener.Start();
while (true)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
Tasks.Add(Task.Run(() => RunClientAsync(tcpClient)));
}
}
static async Task Main(string[] args)
{
_ = Task.Run(() => RunServerAsync());
await Task.Delay(1000); // give the server a sec to start
_ = Task.Run(() => RunClientAsync("This is from client1"));
_ = Task.Run(() => RunClientAsync("Client2 is here!!"));
_ = Task.Run(() => RunClientAsync("And I am client3"));
await Task.Delay(int.MaxValue);
}
static async Task RunClientAsync(string message)
{
var tcpClient = new TcpClient("127.0.0.1", 9999);
StreamWriter sw = new(tcpClient.GetStream());
tcpClient.NoDelay = true;
while(true)
{
await sw.WriteLineAsync(message);
await sw.FlushAsync();
await Task.Delay(1000);
}
}
}

How to run async task in background?

I need to forward the port of running service in k8s cluster. So I have use the example provided in github repo to do port forward for specific service. Now I would like to use the example in my test but when I call port forward method then I always get web socket exception but if I written the same code in console application then I don't get any exception it run perfectly fine.
private async Task<bool> ForwardPortOperation(string podName, int[] originalPort, int forwardPort)
{
var pod = GetPodObject(podName);
var webSocket = await _config.GetK8SClient().WebSocketNamespacedPodPortForwardAsync(pod.Metadata.Name, pod.Metadata.NamespaceProperty, originalPort, WebSocketProtocol.V4BinaryWebsocketProtocol);
var demux = new StreamDemuxer(webSocket);
demux.Start();
var stream = demux.GetStream((byte?)0, (byte?)0);
IPAddress ipAddress = IPAddress.Loopback;
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, forwardPort);
Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEndPoint);
listener.Listen(100);
Socket handler = null;
var accept = Task.Run(() =>
{
while (true)
{
handler = listener.Accept();
var bytes = new byte[4096];
while (true)
{
int bytesRec = handler.Receive(bytes);
stream.Write(bytes, 0, bytesRec);
if (bytesRec == 0 || Encoding.Default.GetString(bytes, 0, bytesRec).IndexOf("<EOF>", StringComparison.InvariantCultureIgnoreCase) > -1)
{
break;
}
listener.Close();
}
}
});
var copy = Task.Run(() =>
{
var buff = new byte[4096];
while (true)
{
var read = stream.Read(buff, 0, 4096);
handler.Send(buff, read, 0);
}
});
await accept;
await copy;
return true;
}
Port forward method:
public bool PortForward(string podName, int[] originalPort, int forwardPort)
{
return Task.Run(() => ForwardPortOperation(podName, originalPort, forwardPort)).Result;
}
Nunit test project:
[Test]
public void VerifyPortForward()
{
new Pod(_config).PortForward("web_service_application", new int[3100], 1615);
}
How can I run such async method in background? so that once port forward successful then I can continue to another test.
Exception:
https://gist.github.com/gittadesushil/bc3bf008b7a4fc62c33e97ab1fbf687f
I assume the method PortForward should be rewritten to be asynchronous in the following way:
public async Task<bool> PortForward(string podName, int[] originalPort, int forwardPort)
{
return await Task.Run(() => ForwardPortOperation(podName, originalPort, forwardPort));
}
After that you can write a NUnit test:
[Test]
public async Task VerifyPortForward()
{
var pod = new Pod(_config);
var result = await pod.PortForward("web_service_application", new int[3100], 1615);
// TODO: assert the result
}

Trouble with Task.WhenAny

I am using .NET framework to create a server, which listens on two ports on localhost. It is a simple console application.
It works when I keep connecting to one of the ports, but after I connect to first, another does not respond. First still is alive.
Here is my code:
static void Main(string[] args)
{
IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
List<TcpListener> listeners = new List<TcpListener>()
{
new TcpListener(hostIP, 6060),
new TcpListener(hostIP, 6061)
};
foreach (TcpListener listener in listeners)
{
listener.Start();
}
try
{
while (true)
{
Socket socket = AcceptAnyConnection(listeners).Result;
NetworkStream stream = new NetworkStream(socket);
byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
stream.Write(bytes, 0, bytes.Length);
//stream.Close();
socket.Close();
}
}
finally
{
foreach (TcpListener listener in listeners)
{
listener.Stop();
}
}
}
private static async Task<Socket> AcceptAnyConnection(List<TcpListener> listeners)
{
List<Task<Socket>> tasks = new List<Task<Socket>>();
foreach (TcpListener listener in listeners)
{
tasks.Add(AcceptConnection(listener));
}
Task<Socket> completedTask = await Task.WhenAny(tasks);
return await completedTask;
}
private static async Task<Socket> AcceptConnection(TcpListener listener)
{
Socket socket = await listener.AcceptSocketAsync();
return socket;
}
await Task.WhenAny() blocks if I connect to another port.
I must be doing something wrong, but I am not sure what.
BTW, I did try the same with .NET Core console application, and it works fine.
Thanks
I'd suggest refactoring your code to something like this, and taking it from there. That is, run an infinite loop for each listener. That avoids problems with making sure that you only call AcceptTcpClientAsync once at a time for each client.
(Note there's no code to actually stop the listeners. Also completely untested - please use it only as an indication of the sort of approach to take)
static void Main(string[] args)
{
IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
List<TcpListener> listeners = new List<TcpListener>()
{
new TcpListener(hostIP, 6060),
new TcpListener(hostIP, 6061)
};
var listenerTasks = listeners.Select(x => RunTcpListener(x)).ToArray();
Task.WaitAll(listenerTasks);
}
private static async Task RunTcpListener(TcpListener listener)
{
listener.Start();
try
{
while (true)
{
using (var client = await listener.AcceptTcpClientAsync())
{
var stream = client.GetStream();
byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
stream.Write(bytes, 0, bytes.Length);
client.Close();
}
}
}
finally
{
listener.Stop();
}
}
Here is my code, after I followed a suggestion by canton7 to move the task list outside of loop and move add and remove tasks "as we go". Probably, I will have to look at it to clean it up, but the idea works:
static void Main(string[] args)
{
IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
List<TcpListener> listeners = new List<TcpListener>()
{
new TcpListener(hostIP, 6060),
new TcpListener(hostIP, 6061)
};
ProcessConnections(listeners).Wait();
}
private async static Task SendData(Socket socket)
{
NetworkStream stream = new NetworkStream(socket);
byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
await stream.WriteAsync(bytes, 0, bytes.Length);
socket.Close();
}
private static async Task ProcessConnections(List<TcpListener> listeners)
{
foreach (TcpListener listener in listeners)
{
listener.Start();
}
try
{
List<Task<Socket>> tasks = new List<Task<Socket>>();
foreach (TcpListener listener in listeners)
{
tasks.Add(AcceptConnection(listener));
}
while (true)
{
int i = Task.WaitAny(tasks.ToArray());
Socket socket = await tasks[i];
await SendData(socket);
tasks.RemoveAt(i);
tasks.Insert(i, AcceptConnection(listeners[i]));
}
}
finally
{
foreach (TcpListener listener in listeners)
{
listener.Stop();
}
}
}
private static async Task<Socket> AcceptConnection(TcpListener listener)
{
Socket socket = await listener.AcceptSocketAsync();
return socket;
}

c# Form freez after try to socket connect

i created form appliction, and tried to make chat with socket i have problem when its execute line "acc = sck.Accept();" the form freez and cant press any button whats is in Form, basicly, freez! What im foing wrong? Doing by tutorial
static Socket sck;
static Socket acc;
static int port = 9000;
static IPAddress ip;
static Thread rec;
private void button2_Click(object sender, EventArgs e){
rec = new Thread(recV);
ip = IPAddress.Parse(GetIp());
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sck.Bind(new IPEndPoint(ip, port));
sck.Listen(0);
acc = sck.Accept();
/*while (true)
{
byte[] sdata = Encoding.ASCII.GetBytes("TEST");
acc.Send(sdata, 0, sdata.Length, 0);
}*/
}
Use AcceptAsync like this:
private async void button2_Click(object sender, EventArgs e){
rec = new Thread(recV);
ip = IPAddress.Parse(GetIp());
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sck.Bind(new IPEndPoint(ip, port));
sck.Listen(0);
acc = await sck.AcceptAsync();
//This will block here, let the UI thread continue and then resume when a connection is accepted
/*while (true)
{
byte[] sdata = Encoding.ASCII.GetBytes("TEST");
acc.Send(sdata, 0, sdata.Length, 0);
}*/
}
Note that the method is now async and I'm using await in front of theAcceptAsync` call.
If you want your Send/Receive methods to also free the UI, look into their corresponding Async method.

building a C# asynchronous TCP proxy server

I am attempting to build a simple C# TCP proxy for my business so I can block certain websites from my employees. All is well except I am having trouble seeing what website the user is trying to visit... I can see that the user has connected to my proxy server so I know I am getting connections but the OnRecieve callback isn't even firing. Am I reading from the socket wrong?
Here is my code:
internal class AsyncState
{
public const int BufferSize = 4096;
public byte[] Buffer = new byte[AsyncState.BufferSize];
public Socket Socket;
public StringBuilder Content = new StringBuilder();
}
private void OnLoad(object sender, EventArgs e)
{
IPAddress[] addressCollection = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress ipAddress in addressCollection)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
localEndPoint = new IPEndPoint(ipAddress, 8080);
Console.WriteLine("Local IP address found... " + localEndPoint.ToString());
break;
}
}
isListening = true;
thread = new Thread(new ThreadStart(
delegate()
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(localEndPoint);
serverSocket.Listen(10);
while (isListening)
{
resetEvent.Reset();
Console.WriteLine("Waiting for clients...");
serverSocket.BeginAccept(new AsyncCallback(OnAccept), serverSocket);
resetEvent.WaitOne();
}
}));
thread.Start();
}
}
private void OnAccept(IAsyncResult result)
{
resetEvent.Set();
Socket clientSocket = (result.AsyncState as Socket).EndAccept(result);
Console.WriteLine("Client has connected... " + clientSocket.RemoteEndPoint.ToString());
AsyncState state = new AsyncState();
state.Socket = clientSocket;
state.Socket.BeginReceive(state.Buffer, 0, AsyncState.BufferSize, SocketFlags.None, new AsyncCallback(OnRecieve), state);
}
private void OnRecieve(IAsyncResult result)
{
AsyncState state = result.AsyncState as AsyncState;
int totalRead = state.Socket.EndReceive(result);
if (totalRead > 0)
{
state.Content.Append(Encoding.ASCII.GetString(state.Buffer, 0, totalRead));
state.Socket.BeginReceive(state.Buffer, 0, AsyncState.BufferSize, SocketFlags.None, new AsyncCallback(OnRecieve), state);
}
else
{
if (state.Content.Length > 1)
Console.WriteLine("Message recieved from client... " + state.Content.ToString());
state.Socket.Close();
}
}
Building a well working proxy is no simple task as you will have to understand and handle HTTP etc. in both directions...
I would recommend to either use an existing library for that OR some configurable proxy...
http://www.mentalis.org/soft/projects/proxy/ (with source)
http://sourceforge.net/p/portfusion/home/PortFusion/ (with source)
http://www.wingate.com/
http://www.squid-cache.org/
REMARK:
I don't know in which jurisdiction you are but using such technology without knowledge/consent of employees can in some places be a problem...
Another point: Instead of using such methods I would tell the employee to stop abusing the internet connection of the company 1-3 times and if that doesn't work I would rather fire that person... such employees is not only abusing the internet connection of the company but in worstcase is putting the company at risk (virus/trojan etc.) and also defrauding the company (if he does this in work hours)...

Categories