The core element of my project is the connection through local network between two applications ( client and server ) using sockets. I've followed many tutorials and the most stable version is the one I am about to post below.
I've run it on c# console applications and it works fine
I've run it on Windows Forms application and it works fine
So I was ready to implement it on my Xamarin application and for one time ( the first time ) it worked. I've even tested it on my android smartphone ( as client ) and UWP on windows ( as server ). After that first time it never worked again. Neither on my Desktop nor my Laptop. I've literally changed nothing and it stopped working.
At my first touch with sockets and Xamarin I though that it just don't work. But after that one-working time. It must not be that.
TROUBLESHOOTING
I am getting on the client classes ( ClientSocket ) on ClientSocket.Connect -> _socket.BeginConnect = false
I've checked the firewalls, I've uninstalled and disable the
firewalls
I've checked the manifest and even there I tried after the (
must-have permissions ) I tried to enable all permissions.
I will try and upload a dropbox link ( for my files )
Server code :
namespace Control
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
}
private void ServerConnectBtn_Clicked(object sender, EventArgs e)
{
ServerSocket.Bind(9000);
ServerSocket.Listen(500);
ServerSocket.Accept();
msg_lbl.Text = PacketHandler.status;
}
}
}
My server classes :
namespace Control.Server
{
class ServerSocket
{
private static Socket _socket;
private static byte[] _buffer = new byte[1024];
public ServerSocket()
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public static void Bind(int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Any, port));
}
public static void Listen(int backlog)
{
_socket.Listen(500);
}
public static void Accept()
{
_socket.BeginAccept(AcceptedCallback, null);
}
private static void AcceptedCallback(IAsyncResult result)
{
Socket clientSocket = _socket.EndAccept(result);
_buffer = new byte[1024];
clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
Accept();
}
private static void ReceivedCallback(IAsyncResult result)
{
Socket clientSocket = result.AsyncState as Socket;
int bufferSize = clientSocket.EndReceive(result);
byte[] packet = new byte[bufferSize];
Array.Copy(_buffer, packet, packet.Length);
//Handle the packet
PacketHandler.Handle(packet, clientSocket);
_buffer = new byte[1024];
clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
}
}
}
namespace Control.Server
{
public abstract class PacketStructure
{
private byte[] _buffer;
public PacketStructure(ushort length, ushort type)
{
_buffer = new byte[length];
WriteUshort(length, 0);
WriteUshort(type, 2);
}
public PacketStructure(byte[] packet)
{
_buffer = packet;
}
public void WriteUshort(ushort value, int offset)
{
byte[] tempbuffer = new byte[2];
tempbuffer = BitConverter.GetBytes(value);
Buffer.BlockCopy(tempbuffer, 0, _buffer, offset, 2);
}
public short ReadUshort(int offset)
{
return BitConverter.ToInt16(_buffer, offset);
}
public void WriteUint(uint value, int offset)
{
byte[] tempbuffer = new byte[4];
tempbuffer = BitConverter.GetBytes(value);
Buffer.BlockCopy(tempbuffer, 0, _buffer, offset,4);
}
public void WriteString(string value, int offset)
{
byte[] tempbuffer = new byte[value.Length];
tempbuffer = Encoding.UTF8.GetBytes(value);
Buffer.BlockCopy(tempbuffer, 0, _buffer, offset, value.Length);
}
public string ReadString(int offset, int count)
{
return Encoding.UTF8.GetString(_buffer, offset, count);
}
public byte[] Data { get { return _buffer; } }
}
}
namespace Control.Server
{
public static class PacketHandler
{
public static string status;
public static void Handle(byte[] packet, Socket clientSocket)
{
ushort packetLength = BitConverter.ToUInt16(packet, 0);
ushort packetType = BitConverter.ToUInt16(packet, 2);
status = "Received packet! Length: "+ packetLength + " | Type: "+ packetType;
switch (packetType)
{
case 2000:
Message msg = new Message(packet);
Console.WriteLine(msg.Text);
break;
}
}
}
}
namespace Control.Server
{
public class Message : PacketStructure
{
private string _message;
public Message(string message)
: base((ushort)(4 + message.Length), 2000)
{
Text = message;
}
public Message(byte[] packet)
: base(packet)
{
}
public string Text
{
get { return ReadString(4, Data.Length - 4); }
set
{
_message = value;
WriteString(value, 4);
}
}
}
}
Client code:
namespace Remote
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SettingsPage : ContentPage
{
public SettingsPage()
{
InitializeComponent();
}
private void ClientConnectBtn_Clicked(object sender, EventArgs e)
{
ClientSocket.Connect("192.168.1.17",9000);
Status_lbl.Text = "Status : " +ClientSocket.status;
}
private void Send_Clicked(object sender, EventArgs e)
{
string msg = msgEntry.Text;
Message packet = new Message(msg);
ClientSocket.Send(packet.Data);
Status_lbl.Text = "Status : " + ClientSocket.status;
}
}
}
Client Classes
namespace Remote.Client
{
public abstract class PacketStructure
{
private byte[] _buffer;
public PacketStructure(ushort length, ushort type)
{
_buffer = new byte[length];
WriteUshort(length, 0);
WriteUshort(type, 2);
}
public PacketStructure(byte[] packet)
{
_buffer = packet;
}
public void WriteUshort(ushort value, int offset)
{
byte[] tempbuffer = new byte[2];
tempbuffer = BitConverter.GetBytes(value);
Buffer.BlockCopy(tempbuffer, 0, _buffer, offset, 2);
}
public short ReadUshort(int offset)
{
return BitConverter.ToInt16(_buffer, offset);
}
public void WriteUint(uint value, int offset)
{
byte[] tempbuffer = new byte[4];
tempbuffer = BitConverter.GetBytes(value);
Buffer.BlockCopy(tempbuffer, 0, _buffer, offset, 4);
}
public void WriteString(string value, int offset)
{
byte[] tempbuffer = new byte[value.Length];
tempbuffer = Encoding.UTF8.GetBytes(value);
Buffer.BlockCopy(tempbuffer, 0, _buffer, offset, value.Length);
}
public string ReadString(int offset, int count)
{
return Encoding.UTF8.GetString(_buffer, offset, count);
}
public byte[] Data { get { return _buffer; } }
}
}
namespace Remote.Client
{
public class Message : PacketStructure
{
private string _message;
public Message(string message)
:base((ushort)(4 + message.Length), 2000)
{
Text = message;
}
public Message(byte[] packet)
:base(packet)
{
}
public string Text
{
get { return ReadString(4, Data.Length - 4); }
set
{
_message = value;
WriteString(value, 4);
}
}
}
}
namespace Remote.Client
{
class ClientSocket
{
private static Socket _socket;
private static byte[] _buffer;
public static string status;
public ClientSocket()
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public static void Connect(string ipAddress, int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.BeginConnect(new IPEndPoint(IPAddress.Parse(ipAddress), port), ConnectCallback, null);
}
private static void ConnectCallback(IAsyncResult result)
{
if (_socket.Connected)
{
status = "Connected to the server!";
_buffer = new byte[1024];
_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
// throw new Exception("Conencted");
}
else
{
status = "Could not connect";
// throw new Exception("Could not connect");
}
}
private static void ReceiveCallback(IAsyncResult result)
{
int bufLength = _socket.EndReceive(result);
byte[] packet = new byte[bufLength];
Array.Copy(_buffer, packet, packet.Length);
//Handle packet
_buffer = new byte[1024];
_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
}
public static void Send(byte[] data)
{
_socket.Send(data);
}
}
}
I managed to achieve the proper connections by enabling loopback as #NicoZhi-MSFT said.
Have you enabled uwp app's loopback and private network capability?
Please check this document to enable this app loop back
I've only ran the commands for the server's side from this link and it seems to be working just fine. But by have Private Network always enabled.
So if you can make the UWP application run cmd commands every time the server needs to start or by settings an automatic task (following the instructions for the link above) it should be ok.
TIP:
UWP is not very handy on running CMD commands so if needed for anyone can look for UWP fulltrust or/and by setting an external application that runs when needed in the background
Many thanks
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();
}
}
I have Implemented a simple TCP forwarder which is connected to a backend server. This server is set on http://localhost:5000 and this TCP forwarder is listening to http://localhost:5001. Using another machine to generate load using wrk which is a HTTP benchmarking tool to generator load, I have 2 different results. When I send the load directly to the service which is based on asp.net core web api on the kestrel more than 230K requests/second are handled but when I send the load to this TCP forwarder 83Krequest/second can be handled. Here is the code:
using System;
using System.Net;
using System.Net.Sockets;
namespace BrunoGarcia.Net
{
static void Main(string[] args)
{
new TcpForwarderSlim().Start(
new IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1])),
new IPEndPoint(IPAddress.Parse(args[2]), int.Parse(args[3])));
}
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
}
}
What is the problem do you think?! How can I improve the result when I use TCP forwarder?!Or is there a better way to make a tunnel or a forwarder to listen to a port and send the TCP requests to one of 2 or more back end service?!
You do not start listening for a more data till state.DestinationSocket.Send completes. You can start listening for more data as soon as you start processing the Send, the order of multiple BeginSend calls is preserved so if you switched to that it would allow you to start processing the next request before the previous one finished.
Important note! You will now need to create a new buffer (or use a pool of buffers) for each new BeginReceive request. Below is untested code but hopefully is close enough to get you on the right path.
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
//Start an asyncronous send.
var sendAr = state.DestinationSocket.BeginSend(state.Buffer, 0, bytesRead, SocketFlags.None,null,null);
//Get or create a new buffer for the state object.
var oldBuffer = state.ReplaceBuffer();
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
//Wait for the send to finish.
state.DestinationSocket.EndSend(sendAr);
//Return byte[] to the pool.
state.AddBufferToPool(oldBuffer);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
private readonly ConcurrentBag<byte[]> _bufferPool = new ConcurrentBag<byte[]>();
private readonly int _bufferSize;
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
_bufferSize = Math.Min(SourceSocket.ReceiveBufferSize, DestinationSocket.SendBufferSize);
Buffer = new byte[_bufferSize];
}
/// <summary>
/// Replaces the buffer in the state object.
/// </summary>
/// <returns>The previous buffer.</returns>
public byte[] ReplaceBuffer()
{
byte[] newBuffer;
if (!_bufferPool.TryTake(out newBuffer))
{
newBuffer = new byte[_bufferSize];
}
var oldBuffer = Buffer;
Buffer = newBuffer;
return oldBuffer;
}
public void AddBufferToPool(byte[] buffer)
{
_bufferPool.Add(buffer);
}
}
}
I made super simple game on plain C# using Windows Forms to learn a little bit about making programs "talk" over internet. Server and client are on same program and user just chooses which one he is by clicking a button.
It currently works just as I want as long as server and client are both ran on same PC using ip 127.0.0.1 to connect. If I try to use internal or external ip (port is opened) it doesn't work anymore ("Before Accept" triggers, but "Before Receive" doesn't).
I got base for my code from Darryl Braaten who answered someone elses question:
create c# asynchronous socket client in multithreaded environment
AsyncServer.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace blockbattle
{
public class AsyncServer
{
private const int port = 11000;
public int clientID = -1;
public string serverPos = "";
public string clientPos = "";
public string newBullets = "";
public bool bulletsSent = false;
public string receivedBullets = "";
public void StartServer()
{
Thread thread = new Thread(Run) { IsBackground = true };
thread.Start();
}
private void Run()
{
Debug.WriteLine("Running");
TcpListener tcpListener = new TcpListener(IPAddress.Loopback, port);
tcpListener.Start();
while (true)
{
Debug.WriteLine("Before Accept");
ServerState state = new ServerState { WorkSocket = tcpListener.AcceptSocket() };
Debug.WriteLine("Before Receive");
Receive(state);
}
}
private void Receive(ServerState state)
{
state.WorkSocket.BeginReceive(state.Buffer, 0, ServerState.BufferSize, 0, ReceiveCallBack, state);
}
private void ReceiveCallBack(IAsyncResult ar)
{
ServerState state = (ServerState)ar.AsyncState;
try
{
int byteReceived = state.WorkSocket.EndReceive(ar);
if (byteReceived > 0)
{
string receivedString = Encoding.UTF8.GetString(state.Buffer, 0, byteReceived);
string[] receivedData = receivedString.Split('+');
clientID = int.Parse(receivedData[0]);
clientPos = receivedData[1];
if (receivedData[2].Length > 0)
receivedBullets = receivedData[2];
byte[] bytesToSend = Encoding.UTF8.GetBytes(string.Format("{0}+{1}", serverPos, newBullets));
if (newBullets.Length > 0)
{
newBullets = "";
bulletsSent = true;
}
Array.Copy(bytesToSend, state.Buffer, bytesToSend.Length);
state.WorkSocket.Send(state.Buffer, 0, bytesToSend.Length, SocketFlags.None);
Array.Clear(state.Buffer, 0, state.Buffer.Length);
Receive(state);
}
}
catch (Exception e)
{
Debug.Print(e.ToString());
}
}
private class ServerState
{
public const int BufferSize = 1024;
public readonly byte[] Buffer = new byte[BufferSize];
public Socket WorkSocket = null;
}
}
}
AsyncClient.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace blockbattle
{
public partial class AsyncClient
{
private const int port = 11000;
private readonly int _clientId;
private readonly Random _random;
public string clientPos = "";
public string serverPos = "";
public string newBullets = "";
public bool bulletsSent = false;
public string receivedBullets = "";
public string lastBullet = "";
public AsyncClient(int clientId)
{
_clientId = clientId;
_random = new Random(clientId);
}
public void StartClient(IPAddress serverIP)
{
try
{
Socket workSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ClientState state = new ClientState { WorkSocket = workSocket };
workSocket.BeginConnect(new IPEndPoint(serverIP, port), ConnectCallBack, state);
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
}
private void ConnectCallBack(IAsyncResult ar)
{
ClientState state = (ClientState)ar.AsyncState;
state.WorkSocket.EndConnect(ar);
Send(state);
}
private void Receive(ClientState clientState)
{
clientState.WorkSocket.BeginReceive(clientState.Buffer, 0, ClientState.BufferSize, 0, ReceiveCallBack, clientState);
}
private void ReceiveCallBack(IAsyncResult ar)
{
ClientState state = (ClientState)ar.AsyncState;
Socket client = state.WorkSocket;
int byteReceived = client.EndReceive(ar);
if (byteReceived > 0)
{
string receivedString = Encoding.UTF8.GetString(state.Buffer, 0, byteReceived);
string[] receivedData = receivedString.Split('+');
serverPos = receivedData[0];
if (receivedData[1].Length > 0)
receivedBullets = receivedData[1];
Array.Clear(state.Buffer, 0, state.Buffer.Length);
Thread.Sleep(33);
Send(state);
}
}
private void Send(ClientState clientState)
{
byte[] buffer = Encoding.UTF8.GetBytes(string.Format("{0}+{1}+{2}", _clientId, clientPos, newBullets));
if (newBullets.Length > 0)
{
newBullets = "";
bulletsSent = true;
}
try
{
clientState.WorkSocket.BeginSend(buffer, 0, buffer.Length, 0, BeginSendCallBack, clientState);
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
}
private void BeginSendCallBack(IAsyncResult ar)
{
ClientState state = (ClientState)ar.AsyncState;
int byteSent = state.WorkSocket.EndSend(ar);
Receive(state);
}
}
public class ClientState
{
// Client socket.
public Socket WorkSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] Buffer = new byte[BufferSize];
public int Count = 0;
}
}
Change IPAddress.Loopback to IPAddress.Any in your TcpListener.
EDIT
I figured out what the problem was, but now I faced an other. I want to remote control 1 PC from an other PC, that part works, but you have to port forward the port. Is it possible to connect like TeamViewer? So I bypass the firewall and don't have to port forward? If yes, how? If someone could help me out it would be pretty awesome :)
Bram
ORIGINAL POST
I made a code so you can control a computer from an other computer. The only problem is that it doesn't work for computers outside of your own network (Maybe I connected the wrong IP? I tried the IPv4 of the guy and the IP from WhatIsMyIP ). How can I make this work for outside my network?
Here is the server class(I removed a lot of code, because it didn't make sense)
public partial class Form1 : Form
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
private const int MOUSE_LEFTDOWN = 0x02;
private const int MOUSE_LEFTUP = 0x04;
private const int MOUSE_RIGTDOWN = 0x08;
private const int MOUSE_RIGHTUP = 0x10;
private TcpListener listener;
private Socket mainSocket;
private int port;
private Stream s;
private Thread eventWatcher;
private int imageDelay;
string connString;
string connToString;
string db;
MySqlCommand command;
MySqlCommand command2;
MySqlCommand command3;
MySqlCommand command4;
MySqlConnection connection = null;
public Form1()
{
InitializeComponent();
port = 1338;
imageDelay = 1000;
}
public Form1(int p)
{
port = p;
imageDelay = 1000;
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show(getIP());
startConnection();
command = connection.CreateCommand();
command2 = connection.CreateCommand();
command3 = connection.CreateCommand();
command4 = connection.CreateCommand();
MessageBox.Show(connString);
MessageBox.Show(db);
this.Hide();
port = 1338;
while (true)
{
try
{
startListening();
}
catch (Exception)
{
}
}
}
public void startListening()
{
try
{
listener = new TcpListener(port);
listener.Start();
mainSocket = listener.AcceptSocket();
s = new NetworkStream(mainSocket);
eventWatcher = new Thread(new ThreadStart(waitForKeys));
eventWatcher.Start();
while (true)
{
Bitmap screeny = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
Graphics theShot = Graphics.FromImage(screeny);
theShot.ScaleTransform(.25F, .25F);
theShot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
BinaryFormatter bFormat = new BinaryFormatter();
bFormat.Serialize(s, screeny);
Thread.Sleep(imageDelay);
theShot.Dispose();
screeny.Dispose();
}
}
catch (Exception)
{
if (mainSocket.IsBound)
mainSocket.Close();
if (listener != null)
listener.Stop();
}
}
private static void trigger(IAsyncResult i) { }
And the client code(Again, removed a lot):
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 256;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
public partial class Form2 : Form
{
private static string response;
private Stream stream;
private StreamWriter eventSender;
private Thread theThread;
private TcpClient client;
private Form1 mForm;
private int resolutionX;
private int resolutionY;
//private int sendDelay = 250;
//private Thread delayThread;
public bool sendKeysAndMouse = false;
private static ManualResetEvent sendDone = new ManualResetEvent(false);
private static ManualResetEvent receiveDone = new ManualResetEvent(false);
public Form2()
{
InitializeComponent();
}
public Form2(TcpClient s, Form1 callingForm)
{
client = s;
mForm = callingForm;
InitializeComponent();
theThread = new Thread(new ThreadStart(startRead));
theThread.Start();
}
private void Form2_FormClosed(object sender, FormClosedEventArgs e)
{
if (theThread.IsAlive)
theThread.Abort();
mForm.form2Closed();
}
private void startRead()
{
try
{
stream = client.GetStream();
eventSender = new StreamWriter(stream);
while (true)
{
BinaryFormatter bFormat = new BinaryFormatter();
Bitmap inImage = bFormat.Deserialize(stream) as Bitmap;
resolutionX = inImage.Width;
resolutionY = inImage.Height;
theImage.Image = (Image)inImage;
}
}
catch (Exception) { }
/*try
{
Image theDesktop;
stream = client.GetStream();
theDesktop.Save(stream,new ImageFormat(
while (true)
{
while (stream.Read(buffer, 0, 1024) > 0)
theDesktop.Read(buffer, 0, 1024);
if(theDesktop != null) {
theDesktop.
theImage.Image = temp;
}
}
catch (Exception gay) { }*/
}
private void Form2_ResizeEnd(object sender, EventArgs e)
{
}
private void Form2_Load(object sender, EventArgs e)
{
}
private void Form2_MouseMove(object sender, MouseEventArgs e)
{
}
private void theImage_MouseClick(object sender, MouseEventArgs e)
{
if (!sendKeysAndMouse)
return;
eventSender.Write("LCLICK\n");
eventSender.Flush();
}
private void theImage_MouseDown(object sender, MouseEventArgs e)
{
if (!sendKeysAndMouse)
return;
eventSender.Write("LDOWN\n");
eventSender.Flush();
}
private void theImage_MouseUp(object sender, MouseEventArgs e)
{
if (!sendKeysAndMouse)
return;
eventSender.Write("LUP\n");
eventSender.Flush();
}
private void Receive(Socket client)
{
try
{
StateObject state = new StateObject();
state.workSocket = client;
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private void Send(Socket client, String data)
{
byte[] byteData = Encoding.ASCII.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client);
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
if (state.sb.Length > 1)
{
response = state.sb.ToString();
}
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private void SendCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
I hope someone can help me, or explain me what I'm doing wrong and how I can make it work.
The way Team Viewer does this is there is a 3rd computer in the interaction. Team Viewer has a public server on the internet that both you and the person you are connecting to talk to and all it does is act as a bridge forwarding incoming messages from one end to the other end. Now both ends have outbound connections and don't need port forwarding.
To do the same with your system you would need to set up, host (pay for), and maintain a public server on the internet that all users of your program would talk to and act as the bridge for communications.
The steps are like this:
Computer1 opens a outbound connection to Server1 and keeps it open, they can now talk back and forth without port porwarding.
Computer2 opens a outbound connection to Server1 and keeps it open, they can now talk back and forth without port forwarding.
Computer1 sends a message to Server1 that says "Forward the following to Computer2"
Server1 uses the open connection from step 2 that Computer2 initiated to forward the message.
When Computer2 wants to reply it sends a message to Server1 that says "Forward the following to Computer1"
Server1 uses the open connection from step 1 that Computer1 initiated to forward the message.