I keep getting this error:
"The IAsyncResult object was not returned from the corresponding asynchonous method on this class. Parameter name : aysncResult. Line 105.
This happens when I attempt to connect to a local server; It errors and won't connect.
Here's my client code:
public class Client
{
public delegate void OnConnectEventHandler(Client sender, bool connected);
public event OnConnectEventHandler OnConnect;
public delegate void OnSendEventHandler(Client sender, int sent);
public event OnSendEventHandler OnSend;
public delegate void OnDisconnectEventHandler(Client sender);
public event OnDisconnectEventHandler OnDisconnect;
Socket socket;
public bool Connected
{
get
{
if (socket != null)
{
return socket.Connected;
}
return false;
}
}
public Client()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public void SendData()
{
}
public void Connect(string IP, int port)
{
if (socket == null)
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
socket.BeginConnect(IP, port, new AsyncCallback(sendCallback), null);
}
private void connectCallback(IAsyncResult ar)
{
//try
//{
socket.EndConnect(ar);
if (OnConnect != null)
{
OnConnect(this, Connected);
}
//}
//catch
//{
//}
}
public void Send(byte[] data, int index, int length)
{
socket.BeginSend(BitConverter.GetBytes(length), 0, 4, SocketFlags.None, new AsyncCallback(sendCallback), null);
socket.BeginSend(data, index, length, SocketFlags.None, new AsyncCallback(sendCallback), null);
}
private void sendCallback(IAsyncResult ar)
{
try
{
int sent = socket.EndSend(ar); ( errrors here )
if (OnSend != null)
{
OnSend(this, sent);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString());
return;
}
}
public void Disconnect()
{
try
{
if (socket.Connected)
{
socket.Close();
socket = null;
if (OnDisconnect != null)
{
OnDisconnect(this);
}
}
}
catch
{
}
}
you should not have two pending BeginSend operations.
Send the size and then the buffer when it completes:
public void Send(byte[] data, int index, int length)
{
//add data as state
socket.BeginSend(BitConverter.GetBytes(length), 0, 4, SocketFlags.None, sendCallback, data);
}
private void sendCallback(IAsyncResult ar)
{
try
{
int sent = socket.EndSend(ar); ( errrors here )
// check if data was attached.
if (ar.AsyncState != null)
{
byte[] buffer = (byte[])ar.AsyncState;
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, sendCallback, null);
return;
}
if (OnSend != null)
{
OnSend(this, sent);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString());
return;
}
}
You can also use the BeginSend overload which takes a list of buffers.
Related
I have a bunch of async methods that I want to expose via C# sockets. The general pattern in the MSDN documentaions Has the following form:
public static void StartListening()
{
...
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
...
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
...
}
public static void AcceptCallback(IAsyncResult ar)
{
...
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
...
StateObject state = (StateObject) ar.AsyncState;
...
CalculateResult(state);
...
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
...
}
So writing all this in a nice and clean form without repetition of code has been a challenge. I am thinking along these lines but have not been able to connect the dots:
public static void StartListeningMaster()
{
string ipAddress = "localhost";
IPHostEntry ipHost = Dns.GetHostEntry(ipAddress);
IPAddress address = ipHost.AddressList[0];
StartListening(50000, address, AcceptCallback1);
StartListening(50001, address, AcceptCallback2);
StartListening(50002, address, AcceptCallback3);
...
}
public static void StartListening(int port, IPAddress ipAddress,
Action<IAsyncResult> acceptCallback) {...}
public static void AcceptCallback1(IAsyncResult ar)
{
...
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize,
0, new AsyncCallback1(ReadCallback1), state);
}
...
This works fine up to this point. But to refactor it properly I would like to have one AcceptCallback method that takes as its parameter a generic ReadCallback that takes as its parameter a CalculateResult method. This way I would not have any repetition of code. However, if I modify my AcceptCallback method to take any more parameters than IAsyncResult (for example something like:
public static void StartListening(int port, IPAddress ipAddress, Action<IAsyncResult, Action<IAsyncResult>> acceptCallback) {...}
public static void AcceptCallback(IAsyncResult ar, Action<IAsyncResult> readCallback) {}
I break the AsyncCallback delegate contract.
public delegate void AsyncCallback(IAsyncResult ar);
Then I looked into extending the existing interfaces to allow the functionality. I looked into extending
public interface IAsyncResult
But that does not seem to be the right approach either. So, how do I write this code so I do not copy and paste pretty much the same code all over the place?
So the way I tackle this is by moving the basic components in to their own abstract objects. Then build upon those objects. For example, the server only needs to accept/track connections. So I would make a server object that looks something like this:
namespace MultiServerExample.Base
{
public interface IAsyncServerBase
{
void StartListening();
bool IsListening { get; }
void StopListening();
void WriteDataToAllClients(byte[] data);
}
public abstract class AsyncServerBase<TClientBase> : IAsyncServerBase
where TClientBase : IAsyncClientBase, new()
{
// implement a TcpListener to gain access to Active property
private sealed class ActiveTcpListener : TcpListener
{
public ActiveTcpListener(IPAddress localaddr, int port)
: base(localaddr, port) { }
public bool IsActive => Active;
}
// our listener object
private ActiveTcpListener Listener { get; }
// our clients
private ConcurrentDictionary<string, TClientBase> Clients { get; }
// construct with a port
public AsyncServerBase(int port)
{
Clients = new ConcurrentDictionary<string, TClientBase>();
Listener = new ActiveTcpListener(IPAddress.Any, port);
}
// virtual methods for client action
public virtual void OnClientConnected(TClientBase client) { }
public virtual void OnClientDisconnected(TClientBase client, Exception ex) { }
// start the server
public void StartListening()
{
if(!IsListening)
{
Listener.Start();
Listener.BeginAcceptTcpClient(OnAcceptedTcpClient, this);
}
}
// check if the server is running
public bool IsListening =>
Listener.IsActive;
// stop the server
public void StopListening()
{
if (IsListening)
{
Listener.Stop();
Parallel.ForEach(Clients, x => x.Value.DetachClient(null));
Clients.Clear();
}
}
// async callback for when a client wants to connect
private static void OnAcceptedTcpClient(IAsyncResult res)
{
var me = (AsyncServerBase<TClientBase>)res.AsyncState;
if (!me.IsListening) { return; }
try
{
TcpClient client = null;
try
{
client = me.Listener.EndAcceptTcpClient(res);
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Warning: unable to accept client:\n{ex}");
}
if(client != null)
{
// create a new client
var t = new TClientBase();
// set up error callbacks
t.Error += me.OnClientBaseError;
// notify client we have attached
t.AttachClient(client);
// track the client
me.Clients[t.Id] = t;
// notify we have a new connection
me.OnClientConnected(t);
}
}
finally
{
// if we are still listening, wait for another connection
if(me.IsListening)
{
me.Listener.BeginAcceptSocket(OnAcceptedTcpClient, me);
}
}
}
// Event callback from a client that an error has occurred
private void OnClientBaseError(object sender, AsyncClientBaseErrorEventArgs e)
{
var client = (TClientBase)sender;
client.Error -= OnClientBaseError;
OnClientDisconnected(client, e.Exception);
client.DetachClient(e.Exception);
Clients.TryRemove(client.Id, out _);
}
// utility method to write data to all clients connected
public void WriteDataToAllClients(byte[] data)
{
Parallel.ForEach(Clients, x => x.Value.WriteData(data));
}
}
}
At this point all the basics of running a server have been accounted for. Now for the client that runs on the server:
namespace MultiServerExample.Base
{
public interface IAsyncClientBase
{
event EventHandler<AsyncClientBaseErrorEventArgs> Error;
void AttachClient(TcpClient client);
void WriteData(byte[] data);
void DetachClient(Exception ex);
string Id { get; }
}
public abstract class AsyncClientBase : IAsyncClientBase
{
protected virtual int ReceiveBufferSize { get; } = 1024;
private TcpClient Client { get; set; }
private byte[] ReceiveBuffer { get; set; }
public event EventHandler<AsyncClientBaseErrorEventArgs> Error;
public string Id { get; }
public AsyncClientBase()
{
Id = Guid.NewGuid().ToString();
}
public void AttachClient(TcpClient client)
{
if(ReceiveBuffer != null) { throw new InvalidOperationException(); }
ReceiveBuffer = new byte[ReceiveBufferSize];
Client = client;
try
{
Client.GetStream().
BeginRead(ReceiveBuffer, 0, ReceiveBufferSize, OnDataReceived, this);
OnAttachedToServer();
}
catch (Exception ex)
{
Error?.Invoke(this,
new AsyncClientBaseErrorEventArgs(ex, "BeginRead"));
}
}
public void DetachClient(Exception ex)
{
try
{
Client.Close();
OnDetachedFromServer(ex);
}
catch { /* intentionally swallow */ }
Client = null;
ReceiveBuffer = null;
}
public virtual void OnDataReceived(byte[] buffer) { }
public virtual void OnAttachedToServer() { }
public virtual void OnDetachedFromServer(Exception ex) { }
public void WriteData(byte[] data)
{
try
{
Client.GetStream().BeginWrite(data, 0, data.Length, OnDataWrote, this);
}
catch(Exception ex)
{
Error?.Invoke(this, new AsyncClientBaseErrorEventArgs(ex, "BeginWrite"));
}
}
private static void OnDataReceived(IAsyncResult iar)
{
var me = (AsyncClientBase)iar.AsyncState;
if(me.Client == null) { return; }
try
{
var bytesRead = me.Client.GetStream().EndRead(iar);
var buf = new byte[bytesRead];
Array.Copy(me.ReceiveBuffer, buf, bytesRead);
me.OnDataReceived(buf);
}
catch (Exception ex)
{
me.Error?.Invoke(me, new AsyncClientBaseErrorEventArgs(ex, "EndRead"));
}
}
private static void OnDataWrote(IAsyncResult iar)
{
var me = (AsyncClientBase)iar.AsyncState;
try
{
me.Client.GetStream().EndWrite(iar);
}
catch(Exception ex)
{
me.Error?.Invoke(me,
new AsyncClientBaseErrorEventArgs(ex, "EndWrite"));
}
}
}
}
Now all your base code is written. You don't need to change this in any way. You simply implement your own client and server to respond accordingly. For example, here is a basic server implementation:
public class MyServer : AsyncServerBase<MyClient>
{
public MyServer(int port) : base(port)
{
}
public override void OnClientConnected(MyClient client)
{
Console.WriteLine($"* MyClient connected with Id: {client.Id}");
base.OnClientConnected(client);
}
public override void OnClientDisconnected(MyClient client, Exception ex)
{
Console.WriteLine($"***** MyClient disconnected with Id: {client.Id} ({ex.Message})");
base.OnClientDisconnected(client, ex);
}
}
And here is a client that the server above uses for communication:
public class MyClient : AsyncClientBase
{
public override void OnAttachedToServer()
{
base.OnAttachedToServer();
Console.WriteLine($"{Id}: {GetType().Name} attached. Waiting for data...");
}
public override void OnDataReceived(byte[] buffer)
{
base.OnDataReceived(buffer);
Console.WriteLine($"{Id}: {GetType().Name} recieved {buffer.Length} bytes. Writing 5 bytes back.");
WriteData(new byte[] { 1, 2, 3, 4, 5 });
}
public override void OnDetachedFromServer(Exception ex)
{
base.OnDetachedFromServer(ex);
Console.WriteLine($"{Id}: {GetType().Name} detached.");
}
}
And to drive the point home, here is another client that simply would plug in to the same server implementation, but gives it different characteristics:
public class MyOtherClient : AsyncClientBase
{
public override void OnAttachedToServer()
{
base.OnAttachedToServer();
Console.WriteLine($"{Id}: {GetType().Name} attached. Writing 4 bytes back.");
WriteData(new byte[] { 1, 2, 3, 4 });
}
public override void OnDataReceived(byte[] buffer)
{
base.OnDataReceived(buffer);
Console.WriteLine($"{Id}: {GetType().Name} recieved {buffer.Length} bytes.");
}
public override void OnDetachedFromServer(Exception ex)
{
base.OnDetachedFromServer(ex);
Console.WriteLine($"{Id}: {GetType().Name} detached.");
}
}
As far as using this, here is a small test program that puts it through a stress-test:
class Program
{
static void Main(string[] args)
{
var servers = new IAsyncServerBase[]
{
new MyServer(50000),
new MyServer(50001),
new MyOtherServer(50002)
};
foreach (var s in servers)
{
s.StartListening();
}
RunTestUsingMyServer("1", 89, 50000);
RunTestUsingMyServer("2", 127, 50001);
RunTestUsingMyOtherServer("3", 88, 50002);
Console.Write("Press any key to exit... ");
Console.ReadKey(true);
foreach (var s in servers)
{
s.WriteDataToAllClients(new byte[] { 1, 2, 3, 4, 5 });
s.StopListening();
}
}
private static void RunTestUsingMyServer(string name, int clientCount, int port)
{
Parallel.For(0, clientCount, x =>
{
using (var t = new TcpClient())
{
t.Connect(IPAddress.Loopback, port);
t.GetStream().Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);
t.GetStream().Read(new byte[512], 0, 512);
t.Close();
}
Console.WriteLine($"FINISHED PASS {name} #{x}");
});
}
private static void RunTestUsingMyOtherServer(string name, int clientCount, int port)
{
Parallel.For(0, clientCount, x =>
{
using (var t = new TcpClient())
{
t.Connect(IPAddress.Loopback, port);
t.GetStream().Read(new byte[512], 0, 512);
t.GetStream().Write(new byte[] { 1, 2, 3, 4, 5, 6 }, 0, 6);
t.Close();
}
Console.WriteLine($"FINISHED PASS {name} #{x}");
});
}
}
If interested, here is the full source code you can check out. Hopefully this gets you to where you want to be as it pertains to reusing code.
I don't know if this can help. You can define a state object with all the information related to every port:
public class StateObject
{
public string Name;
public Socket Listener;
public IPEndPoint LocalEndPoint;
//...
public StateObject(Socket listener, IPEndPoint endPoint, string name)
{
Listener = listener;
LocalEndPoint = endPoint;
Name = name;
}
}
Then, you can use it as you need:
public static void StartListeningMaster()
{
string ipAddress = "localhost";
IPHostEntry ipHost = Dns.GetHostEntry(ipAddress);
IPAddress address = ipHost.AddressList[0];
StartListening(50000, address, "Main port");
StartListening(50001, address, "Service port");
StartListening(50002, address, "Extra");
//...
}
public static void StartListening(int port, IPAddress ipAddress, string name = "")
{
//...
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
//...
StateObject state = new StateObject(listener, localEndPoint);
listener.BeginAccept(AcceptCallback, state);
//...
}
public static void AcceptCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
//...
handler.BeginReceive(client.buffer, 0, StateObject.BufferSize,
0, new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
// Always have the info related to every socket
Socket listener = state.Listener;
string address = state.LocalEndPoint.Address.ToString();
int port = state.LocalEndPoint.Port;
string name = state.Name;
//...
StateObject state = (StateObject)ar.AsyncState;
//...
CalculateResult(state);
//...
handler.BeginReceive(client.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
//...
}
The CalculateResult(state) method will have all the necessary info to do whatever. This way, you only have one StartListening(), one AcceptCallback() and one ReadCallback() for all the ports to manage.
I'm trying to create a WPF application that once sent a message via TCP to a third-party application, listen for the answer and continue listening to incoming communication (This third-party application sends feedback to the most recently connected or authenticated controller) until another message has to be sent and so on.
It has to be async because my application must run continuously being able to perform other actions.
Basically I've adapted this (https://learn.microsoft.com/it-it/dotnet/framework/network-programming/asynchronous-client-socket-example) example to WPF using a backgoundWorker and never closing the socket.
The problem seems to be that
client.BeginReceive(state.buffer, 0, state.buffer.Length, 0,
new asyncCallback(ReceiveCallback), state);
Won't loopback if the last bytes read is less than the buffer size.
my code is:
using System;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows;
namespace socket_WPF
{
/// <summary>
/// Logica di interazione per MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly BackgroundWorker BWI;
public AsyncSocketReceiver ASR;
public string risposta = "";
public string rispostaOLD;
public bool newMessage = false;
public string Extmessage;
public int Mcounter = 0;
public MainWindow()
{
InitializeComponent();
BWI = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
BWI.DoWork += new DoWorkEventHandler(BWI_DoWork);
BWI.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BWI_RunWorkerCompleted);
BWI.ProgressChanged += new ProgressChangedEventHandler(BWI_ProgressChangedEventHandler);
}
private void BWI_DoWork(object sender, DoWorkEventArgs e)
{
int i = -1;
BackgroundWorker myBW = sender as BackgroundWorker;
ASR = new AsyncSocketReceiver("192.168.1.106", 3040);
ASR.Connect();
ASR.Send(string.Format("[{0}] ping \r", Mcounter));
ASR.sendDone.WaitOne();
newMessage = false;
while (AsyncSocketReceiver.client.Connected)
{
if (newMessage)
{
var toSend = string.Format("[{0}] " + Extmessage + "\r", Mcounter++);
ASR.Send(toSend);
ASR.sendDone.WaitOne();
newMessage = false;
}
ASR.Receive();
ASR.receiveDone.WaitOne();
risposta = ASR.response;
myBW.ReportProgress(i++);
}
}
private void BWI_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BWI.RunWorkerAsync();
}
private void BWI_ProgressChangedEventHandler(object sender, ProgressChangedEventArgs e)
{
CueAnswer.Document.Blocks.Clear();
CueAnswer.AppendText(risposta);
}
private void AsyncSocket_Click(object sender, RoutedEventArgs e) => BWI.RunWorkerAsync();
private void AskC_Click(object sender, RoutedEventArgs e)
{
ASR.receiveDone.Set();
Extmessage = MessageToSend.Text;
newMessage = true;
}
}
public class AsyncSocketReceiver
{
private static IPEndPoint remoteEP;
public static Socket client;
public ManualResetEvent connectDone = new ManualResetEvent(false);
public ManualResetEvent sendDone = new ManualResetEvent(false);
public ManualResetEvent receiveDone = new ManualResetEvent(false);
public String response;
public String query;
public AsyncSocketReceiver(String myIp, int myPort)
{
remoteEP = new IPEndPoint(IPAddress.Parse(myIp), myPort);
client = new Socket(IPAddress.Parse(myIp).AddressFamily,
SocketType.Stream, ProtocolType.Tcp)
{
Blocking = false
};
}
public void Connect()
{
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
client.EndConnect(ar);
connectDone.Set();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void Send(String data)
{
query = data;
response = "";
byte[] byteData = Encoding.ASCII.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private void SendCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
int bytesSent = client.EndSend(ar);
sendDone.Set();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void Receive()
{
try
{
StateObject state = new StateObject
{
workSocket = client
};
receiveDone.Reset();
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
client.BeginReceive(state.buffer, 0, state.buffer.Length, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
response = state.sb.ToString();
receiveDone.Set();
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 256;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
}
Disclaimer: My C# isn't even close to as good as my C++
I am trying to learn how to do async sockets in C# in order to write a test app for a component of mine. My former attempts using TcpClient ended in failure and you can read the outstanding questions on that here:
TcpClient.NetworkStream Async operations - Canceling / Disconnect
Detect errors with NetworkStream.WriteAsync
Since, I could not get that working, I tried using Socket.BeginX and Socket.EndX instead. I got much further along. My problem now is that in the listing below, when it comes time to disconnect, which in turn calls shutdown and close on the socket, async operations are still outstanding and they will throw an object disposed exception or an object is set to null exception.
I found a similar post about that here:
After disposing async socket (.Net) callbacks still get called
However, I do not accept that answer, because if you are using exceptions for intended behavior, then 1) They are not exceptional 2) You cannot tell if the exception was thrown for your intended case or if it was thrown because you actually used a disposed object or a null reference in your async method, other than the socket.
In C++ with async socket code, I'd keep track of the number of outstanding async operations with Interlocked and when it came time to disconnect, I'd call shutdown, then wait for the interlocked to hit 0, and then close and destroy any members I needed to.
How would I go about waiting in my Disconnect method for all outstanding Async operations to complete in C# in the following listing?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using System.Net.Sockets;
using System.Net;
namespace IntegrationTests
{
public class Client2
{
class ReceiveContext
{
public Socket m_socket;
public const int m_bufferSize = 1024;
public byte[] m_buffer = new byte[m_bufferSize];
}
private static readonly ILog log = LogManager.GetLogger("root");
static private ulong m_lastId = 1;
private ulong m_id;
private string m_host;
private uint m_port;
private uint m_timeoutMilliseconds;
private string m_clientId;
private Socket m_socket;
private uint m_numOutstandingAsyncOps;
public Client2(string host, uint port, string clientId, uint timeoutMilliseconds)
{
m_id = m_lastId++;
m_host = host;
m_port = port;
m_clientId = clientId;
m_timeoutMilliseconds = timeoutMilliseconds;
m_socket = null;
m_numOutstandingAsyncOps = 0;
}
~Client2()
{
Disconnect();
}
public void Connect()
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(m_host);
IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray();
IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], (int)m_port);
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_socket.ReceiveTimeout = (int)m_timeoutMilliseconds;
m_socket.SendTimeout = (int)m_timeoutMilliseconds;
try
{
m_socket.Connect(endpoint);
log.Info(string.Format("Connected to: {0}", m_socket.RemoteEndPoint.ToString()));
// Issue the next async receive
ReceiveContext context = new ReceiveContext();
context.m_socket = m_socket;
m_socket.BeginReceive(context.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context);
}
catch (Exception e)
{
// Error
log.Error(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}"
, m_id, e.ToString()));
}
}
public void Disconnect()
{
if (m_socket != null)
{
m_socket.Shutdown(SocketShutdown.Both);
// TODO - <--- Error here in the callbacks where they try to use the socket and it is disposed
// We need to wait here until all outstanding async operations complete
// Should we use Interlocked to keep track of them and wait on it somehow?
m_socket.Close();
m_socket = null;
}
}
public void Login()
{
string loginRequest = string.Format("loginstuff{0})", m_clientId);
var data = Encoding.ASCII.GetBytes(loginRequest);
m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket);
}
public void MakeRequest(string thingy)
{
string message = string.Format("requeststuff{0}", thingy);
var data = Encoding.ASCII.GetBytes(message);
m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket);
}
void OnReceive(IAsyncResult asyncResult)
{
ReceiveContext context = (ReceiveContext)asyncResult.AsyncState;
string data = null;
try
{
int bytesReceived = context.m_socket.EndReceive(asyncResult);
data = Encoding.ASCII.GetString(context.m_buffer, 0, bytesReceived);
ReceiveContext newContext = new ReceiveContext();
newContext.m_socket = context.m_socket;
m_socket.BeginReceive(newContext.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext);
}
catch(SocketException e)
{
if(e.SocketErrorCode == SocketError.ConnectionAborted) // Check if we disconnected on our end
{
return;
}
}
catch (Exception e)
{
// Error
log.Error(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}"
, m_id, e.ToString()));
}
}
void OnSend(IAsyncResult asyncResult)
{
Socket socket = (Socket)asyncResult.AsyncState;
try
{
int bytesSent = socket.EndSend(asyncResult);
}
catch(Exception e)
{
log.Error(string.Format("Client #{0} Exception caught OnSend. Exception: {1}"
, m_id, e.ToString()));
}
}
}
}
Main:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using log4net.Config;
namespace IntegrationTests
{
class Program
{
private static readonly ILog log = LogManager.GetLogger("root");
static void Main(string[] args)
{
try
{
XmlConfigurator.Configure();
log.Info("Starting Component Integration Tests...");
Client2 client = new Client2("127.0.0.1", 24001, "MyClientId", 60000);
client.Connect();
client.Login();
client.MakeRequest("StuffAndPuff");
System.Threading.Thread.Sleep(60000); // Sim work until user shutsdown
client.Disconnect();
}
catch (Exception e)
{
log.Error(string.Format("Caught an exception in main. Exception: {0}"
, e.ToString()));
}
}
}
}
EDIT:
Here is my additional attempt using the answer proposed by Evk, to the best of my ability. It works fine as far as I can tell.
Problem with this is, that I feel like I basically made everything that was asynchronous into a synchronous call, because of the requirements to lock around anything that would change the counter or the state of the socket. Again, I am novice at C# compared to my C++, so please do point out if I completely missed the mark interpreting his answer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IntegrationTests
{
public class Client
{
class ReceiveContext
{
public const int _bufferSize = 1024;
public byte[] _buffer = new byte[_bufferSize]; // Contains bytes from one receive
public StringBuilder _stringBuilder = new StringBuilder(); // Contains bytes for multiple receives in order to build message up to delim
}
private static readonly ILog _log = LogManager.GetLogger("root");
static private ulong _lastId = 1;
private ulong _id;
protected string _host;
protected int _port;
protected int _timeoutMilliseconds;
protected string _sessionId;
protected Socket _socket;
protected object _lockNumOutstandingAsyncOps;
protected int _numOutstandingAsyncOps;
private bool _disposed = false;
public Client(string host, int port, string sessionId, int timeoutMilliseconds)
{
_id = _lastId++;
_host = host;
_port = port;
_sessionId = sessionId;
_timeoutMilliseconds = timeoutMilliseconds;
_socket = null;
_numOutstandingAsyncOps = 0;
_lockNumOutstandingAsyncOps = new object();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(_disposed)
{
return;
}
if (disposing)
{
_socket.Close();
}
_disposed = true;
}
public void Connect()
{
lock (_lockNumOutstandingAsyncOps)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(_host);
IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray();
IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], _port);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.ReceiveTimeout = _timeoutMilliseconds;
_socket.SendTimeout = _timeoutMilliseconds;
try
{
_socket.Connect(endpoint);
}
catch (Exception e)
{
// Error
Debug.WriteLine(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}"
, _id, e.ToString()));
return;
}
Debug.WriteLine(string.Format("Client #{0} connected to: {1}", _id, _socket.RemoteEndPoint.ToString()));
// Issue the first async receive
ReceiveContext context = new ReceiveContext();
++_numOutstandingAsyncOps;
_socket.BeginReceive(context._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context);
}
}
public void Disconnect()
{
if (_socket != null)
{
// We need to wait here until all outstanding async operations complete
// In order to avoid getting 'Object was disposed' exceptions in those async ops that use the socket
lock(_lockNumOutstandingAsyncOps)
{
Debug.WriteLine(string.Format("Client #{0} Disconnecting...", _id));
_socket.Shutdown(SocketShutdown.Both);
while (_numOutstandingAsyncOps > 0)
{
Monitor.Wait(_lockNumOutstandingAsyncOps);
}
_socket.Close();
_socket = null;
}
}
}
public void Login()
{
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
string loginRequest = string.Format("loginstuff{0}", _clientId);
var data = Encoding.ASCII.GetBytes(loginRequest);
Debug.WriteLine(string.Format("Client #{0} Sending Login Request: {1}"
, _id, loginRequest));
++_numOutstandingAsyncOps;
_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket);
}
else
{
Debug.WriteLine(string.Format("Client #{0} Login was called, but Socket is null or no longer connected."
, _id));
}
}
}
public void MakeRequest(string thingy)
{
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
string message = string.Format("requeststuff{0}", thingy);
var data = Encoding.ASCII.GetBytes(message);
Debug.WriteLine(string.Format("Client #{0} Sending Request: {1}"
, _id, message));
++_numOutstandingAsyncOps;
_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket);
}
else
{
Debug.WriteLine(string.Format("Client #{0} MakeRequest was called, but Socket is null or no longer connected."
, _id));
}
}
}
protected void OnReceive(IAsyncResult asyncResult)
{
lock (_lockNumOutstandingAsyncOps)
{
ReceiveContext context = (ReceiveContext)asyncResult.AsyncState;
string data = null;
try
{
int bytesReceived = _socket.EndReceive(asyncResult);
data = Encoding.ASCII.GetString(context._buffer, 0, bytesReceived);
// If the remote host shuts down the Socket connection with the Shutdown method, and all available data has been received,
// the EndReceive method will complete immediately and return zero bytes
if (bytesReceived > 0)
{
StringBuilder stringBuilder = context._stringBuilder.Append(data);
int index = -1;
do
{
index = stringBuilder.ToString().IndexOf("#");
if (index != -1)
{
string message = stringBuilder.ToString().Substring(0, index + 1);
stringBuilder.Remove(0, index + 1);
Debug.WriteLine(string.Format("Client #{0} Received Data: {1}"
, _id, message));
}
} while (index != -1);
}
}
catch (SocketException e)
{
// Check if we disconnected on our end
if (e.SocketErrorCode == SocketError.ConnectionAborted)
{
// Ignore
}
else
{
// Error
Debug.WriteLine(string.Format("Client #{0} SocketException caught OnReceive. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
}
catch (Exception e)
{
// Error
Debug.WriteLine(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
finally
{
--_numOutstandingAsyncOps;
Monitor.Pulse(_lockNumOutstandingAsyncOps);
}
}
// Issue the next async receive
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
++_numOutstandingAsyncOps;
ReceiveContext newContext = new ReceiveContext();
_socket.BeginReceive(newContext._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext);
}
}
}
protected void OnSend(IAsyncResult asyncResult)
{
lock (_lockNumOutstandingAsyncOps)
{
try
{
int bytesSent = _socket.EndSend(asyncResult);
}
catch (Exception e)
{
Debug.WriteLine(string.Format("Client #{0} Exception caught OnSend. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
finally
{
--_numOutstandingAsyncOps;
Monitor.Pulse(_lockNumOutstandingAsyncOps);
}
}
}
}
}
You can use Monitor.Wait and Monitor.Pulse for that:
static int _outstandingOperations;
static readonly object _lock = new object();
static void Main() {
for (int i = 0; i < 100; i++) {
var tmp = i;
Task.Run(() =>
{
lock (_lock) {
_outstandingOperations++;
}
// some work
Thread.Sleep(new Random(tmp).Next(0, 5000));
lock (_lock) {
_outstandingOperations--;
// notify condition might have changed
Monitor.Pulse(_lock);
}
});
}
lock (_lock) {
// condition check
while (_outstandingOperations > 0)
// will wait here until pulsed, lock will be released during wait
Monitor.Wait(_lock);
}
}
I'm trying to connect my Android phone with a Server. On the Server runs a program written in C#. I've tryed to make it with the following code but it doesn't work.
This is the Android code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Socket socket;
int port = 52301;
String ip = "79.16.115.30";
Button b = (Button) findViewById(R.id.myButton);
assert b != null;
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
byte[] b = ip.getBytes();
/*SocketAddress socketAddress = new InetSocketAddress(ip, port);
socket.connect(socketAddress);*/
socket = new Socket(ip, port);
Toast.makeText(v.getContext(), "CONNESSO", Toast.LENGTH_SHORT).show();
} catch (UnknownHostException e) {
Toast.makeText(v.getContext(), "CHI MINCHIA รจ COSTUI", Toast.LENGTH_SHORT).show();
}catch(IOException e) {
Toast.makeText(v.getContext(), "NO I/O", Toast.LENGTH_SHORT).show();
}
catch (Exception e)
{
String cause = "Message: " + e.getMessage();
Toast.makeText(v.getContext(), cause, Toast.LENGTH_SHORT).show();
}
}
});
}
I've tryed also to use the code commented but it doesn't work;
This is the program that runs on the Server:
static void Main(string[] args)
{
string ip = Get_external_ip();
int port = 52301;
int max = 10;
IPAddress ipAddress = IPAddress.Parse("192.168.1.56");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port); //setto i parametri del socket
Socket sockServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //creo il socket
Socket client;
Console.WriteLine(ip);
try
{
sockServer.Bind(localEndPoint);
sockServer.Listen(max);
while (true)
{
client = sockServer.Accept();
Console.WriteLine(client.RemoteEndPoint.ToString());
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
private static string Get_external_ip()
{
try
{
WebClient client = new WebClient();
return client.DownloadString("http://icanhazip.com/").TrimEnd('\r', '\n');
}
catch (Exception e)
{
return e.Message;
}
}
First of all, use background Thread for network.
I will give you my working snippet. Try to apply it for your needs.
public class YourClass {
//Server socket variables
private ServerSocket serverSocket;
private DataOutputStream dataOutputStream;
private DataInputStream dataInputStream;
private Socket client;
....
private class SocketServerThread extends Thread {
private int port;
private SocketServerThread(int port) {
this.port = port;
}
#Override
public void run() {
dataInputStream = null;
dataOutputStream = null;
try {
serverSocket = new ServerSocket(port);
isSockedOpened = true;
client = serverSocket.accept(); //wait for client
isClientConnected = true;
Log.d(TAG, "Client connected: " + client.getInetAddress().getHostAddress());
dataInputStream = new DataInputStream(
client.getInputStream());
dataOutputStream = new DataOutputStream(
client.getOutputStream());
String messageFromClient;
while (isClientConnected) {
messageFromClient = dataInputStream.readLine();
handleIncomingMessage(messageFromClient);
}
}
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage());
}
}
}
To open connection:
public void connect(int port) {
this.port = port;
Thread socketServerThread = new Thread(new SocketServerThread(port));
socketServerThread.start();
}
That's how you check if client connected;
public boolean isConnected() {
return serverSocket.isBound() || client.isConnected();
}
To disconnect:
public void disconnect(boolean needsGathering) {
if (isSockedOpened) {
try {
if (serverSocket != null) {
serverSocket.close();
}
if (dataOutputStream != null) {
dataOutputStream.close();
}
if (dataInputStream != null) {
dataInputStream.close();
}
if (client != null) {
client.close();
}
} catch (IOException e) {
Log.e(TAG, "Couldn't get I/O for the connection");
Log.e(TAG, e.getMessage());
}
} else {
Log.d(TAG, "Socket was not opened");
}
}
}
I have in MainPage:
private void AcceptCallBack(IAsyncResult ar)
{
try
{
((Service1)ar.AsyncState).EndAccept(ar);
}
catch
{
}
}
And in service1:
private void ReceiveCallBack(IAsyncResult ar)
{
try
{
if (buffer != null)
{...
string str = System.Text.UTF8Encoding.UTF8.GetString(buffer);
strReceive = str;
}
worker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new
AsyncCallback(ReceiveCallBack), worker);
}
catch (SocketException)
{
}
}
[OperationContract]
public void Accept(string Ip,int Port)
{
try
{
bind and listen....
}
catch
{
}
}
private void AcceptCallback(IAsyncResult ar)
{
try
{
...
worker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new
AsyncCallback(ReceiveCallBack), worker);
}
catch (SocketException)
{
}
}
[OperationContract]
public string GetReceiveData()
{
return strReceive;
}
In MainPage, I would like to get message of GetReceiveData but I don't know how.