I have to connect, send and receive socket messages to a server. I have to implement the client application which is based on Windows Forms. (Target Framework: 4.5.1)
In five minute intervals I have to send a message to the server to keep the server online. (This is not a keep alive ping, this is a specified command to the server.)
I'd like to implement this task to keep the GUI live (not blocked), until the socket communication working in the background.
Everything is working fine, until one or more of the server are not available. The ConnectTaskAsync method wait (approximately 15 seconds) and send a "TIME OUT EXCEPTION". But this time to exception raised is a long time.
How is possible to call connect socket with 5 seconds timeout without blocking UI thread?
On Form load event, I create a timer
private void Form1_Load(object sender, EventArgs e)
{
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 1000;
t.Tick += this.displayTimers;
t.Start();
System.Windows.Forms.Timer tKeepAlive = new System.Windows.Forms.Timer();
tKeepAlive.Interval = 10000;
tKeepAlive.Tick += clsKeepAlive.keepAlivePanelTimer;
tKeepAlive.Start();
}
keepAlivePanelTimer is caller by timer
public async static void keepAlivePanelTimer(object sender, EventArgs e)
{
List<clsPanelItem> panelList = (List<clsPanelItem>)((clsVmsGroup)actGroup).lstVmsItems;
IEnumerator _idePanel = panelList.GetEnumerator();
while (_idePanel.MoveNext())
{
clsPanelItem actPanel = (clsPanelItem)_idePanel.Current;
try
{
//CancellationTokenSource cts = new CancellationTokenSource();
await keepAlivePanel(actPanel);
}
catch (Exception _ex)
{
//Timeout Exceptions are handle here
System.Diagnostics.Debug.Print("Err: " + _ex.Message + " - Panel: " + actPanel.ipAddress.ToString());
}
finally
{
}
}
}
keepAlivePanel Async task called from timer
private static async Task keepAlivePanel(clsPanelItem actPanel, CancellationToken ct)
{
IPAddress ipAddress = actPanel.ipAddress;
IPEndPoint remoteEP = new IPEndPoint(ipAddress, app.vmsPanelCommPort);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.SendTimeout = 1000; //Nothing was happened
client.ReceiveTimeout = 1000; //Nothing was happened
await async45.ConnectTaskAsync(client, remoteEP, ipAddress, app.vmsPanelCommPort, ct);
}
ConnectTaskAsync connect to server
public static class async45
{
public static Task ConnectTaskAsync(Socket socket, EndPoint endpoint, IPAddress ip, int port, CancellationToken ct)
{
Task t = Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, endpoint, null);
return t;
}
}
Another methods to use send and receive bytes
public static Task SendTaskAsync(this Socket socket, byte[] buffer)
{
return Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket),
socket.EndSend);
}
public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
{
return Task.Factory.FromAsync<int>(
socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndReceive);
}
I tried different things with less success.
// var timeoutTask = Task.Delay(TimeSpan.FromSeconds(2));
// //var ConnectTask = Task.Factory.FromAsync(client.BeginConnect, client.EndConnect, remoteEP, null, TaskCreationOptions.LongRunning);
// var ConnectTask = Task.Factory.FromAsync(client.BeginConnect, client.EndConnect, remoteEP, null);
// var completedTask = await Task.WhenAny(timeoutTask, ConnectTask)
// .ConfigureAwait(false);
Related
I'm trying to communicate with server, using TCP CLient. But to communicate, there are connection rules:
Whenever the reader is going to start a communication, mark 1 must be placed on its output.
This signal is felt by the server as a connection request indication and is only valid after 1 second of stabilization.
Once the connection request is accepted, the server starts sending the ENQ signal chars. (ENQ = 05 Hexadecimal)
I think I need to use some "sleep" function for 1 second and sending 1 as mark. So I implemented the following example I had:
public void Initialize(string ip, int port)
{
try
{
tcpClient = new TcpClient(ip, port);
if (tcpClient.Connected)
Console.WriteLine("Connected to: {0}:{1}", ip, port);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Initialize(ip, port);
}
}
public void BeginRead()
{
var buffer = new byte[4096];
NetworkStream ns = tcpClient.GetStream();
ns.ReadTimeout = 1000;
ns.BeginRead(buffer, 0, 9, EndRead, buffer);
}
class Program
{
static void Main(string[] args)
{
var client = new Client();
client.Initialize("192.168.0.250", 2180);
client.BeginRead();
Console.ReadLine();
}
}
When I run this code, show the message: "Connected to 192.168.0.250". Now following the rules, I need to receive the ENQ (05 Hexa) signal from the server. How do I receive this signal?
They way you are using the Begin... and End... pattern is a little off. I am going to give you a basic example on how you can get going on this.
What you are doing here:
tcpClient = new TcpClient(ip, port);
if (tcpClient.Connected)
Console.WriteLine("Connected to: {0}:{1}", ip, port);
is wrong. You never call a Connect... method. So, nothing is connected.
Here are typical CRUD operations on a TcpClient object:
public sealed class TcpClientTest
{
private readonly TcpClient _tcpClient = new TcpClient();
public void Connect(string host, int port, Action endConnect)
{
_tcpClient.BeginConnect(host, port, asyncResult =>
{
_tcpClient.EndConnect(asyncResult);
endConnect?.Invoke();
}, null);
}
public void Read(byte[] buffer, Action<byte[], int> endRead)
{
_tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, asyncResult =>
{
var bytesRead = _tcpClient.GetStream().EndRead(asyncResult);
endRead?.Invoke(buffer, bytesRead);
}, null);
}
public void Write(byte[] buffer, Action endWrite)
{
_tcpClient.GetStream().BeginWrite(buffer, 0, buffer.Length, asyncRsult =>
{
_tcpClient.GetStream().EndWrite(asyncRsult);
endWrite?.Invoke();
}, null);
}
public void Disconnect()
{
_tcpClient.Close();
}
}
You can call this code by doing this:
Console.WriteLine("Connecting...");
var tcp = new TcpClientTest();
tcp.Connect("www.example.com", 80, () =>
{
Console.WriteLine("We are connected... Sending request...");
var str = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
tcp.Write(Encoding.UTF8.GetBytes(str), () =>
{
Console.WriteLine("Data sent. Waiting for data to come back...");
var bytes = new byte[2048];
tcp.Read(bytes, (buffer, read) =>
{
var data = Encoding.UTF8.GetString(buffer, 0, read);
Console.WriteLine($"Data Read: {data}");
Console.WriteLine("Closing connection");
tcp.Disconnect();
Console.WriteLine("Done.");
});
});
})
Things become somewhat easier if you don't use the Begin... and End... methods and instead use the ...Async methods, such as ConnectAsync, WriteAsync, ReadAsync, etc. methods.
This requires a knowledge of the async/await pattern, which at first is complicated, but overtime becomes one of the most useful patterns you may ever use.
I tested another example, which I found here on Stack. And connected successfully using TcpClient and Socket. The example is this:
namespace test3
{
class Program
{
private static void _TestSOCode()
{
using (var tcp = new TcpClient())
{
var c = tcp.BeginConnect(IPAddress.Parse("192.168.0.250"), 2180, null, null);
var success = c.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (!success)
{
Console.WriteLine("Before cleanup");
tcp.Close();
tcp.EndConnect(c);
Console.WriteLine("After cleanup");
throw new Exception("Failed to connect.");
}
}
}
private static void _TestWithTcpClient()
{
TcpClient client = new TcpClient();
object o = new object();
Console.WriteLine("connecting TcpClient...");
client.BeginConnect("192.168.0.250", 2180, asyncResult =>
{
Console.WriteLine("connect completed");
try
{
client.EndConnect(asyncResult);
Console.WriteLine("client connected");
}
catch (NullReferenceException)
{
Console.WriteLine("client closed before connected: NullReferenceException");
}
catch (ObjectDisposedException)
{
Console.WriteLine("client closed before connected: ObjectDisposedException");
}
lock (o) Monitor.Pulse(o);
}, null);
Thread.Sleep(1000);
Stopwatch sw = Stopwatch.StartNew();
client.Close();
lock (o) Monitor.Wait(o);
Console.WriteLine("close took {0:0.00} seconds", sw.Elapsed.TotalSeconds);
Console.WriteLine();
}
private static void _TestWithSocket()
{
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
object o = new object();
Console.WriteLine("connecting Socket...");
socket.BeginConnect("192.168.0.250", 2180, asyncResult =>
{
Console.WriteLine("connect completed");
try
{
socket.EndConnect(asyncResult);
Console.WriteLine("socket connected");
}
catch (ObjectDisposedException)
{
Console.WriteLine("socket closed before connected");
}
lock (o) Monitor.Pulse(o);
}, null);
Thread.Sleep(1000);
Stopwatch sw = Stopwatch.StartNew();
socket.Close();
lock (o) Monitor.Wait(o);
Console.WriteLine("close took {0:0.00} seconds", sw.Elapsed.TotalSeconds);
Console.WriteLine();
}
static void Main(string[] args)
{
_TestWithSocket();
_TestWithTcpClient();
try
{
_TestSOCode();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e);
}
}
}
}
Now I need to figure out how to read the server response. The rule #3:
Once the connection request is accepted, the server starts sending
the ENQ signal chars. (ENQ = 05 Hexadecimal)
I have a basic TcpListener server that accepts TcpClient requests and sticks them in separate threads.
The server thread that accepts requests uses await _server.AcceptTcpClientAsync() so it returns cpu resources and doesn't use any cpu until it gets a client request.
My client loop calls await client.GetStream().ReadAsync but that's wrapped in a if (client.Connected && client.Available > 0) so if there is no data it just returns null, so nothing is being awaited.
The options I know of are to add a Thread.Sleep which is bad practice and will slow down my apps, use a EventWaitHandle but I'd have to know set that somehow or to remove the if (client.Connected && client.Available > 0) check, which would sit indefinitely waiting for data, so if the server went down the program would effectively be dead.
EDIT: Added client code
public class TcpServer
{
private TcpListener _server;
private bool _isRunning;
private object _lock = new object();
// Listener thread
public async void StartServer()
{
_server = new TcpListener("127.0.0.1", 1234);
_server.Start();
_isRunning = true;
Thread.CurrentThread.Name = "TcpServer";
while (_isRunning)
{
// Block until client connection
TcpClient newClient = await _server.AcceptTcpClientAsync();
// Create a thread to handle client connection
new Thread(new ParameterizedThreadStart(HandleClientConnection)).Start(newClient);
}
}
// Client Thread
public async void HandleClientConnection(object obj)
{
// Create worker object
var client = (TcpClient)obj;
{
ClientId = Guid.NewGuid()
};
// Read client data
while (client.Connected)
{
var msg = await client.ReadMessageAsync();
}
// Disconnected
}
}
// Read data from stream when available. Used on server and client
public static async Task<Message> ReadMessageAsync(this TcpClient client)
{
if (client.Connected && client.Available > 0)
{
int totalBytesRead = 0;
var data = new byte[client.Available];
while (totalBytesRead < client.Available)
{
totalBytesRead += await client.GetStream().ReadAsync(data, 0, client.Available);
}
return BinarySerializer.DeserializeMessage(data) as Message;
}
return null;
}
// Client Code
public async Task Start()
{
ConnectToServer();
while (_isRunning)
{
// Reconnect to server if needed.
if (!_tcpClient.Connected)
{
ConnectToServer();
}
// Wait for messages
var message = await _tcpClient.ReadMessageAsync();
HandleMessage(message);
}
}
I was struggling with a problem for a long time. I was using unity and I want to ping my server and router. I was using UnityPing class at the beginning and it works fine for the most of devices, but when I testing on Google Pixel(Android 7.1) it always return -1. So I tried to use System.Net.NetworkInformation to ping my server. here is my code:
private void PingToServer()
{
AutoResetEvent waiter = new AutoResetEvent(false);
System.Net.NetworkInformation.Ping pingSender = new System.Net.NetworkInformation.Ping();
pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 10000;
PingOptions options = new PingOptions(64, true);
IPAddress address = IPAddress.Parse("119.81.194.57");
pingSender.SendAsync(address, timeout, buffer, options, waiter);
}
private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
if (e.Cancelled)
{
Console.WriteLine("Ping canceled.");
((AutoResetEvent)e.UserState).Set();
}
if (e.Error != null)
{
Console.WriteLine("Ping failed:");
Console.WriteLine(e.Error.ToString());
((AutoResetEvent)e.UserState).Set();
}
PingReply reply = e.Reply;
int pingTime = (int)reply.RoundtripTime;
UnityEngine.Debug.Log(reply.RoundtripTime);
((AutoResetEvent)e.UserState).Set();
}
And It always return a RoundripTime which seems like a number make sense, but when I try to ping another ip address which is not available to reach or even turn off the internet it always return a RoundripTime with reply.status equals to IPStatus.Success. Now I'm confused, did I really ping to my remote server or not?
I did chekced some other similar questions, and it doesn't solved the problem. Some answers suggest to use SendPingAsync instead of SendAsync, but that is not possible for unity.
I have button.click event where i have this code ... and it works fine
AutoResetEvent waiter = new AutoResetEvent(false);
Ping pingSender = new Ping();
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 100;
PingOptions options = new PingOptions(64, true);
IPAddress address = IPAddress.Parse("YourTestIP");
PingReply reply = pingSender.Send(address, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
MessageBox.Show(reply.Address.ToString());
}
This is my current setup (using UDP):
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packet
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
}
I was wondering what would happen if I immediately call socket.BeginReceive after calling EndReceive and then process the packet to obtain a continous packet flow like this:
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packets
}
If a packet is received as soon as I call BeginReceive, would this conflict with the current packet processing somehow?
Also if this would not conflict would changing to TCP make this disfunctional?
Looks like you are creating some sort of recursive handler there. I am unsure how that will work, probably not in a good way. I usually go for a separate reader thread that listens to incoming data and passes it on to an event. This has served me well in the past. I have not looked into using async for this though.
Here is some example code on how to use a separate thread to handle incoming UDP data. It is not complete but should give you an idea of how to set it up.
private Thread _udpReadThread;
private volatile bool _terminateThread;
public event DataEventHandler OnDataReceived;
public delegate void DataEventHandler(object sender, DataEventArgs e);
private void CreateUdpReadThread()
{
_udpReadThread = new Thread(UdpReadThread) { Name = "UDP Read thread" };
_udpReadThread.Start(new IPEndPoint(IPAddress.Any, 1234));
}
private void UdpReadThread(object endPoint)
{
var myEndPoint = (EndPoint)endPoint;
var udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// Important to specify a timeout value, otherwise the socket ReceiveFrom()
// will block indefinitely if no packets are received and the thread will never terminate
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100);
udpListener.Bind(myEndPoint);
try
{
while (!_terminateThread)
{
try
{
var buffer = new byte[1024];
var size = udpListener.ReceiveFrom(buffer, ref myEndPoint);
Array.Resize(ref buffer, size);
// Let any consumer(s) handle the data via an event
FireOnDataReceived(((IPEndPoint)(myEndPoint)).Address, buffer);
}
catch (SocketException socketException)
{
// Handle socket errors
}
}
}
finally
{
// Close Socket
udpListener.Shutdown(SocketShutdown.Both);
udpListener.Close();
}
}
public class DataEventArgs : EventArgs
{
public byte[] Data { get; private set; }
public IPAddress IpAddress { get; private set; }
public DataEventArgs(IPAddress ipaddress, byte[] data)
{
IpAddress = ipaddress;
Data = data;
}
}
I've previously used BeginAccept() and BeginRead(), but with Visual Studio 2012 I want to make use of the new asynchronous (async, await) features in my socket server program.
How can I complete the AcceptAsync and ReceiveAsync functions?
using System.Net;
using System.Net.Sockets;
namespace OfficialServer.Core.Server
{
public abstract class CoreServer
{
private const int ListenLength = 500;
private const int ReceiveTimeOut = 30000;
private const int SendTimeOut = 30000;
private readonly Socket _socket;
protected CoreServer(int port, string ip = "0.0.0.0")
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
_socket.Listen(ListenLength);
_socket.ReceiveTimeout = ReceiveTimeOut;
_socket.SendTimeout = SendTimeOut;
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
}
public void Start()
{
}
}
}
...because you're so determined, I put together a very simple example of how to write an echo server to get you on your way. Anything received gets echoed back to the client. The server will stay running for 60s. Try telnetting to it on localhost port 6666. Take time to understand exactly what's going on here.
void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
TcpListener listener = new TcpListener(IPAddress.Any, 6666);
try
{
listener.Start();
//just fire and forget. We break from the "forgotten" async loops
//in AcceptClientsAsync using a CancellationToken from `cts`
AcceptClientsAsync(listener, cts.Token);
Thread.Sleep(60000); //block here to hold open the server
}
finally
{
cts.Cancel();
listener.Stop();
}
}
async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct)
{
var clientCounter = 0;
while (!ct.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync()
.ConfigureAwait(false);
clientCounter++;
//once again, just fire and forget, and use the CancellationToken
//to signal to the "forgotten" async invocation.
EchoAsync(client, clientCounter, ct);
}
}
async Task EchoAsync(TcpClient client,
int clientIndex,
CancellationToken ct)
{
Console.WriteLine("New client ({0}) connected", clientIndex);
using (client)
{
var buf = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
//under some circumstances, it's not possible to detect
//a client disconnecting if there's no data being sent
//so it's a good idea to give them a timeout to ensure that
//we clean them up.
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, ct);
var completedTask = await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
if (completedTask == timeoutTask)
{
var msg = Encoding.ASCII.GetBytes("Client timed out");
await stream.WriteAsync(msg, 0, msg.Length);
break;
}
//now we know that the amountTask is complete so
//we can ask for its Result without blocking
var amountRead = amountReadTask.Result;
if (amountRead == 0) break; //end of stream.
await stream.WriteAsync(buf, 0, amountRead, ct)
.ConfigureAwait(false);
}
}
Console.WriteLine("Client ({0}) disconnected", clientIndex);
}
You can use TaskFactory.FromAsync to wrap up Begin / End pairs into async-ready operations.
Stephen Toub has an awaitable Socket on his blog which wraps the more efficient *Async endpoints. I recommend combining this with TPL Dataflow to create a fully async-compatible Socket component.