I am trying to learn more about sockets and threading in c#. I have come across a lot of good resources online to help get me started. The program I made so far, is a simple "man-in-the-middle" application. It's designed as the following: client <--> [application] <--> server
Given the following code, how can I prevent this thread from running at 100% CPU? How can I have the thread wait and block for data and not exit when the client / server is idle?
while (true)
{
lock (ClientState)
{
checkConnectionStatus(client, server);
}
if (clientStream.CanRead && clientStream.DataAvailable)
{
Byte[] bytes = new Byte[(client.ReceiveBufferSize)];
IAsyncResult result = clientStream.BeginRead(bytes, 0, client.ReceiveBufferSize, null, null);
int size = clientStream.EndRead(result);
sendData(bytes, serverStream, size);
}
if (serverStream.CanRead && serverStream.DataAvailable)
{
Byte[] bytes = new byte[(server.ReceiveBufferSize)];
IAsyncResult result = serverStream.BeginRead(bytes, 0, server.ReceiveBufferSize, null, null);
int size = serverStream.EndRead(result);
sendData(bytes, clientStream, size);
}
}
EDIT: Decided to post the entire "Connection.cs" class for anyone interested. I'm a beginner programming, so I know there are some bad coding practices here. Basically this whole class is ran in another thread and should die when the connection (to either the client socket or server socket) drops.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
namespace TCPRelay
{
public class Connection
{
public delegate void delThreadSafeHandleException(System.Exception ex);
public delegate void ConnectionDelegate(Connection conn);
public int DataGridIndex;
Main pMain;
public TcpClient client { get; set; }
public TcpClient server { get; set; }
public String ClientState { get; set; }
public string ListenPort { get; set; }
public string remotePort { get; set; }
public string listenAddress { get; set; }
public string remoteAddress { get; set; }
private TcpListener service { get; set; }
private Main Form
{
get
{
return pMain;
}
}
private NetworkStream clientStream { get; set; }
private NetworkStream serverStream { get; set; }
public Connection(TcpClient client, TcpClient server)
{
clientStream = client.GetStream();
serverStream = server.GetStream();
}
public Connection(String srcAddress, int srcPort, String dstAddress, int dstPort, Main caller)
{
try
{
pMain = caller;
TcpListener _service = new TcpListener((IPAddress.Parse(srcAddress)), srcPort);
//Start the client service and add to connection property
_service.Start();
service = _service;
//Set other useful parameters
listenAddress = srcAddress;
ListenPort = srcPort.ToString();
remoteAddress = dstAddress;
remotePort = dstPort.ToString();
this.ClientState = "Listening";
}
catch (Exception ex)
{
pMain.HandleException(ex);
Thread.CurrentThread.Abort();
}
}
private TcpClient getServerConnection(String address, int port)
{
TcpClient client = new TcpClient(address, port);
if (client.Connected)
{
return client;
}
else
{
throw new Exception(
String.Format("Unable to connect to {0} on port {0}",
address,
port)
);
}
}
private void sendData(Byte[] databuf, NetworkStream stream, int size)
{
bool waiting = true;
while (waiting)
{
if (stream.CanWrite)
{
waiting = false;
stream.Write(databuf, 0, size);
}
else { throw new Exception("Unable to write to network stream"); }
}
}
//Main Looping and data processing goes here
public void ProcessClientRequest()
{
try
{
//Wait for a connection to the client
TcpClient client = service.AcceptTcpClient();
//Get the streams and set the peer endpoints
this.clientStream = client.GetStream();
this.client = client;
//Now that we have a client, lets connect to our server endpoint
TcpClient server = getServerConnection(remoteAddress, int.Parse(remotePort));
//Set some useful parameters
this.server = server;
this.serverStream = server.GetStream();
}
catch (Exception ex)
{
lock (ClientState)
{
this.ClientState = ex.Message;
}
CloseConnection();
Thread.CurrentThread.Abort();
}
while (true)
{
lock (ClientState)
{
checkConnectionStatus(client, server);
}
if (clientStream.CanRead && clientStream.DataAvailable)
{
Byte[] bytes = new Byte[(client.ReceiveBufferSize)];
IAsyncResult result = clientStream.BeginRead(bytes, 0, client.ReceiveBufferSize, null, null);
int size = clientStream.EndRead(result);
sendData(bytes, serverStream, size);
}
if (serverStream.CanRead && serverStream.DataAvailable)
{
Byte[] bytes = new byte[(server.ReceiveBufferSize)];
IAsyncResult result = serverStream.BeginRead(bytes, 0, server.ReceiveBufferSize, null, null);
int size = serverStream.EndRead(result);
sendData(bytes, clientStream, size);
}
}
}
private void checkConnectionStatus(TcpClient _client, TcpClient _server)
{
try
{
if (_client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_client.Client.Receive(buff, SocketFlags.Peek) == 0)
{
this.ClientState = "Closed";
CloseConnection();
Thread.CurrentThread.Abort();
}
}
else if (_server.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_server.Client.Receive(buff, SocketFlags.Peek) == 0)
{
this.ClientState = "Closed";
CloseConnection();
Thread.CurrentThread.Abort();
}
}
else { this.ClientState = "Connected"; }
}
catch (System.Net.Sockets.SocketException ex)
{
this.ClientState = ex.SocketErrorCode.ToString();
CloseConnection();
Thread.CurrentThread.Abort();
}
}
public void CloseConnection()
{
if (clientStream != null)
{
clientStream.Close();
clientStream.Dispose();
}
if (client != null)
{
client.Close();
}
if (serverStream != null)
{
serverStream.Close();
serverStream.Dispose();
}
if (server != null)
{
server.Close();
}
if (service != null)
{
service.Stop();
}
}
}
}
I also have a "Main" form and a "ConnectionManager" class that i'm playing around with.