Currently my android app communicates with a server via tcp socket with synchronous read and write methods
private val addressString : String = "192.168.1.200"
private val port : Int = 33322
private var socketAddress: InetSocketAddress = InetSocketAddress(addressString, port)
private var socket: Socket? = null
fun connect(){
socket = Socket()
socketAddress = InetSocketAddress(addressString, port)
try{
socket!!.connect(socketAddress)
}catch(e : Exception){
socket!!.close()
}
}
fun disconnect(){
try {
socket?.shutdownOutput()
} catch (e: Exception) {}
try {
socket?.shutdownInput()
} catch (e: Exception) {}
try {
socket?.close()
} catch (e: Exception) {}
socket = null
}
fun send(data: ByteArray): Int {
var sentByteCount: Int = -1
try {
socket!!.getOutputStream().write(data)
sentByteCount = data.size
} catch (e: Exception) {
throw e
}
return sentByteCount
}
data class Wrapper<T>(
var value:T
)
fun receive(buffer: Wrapper<ByteArray>): Int {
val size = buffer.value.size
var receivedByteCount: Int = -1
try {
receivedByteCount = socket!!.getInputStream().read(buffer.value)
} catch (e: Exception) {
throw e
}
return receivedByteCount
}
However, the server, written in C#, always communicates with another device via socket but with an asynchronous reading method
public const int BUFFER_SIZE = 4096;
private string addressString = "192.168.1.200"
private int port = 33322
private int timeout = 5000
private byte[] _buffer = new byte[BUFFER_SIZE];
private TcpClient _client;
private SocketError _socketError;
private AsyncCallback _callback = new AsyncCallback(ReceiveCallback);
public void connect()
{
_client = TCpClient()
_client.ConnectAsync(addressString, port).Wait(timeout)
}
public void receive()
{
_client.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, _socketError, _callback, null)
}
private void receiveCallback(IAsyncResult ar)
{
//callback method when it has finished reading asynchronously
}
public void send()
{
_client.GetStream().Write(_buffer, 0, _buffer.Length);
}
public void disconnect()
{
if (_client.Connected)
_client.Close();
}
What I need is to communicate between my app and the device as the Server is already doing.
I tried to find out if even in kotlin there was the possibility of creating an asynchronous connection that would give me the possibility of being able to do an asynchronous reading as it can be done in C # and I found AsynchronousSocketChannel.
From the documentation, however, it has been deduced that the possibility of using this socket is linked to the fact that the server is an AsynchronousServerSocketChannel socket, in my case it is not possible to use it as I can only create the client.
Are there any other possibilities to recreate something similar to the C # code shown above in Kotlin?
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 have an Android application that is currently using a Socket Listener to listen for incoming connections from a C# application. I am able to get the C# application to send a string to the Android application, but the Socket Listener only seems to accept/execute the necessary code to write the information out only once, but continues to accept incoming connection requests from the C# application.
I have searched through Stackoverflow and spent plenty of time on Google, but can't seem to nail down the exact cause for this issue. Below is my code.
Android Code
public class ScoringActivity extends Activity
{
InputStream is;
private String ipAddress = "";
ProgressDialog progress;
private TextView serverStatus;
// DEFAULT IP
public static String SERVERIP = "10.0.2.2";
// DESIGNATE A PORT
public static final int SERVERPORT = 6000;
private Handler handler = new Handler();
private ServerSocket serverSocket;
#Override
protected void onCreate(Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
setContentView(R.layout.scoring);
Intent intent= getIntent();
ipAddress = intent.getStringExtra("ipAddress");
setProgressBarIndeterminateVisibility(false);
Thread fst = new Thread(new ServerThread());
fst.start();
}
public class ServerThread implements Runnable
{
public void run()
{
try
{
if (SERVERIP != null)
{
handler.post(new Runnable()
{
#Override
public void run()
{
Log.d("Listening on IP: ", SERVERIP + " " + SERVERPORT);
}
});
serverSocket = new ServerSocket(SERVERPORT);
while (true)
{
// LISTEN FOR INCOMING CLIENTS
Socket client = serverSocket.accept();
handler.post(new Runnable()
{
#Override
public void run()
{
Log.d("Connected.","Connected.");
}
});
try
{
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String line = null;
while ((line = in.readLine()) != null)
{
Log.d("ServerActivity", line);
handler.post(new Runnable()
{
#Override
public void run()
{
Log.d("In the run method", "in the run method");
}
});
}
in.close();
break;
} catch (Exception e)
{
handler.post(new Runnable()
{
#Override
public void run()
{
Log.d("Oops. Connection interrupted. Please reconnect your phones.","Oops. Connection interrupted. Please reconnect your phones.");
}
});
e.printStackTrace();
}
}
} else
{
handler.post(new Runnable()
{
#Override
public void run()
{
Log.d("Couldn't detect internet connection.","Couldn't detect internet connection.");
}
});
}
} catch (Exception e)
{
handler.post(new Runnable()
{
#Override
public void run()
{
Log.d("Error","Error");
}
});
e.printStackTrace();
}
}
}
}
C# Code
//ProcessSqlFiles();
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
if (!clientSocket.Connected)
clientSocket.Connect(IPAddress.Parse("127.0.0.1"), 8080);
clientSocket.Send(Encoding.UTF8.GetBytes("Test Input"));
clientSocket.Close();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
You're breaking out of the loop:
}
in.close();
break;
Remove the break statement. I think what you may want is continue assuming you want to go back to the start of the infinite loop.
I am working on a c# application (.net 4) that accepts multiple tcp connections from different clients. There is a single tcp listener that accepts socket. Communication b/w nodes in duplex. Data is sent using Networkstream.Write method and read using Networkstream.read method. For each tcp connection a seperate thread is created.
The problem is, a few days ago we noticed that one of the clients stopped reading data (due to a bug) for 20 minutes. As the connection was not broken, there was no (IO) exception at the server. However, we noticed that data at the other clients was also not going. After 20 minutes, that client again started receiving the data and soon other clients also started receiving the data.
I know that Network stream's write method is a blocking method and we are not using any timeouts. So there is a potential that write has blocked (described here). But as I understood it, there has to be a seperate write buffer for each tcp connection or is there something more at play. Can a send blockage at a tcp connection, effect other tcp connections in the same application?
Here is the pseudo code for write operation. For each connection there is a seperate outgoing queue process by a seperate thread.
public class TCPServerListener : baseConnection
{
private readonly int _Port;
private TcpListener _tcpListener;
private Thread _thread;
private List<TcpClientData> _tcpClientDataList = new List<TcpClientData>();
private long _messageDiscardTimeout;
private bool LoopForClientConnection = true;
public TCPServerListener(int port, ThreadPriority threadPriority)
{
try
{
// init property
}
catch (Exception ex)
{
// log
}
}
public void SendMessageToAll(int type)
{
base.EnqueueMessageToSend(type, _tcpClientDataList);
}
public void SendMessageToList(int type, IList<TcpClient> tcpClientList)
{
base.EnqueueMessageToSend(type, tcpClientList);
}
public void SendMessage(int type, TcpClient tcpClient)
{
base.EnqueueMessageToSend(type, tcpClient);
}
private void AcceptClientConnections()
{
while (LoopForClientConnection)
{
try
{
Socket socket = _tcpListener.AcceptSocket();
TcpClientData tcpClientData = new TcpClientData();
tcpClientData.tcpClientThread = new Thread(new ParameterizedThreadStart(StartAsync));
tcpClientData.tcpClientThread.Priority = _threadPriority;
tcpClientData.tcpClientThread.IsBackground = true;
tcpClientData.tcpClientThread.Name = "CD" + tcpClientData.tcpClientThread.ManagedThreadId;
tcpClientData.tcpClient = new TcpClient();
tcpClientData.tcpClient.Client = socket;
_tcpClientDataList.Add(tcpClientData);
tcpClientData.tcpClientThread.Start(tcpClientData.tcpClient);
}
catch (ThreadAbortException ex)
{
//log
}
catch (Exception ex)
{
//log
}
}
}
public override void Start()
{
base.Start();
_tcpListener = new TcpListener(System.Net.IPAddress.Any, _Port);
_thread = new Thread(AcceptClientConnections);
_thread.Priority = _threadPriority;
_thread.IsBackground = true;
_tcpListener.Start();
_thread.Start();
}
public override void Stop()
{
// stop listener and terminate threads
}
}
public class baseConnection
{
private Thread _InCommingThread;
private Thread _OutGoingThread;
protected ThreadPriority _threadPriority;
protected BlockingCollection<MessageReceived> _InComingMessageQueue = new BlockingCollection<MessageReceived>();
protected BlockingCollection<MessageToSend> _OutgoingMessageQueue = new BlockingCollection<MessageToSend>();
public void StartAsync(Object oTcpClient)
{
TcpClient tcpClient = oTcpClient as TcpClient;
if (tcpClient == null)
return;
using (tcpClient)
{
using (NetworkStream stream = tcpClient.GetStream())
{
stream.ReadTimeout = Timeout.Infinite;
stream.WriteTimeout = Timeout.Infinite;
BinaryReader bodyReader = new BinaryReader(stream);
while (tcpClient.Connected)
{
try
{
int messageType = bodyReader.ReadInt32();
// checks to verify messages
// enqueue message in incoming queue
_InComingMessageQueue.Add(new MessageReceived(messageType, tcpClient));
}
catch (EndOfStreamException ex)
{
// log
break;
}
catch (Exception ex)
{
// log
Thread.Sleep(100);
}
}
//RaiseDisconnected(tcpClient);
}
}
}
public virtual void Start()
{
_InCommingThread = new Thread(HandleInCommingMessnge);
_InCommingThread.Priority = _threadPriority;
_InCommingThread.IsBackground = true;
_InCommingThread.Start();
_OutGoingThread = new Thread(HandleOutgoingQueue);
_OutGoingThread.Priority = _threadPriority;
_OutGoingThread.IsBackground = true;
_OutGoingThread.Start();
}
public virtual void Stop()
{
// stop the threads and free up resources
}
protected void EnqueueMessageToSend(int type, List<TcpClientData> tcpClientDataList)
{
tcpClientDataList.ForEach(x => _OutgoingMessageQueue.Add(new MessageToSend(type, x.tcpClient)));
}
protected void EnqueueMessageToSend(int type, IList<TcpClient> tcpClientList)
{
foreach (TcpClient tcpClient in tcpClientList)
{
_OutgoingMessageQueue.Add(new MessageToSend(type, tcpClient));
}
}
protected void EnqueueMessageToSend(int type, TcpClient tcpClient)
{
_OutgoingMessageQueue.Add(new MessageToSend(type, tcpClient));
}
private void HandleOutgoingQueue()
{
while (true)
{
try
{
MessageToSend message = _OutgoingMessageQueue.Take();
if (message.tcpClient.Connected)
{
BinaryWriter writer = new BinaryWriter(message.tcpClient.GetStream());
writer.Write(message.type);
}
}
catch (ThreadAbortException ex)
{
// log
return;
}
catch (Exception ex)
{
//_logger.Error(ex.Message, ex);
}
}
}
private void HandleInCommingMessnge()
{
while (true)
{
try
{
MessageReceived messageReceived = _InComingMessageQueue.Take();
// handle message
}
catch (ThreadAbortException ex)
{
// log
return;
}
catch (Exception ex)
{
// log
//_logger.Error(ex.Message, ex);
}
}
}
public class MessageReceived
{
public MessageReceived(int type, TcpClient tcpClient)
{
this.tcpClient = tcpClient;
this.type = type;
}
public int type;
public TcpClient tcpClient;
}
public class MessageToSend
{
public MessageToSend(int type, TcpClient tcpClient)
{
this.tcpClient = tcpClient;
this.type = type;
}
public int type;
public TcpClient tcpClient;
}
public class TcpClientData
{
public Thread tcpClientThread;
public TcpClient tcpClient;
}
}
You mention that for each connection a separate thread is created, but the code you have shown seems to be able to dequeue a message for any connection.
If this code is running on multiple threads, the program will block as soon as every thread is currently trying to send a message to the blocking connection. Another problem you may face if this loop runs on multiple threads is that messages may not arrive in the correct order for the same connection.
I'm making a game in C# and I want to display the progress (movements and so on) of opponent. So I send events in game via TCP protocol to opponent.
I've already tried my application on localhost and it works but when I try to use my external address in order to communicate over the internet I get the error below in class TcpInformer.Connect():
a connection attempt failed because the connected party did not properly respond after a
period of time, or established connection failed because connected host has failed to
respond (my external IP address):(port)
I thought the problem was that I was behind NAT. But I've already set up portforwarding for port 49731 on IP 10.0.0.1 and nothing changed.
My second guess was Windows firewall but even when I stopped the firewall my app didn't start working.
My code for connecting of the two PCs is:
TcpInformer peer;
TcpHost server;
public void PrepareConnection() // for server (host)
{
playerType = PlayerType.One;
server = new TcpHost(form, this);
server.Start("10.0.0.1", 49731);
}
public void PrepareConnection2() // for client
{
playerType = PlayerType.Two;
peer = new TcpInformer(form, this);
peer.Connect("MY EXTERNAL IP", 49731);
}
// classes TcpHost and TcpInformer
public interface ITcpCommunication
{
#region Operations (3)
void ReadData();
void SendData(byte[] message);
void SendData(byte[] message, int size);
#endregion Operations
}
public class TcpInformer : ITcpCommunication
{
#region Fields (9)
private NetworkStream con_ns;
private TcpClient con_server;
private bool connected;
private Fmain form;
private SecondPlayer player;
private int port;
private string server;
private string stringData;
#endregion Fields
#region Delegates and Events (1)
// Events (1)
public event SimulationEventHandler ReadEvent;
#endregion Delegates and Events
#region Constructors (1)
public TcpInformer(Fmain form, SecondPlayer player)
{
this.form = form;
connected = false;
this.player = player;
}
#endregion Constructors
#region Methods (6)
// Public Methods (5)
///
///
///
/// e.g., server = "127.0.0.1"
/// e.g., port = 9050
public void Connect(string server, int port)
{
this.port = port;
this.server = server;
connected = true;
try
{
con_server = new TcpClient(this.server, this.port);
}
catch (SocketException ex)
{
connected = false;
MessageBox.Show("Unable to connect to server" + ex.Message);
return;
}
con_ns = con_server.GetStream();
}
public void Disconnect()
{
form.Debug("Disconnecting from server...", "Player2Net");
con_ns.Close();
con_server.Close();
}
public void ReadData()
{
if (con_ns != null)
{
if (con_ns.DataAvailable)
{
byte[] data = new byte[1200];
int received = con_ns.Read(data, 0, data.Length);
player.ProcessReceivedData(data, received);
}
}
else
{
form.Debug("Warning: con_ns is not inicialized.","player2");
}
}
public void SendData(byte[] message)
{
con_ns.Write(message, 0, message.Length);
con_ns.Flush();
}
public void SendData(byte[] message, int size)
{
if (con_ns != null)
{
con_ns.Write(message, 0, size);
}
}
// Private Methods (1)
private void Debug(string message)
{
form.Debug("Connected to: " + server + "port: " + port.ToString() + ": " + message, "Player2Net");
}
#endregion Methods
}
public class TcpHost : ITcpCommunication
{
#region Fields (9)
private ASCIIEncoding enc;
private Fmain form;
private TcpListener listener;
private SecondPlayer player;
private int port;
private Socket s;
private string server;
private bool state;
#endregion Fields
#region Delegates and Events (1)
// Events (1)
public event SimulationEventHandler ReadEvent;
#endregion Delegates and Events
#region Constructors (1)
public TcpHost(Fmain form, SecondPlayer player)
{
this.player = player;
this.form = form;
state = false;
enc = new ASCIIEncoding();
}
#endregion Constructors
#region Methods (5)
// Public Methods (5)
public void Close()
{
state = false;
s.Close();
listener.Stop();
}
public void ReadData()
{
if (state == true)
{
if (s.Available > 0) // if there's any data
{
byte[] data = new byte[1200];
int received = s.Receive(data);
player.ProcessReceivedData(data, received);
}
}
}
public void SendData(byte[] message)
{
if (state == true)
{
s.Send(message);
}
}
public void SendData(byte[] message, int size)
{
if (state == true)
{
s.Send(message, size, SocketFlags.None);
}
}
public void Start(string p_ipAddress, int listenPort)
{
//IPAddress ipAddress = IPAddress.Loopback
IPAddress ipAddress = IPAddress.Parse(p_ipAddress);
IPEndPoint ipLocalEndPoint = new IPEndPoint(ipAddress, listenPort);
//listener = new TcpListener(ipAddress, listenPort);
listener = new TcpListener(ipLocalEndPoint);
server = "[provider]";
port = listenPort;
listener.Start();
form.Debug("Server is running", "Player1Net");
form.Debug("Listening on port " + listenPort, "Player1Net");
form.Debug("Waiting for connections...", "Player1Net");
s = listener.AcceptSocket();
form.Debug("Connection accepted from " + s.RemoteEndPoint, "Player1Net");
state = true;
}
#endregion Methods
}
Is there a way how to check what is wrong?
Help is much appreciated!
I found out what was the problem. I was listening on 10.0.0.1 and trying to reach my external IP (second instance of my program) which is impossible on a computer with one connection to the internet.
I also faced the same problem in AWS VPN.
I changed the proxy.company.com (external ip) to 10.0.0.5.
And it works now.