C# HTTPS Proxy using TCP - c#

I am trying to implement a HTTPS proxy using C#. The proxy should only support HTTPS, not HTTP. As far as I know, a HTTPListener is not a good choice, as you need a SSL certificate for it to support HTTPS, which a proxy usually does not provide.
I am using a TcpListener and TcpClients. Here's the code I got so far:
protected void HandleTCPRequest(object clientObject)
{
TcpClient inClient = clientObject as TcpClient;
TcpClient outClient = null;
try
{
NetworkStream clientStream = inClient.GetStream();
StreamReader clientReader = new StreamReader(clientStream);
StreamWriter clientWriter = new StreamWriter(clientStream);
// Read initial request.
List<String> connectRequest = new List<string>();
string line;
while (!String.IsNullOrEmpty(line = clientReader.ReadLine()))
{
connectRequest.Add(line);
}
if (connectRequest.Count == 0)
{
return;
}
string[] requestLine0Split = connectRequest[0].Split(' ');
if (requestLine0Split.Length < 3)
{
return;
}
// Check if it is CONNECT
string method = requestLine0Split[0];
if (!method.Equals("CONNECT"))
{
return;
}
// Get host and port
string requestUri = requestLine0Split[1];
string[] uriSplit = requestUri.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (uriSplit.Length < 2)
{
return;
}
string host = uriSplit[0];
int port = Int32.Parse(uriSplit[1]);
// Connect to server
outClient = new TcpClient(host, port);
NetworkStream serverStream = outClient.GetStream();
StreamWriter serverWriter = new StreamWriter(serverStream);
StreamReader serverReader = new StreamReader(serverStream);
// Send 200 Connection Established to Client
clientWriter.WriteLine("HTTP/1.0 200 Connection established\r\n\r\n");
clientWriter.Flush();
Logger.Debug("Established TCP connection for " + host);
while (true)
{
line = clientReader.ReadLine();
if (line != null)
{
Logger.Debug("->Server: " + line);
serverWriter.WriteLine(line);
}
line = serverReader.ReadLine();
if (line != null)
{
Logger.Debug("->Client: " + line);
clientWriter.WriteLine(line);
}
}
}
catch(Exception)
{
// Disconnent if connections still alive
try
{
if (inClient.Connected)
{
inClient.Close();
}
if (outClient != null && outClient.Connected)
{
outClient.Close();
}
}
catch (Exception e)
{
Logger.Warn("Could not close the tcp connection: ", e);
}
}
}
The incoming connections are accepted in another method.
EDIT: I made some changes. Now, the client starts sending SSL data, but the server never responds. After some time, the client just opens a new connection and tries again. The output I get:
Established TCP connection for www.google.de
->Server: ▬♥☺ ?☺ ?♥☺R'"??????#☼}~??♣|]?
->Server: ??_5OL(?? H ??
->Server: ?¶ ? ? 9 8?☼?♣ ? 5?? ?◄?‼ E D 3 2?♀?♫?☻?♦ ? A ♣ ♦ /?↕ ▬ ‼?
->Server: ?♥??
->Server: ☺ 0 ↕ ►
->Server: www.google.de
->Server: ♠ ↨ ↑ ↓ ♂ ☻☺ # 3t
I'm open for other suggestions than a TCP listener. Thanks!

Got it working. Dealing with the SSL data with StreamReader/StreamWriter was wrong. The data was converted to strings and thereby errors appeard (I assume). Using the NetworkStream.Read andNetworkStream.Write methods with byte[] did it.
Here's the code:
/// <summary>
/// Handles a TCP request.
/// </summary>
/// <param name="clientObject">The tcp client from the accepted connection.</param>
protected void HandleTCPRequest(object clientObject)
{
TcpClient inClient = clientObject as TcpClient;
TcpClient outClient = null;
try
{
NetworkStream clientStream = inClient.GetStream();
StreamReader clientReader = new StreamReader(clientStream);
StreamWriter clientWriter = new StreamWriter(clientStream);
// Read initial request.
List<String> connectRequest = new List<string>();
string line;
while (!String.IsNullOrEmpty(line = clientReader.ReadLine()))
{
connectRequest.Add(line);
}
if (connectRequest.Count == 0)
{
throw new Exception();
}
string[] requestLine0Split = connectRequest[0].Split(' ');
if (requestLine0Split.Length < 3)
{
throw new Exception();
}
// Check if it is CONNECT
string method = requestLine0Split[0];
if (!method.Equals("CONNECT"))
{
throw new Exception();
}
// Get host and port
string requestUri = requestLine0Split[1];
string[] uriSplit = requestUri.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (uriSplit.Length < 2)
{
throw new Exception();
}
string host = uriSplit[0];
int port = Int32.Parse(uriSplit[1]);
// Connect to server
outClient = new TcpClient(host, port);
// Send 200 Connection Established to Client
clientWriter.WriteLine("HTTP/1.0 200 Connection established\r\n\r\n");
clientWriter.Flush();
Logger.Debug("Established TCP connection for " + host + ":" + port);
Thread clientThread = new Thread(() => TunnelTCP(inClient, outClient));
Thread serverThread = new Thread(() => TunnelTCP(outClient, inClient));
clientThread.Start();
serverThread.Start();
}
catch(Exception)
{
// Disconnent if connections still alive
Logger.Debug("Closing TCP connection.");
try
{
if (inClient.Connected)
{
inClient.Close();
}
if (outClient != null && outClient.Connected)
{
outClient.Close();
}
}
catch (Exception e)
{
Logger.Warn("Could not close the tcp connection: ", e);
}
}
}
/// <summary>
/// Tunnels a TCP connection.
/// </summary>
/// <param name="inClient">The client to read from.</param>
/// <param name="outClient">The client to write to.</param>
public void TunnelTCP(TcpClient inClient, TcpClient outClient)
{
NetworkStream inStream = inClient.GetStream();
NetworkStream outStream = outClient.GetStream();
byte[] buffer = new byte[1024];
int read;
try
{
while (inClient.Connected && outClient.Connected)
{
if (inStream.DataAvailable && (read = inStream.Read(buffer, 0, buffer.Length)) != 0)
{
outStream.Write(buffer, 0, read);
}
}
}
catch (Exception e)
{
Logger.Debug("TCP connection error: ", e);
}
finally
{
Logger.Debug("Closing TCP connection.");
// Disconnent if connections still alive
try
{
if (inClient.Connected)
{
inClient.Close();
}
if (outClient.Connected)
{
outClient.Close();
}
}
catch (Exception e1)
{
Logger.Warn("Could not close the tcp connection: ", e1);
}
}
}

Related

TCP Connection keep disconnecting with Analyzer Mindray BS240

I'm working on a TCP communication program for the chemestry analyzer "Mindray BS240". The problem is that the analyzer keep disconnecting and reconnecting (every 30s). Here is my code, what did I miss ?
private void startTcpListner()
{
try
{
var port = settings.LisPort;
IPAddress localAddr = IPAddress.Parse(settings.LisIpAddress);
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
var bytes = new byte[256];
String data = null;
LogManager.GetCurrentClassLogger().Info("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also use server.AcceptSocket() here.
var client = server.AcceptTcpClient();
LogManager.GetCurrentClassLogger().Info("Connected !");
data = null;
// Get a stream object for reading and writing
stream = client.GetStream();
// Enter the listening loop.
while (true)
{
while (!stream.DataAvailable) ;
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
var line = Encoding.UTF8.GetString(bytes, 0, i);
LogManager.GetCurrentClassLogger().Info("Received: {0}", line);
data += line;
if (line.Length > 3 &&
line[line.Length - 2] == Hl7Helper.FileSeparator &&
line[line.Length - 1] == Hl7Helper.CarriageReturn)
{
handleMessage(data);
data = null;
}
}
}
}
catch (SocketException e)
{
LogManager.GetCurrentClassLogger().Error("SocketException: {0}", e);
}
catch (Exception e)
{
LogManager.GetCurrentClassLogger().Error(e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
}
In the log file, I have :
Waiting for a connection...
Connected !

StreamSocket C# Client is only able to write one time to server

I have been trying to setup a client-server application using StreamSockets in c#. I am able to initially connect to the server (ConnectAsync) and following to write and read the stream. If the client sends another stream to the server using the method WriteToServer the event on the server side is not being triggered (SocketListener_ConnectionReceived). I'm sending a xmlSerialized object "Message" to the server.
While debugging I don't receive any errors. On the client side though the after having used "WriteToServer" and moving forward to "ReadFromServer" the code gets obviously stuck in the line below since the server doesn't reply
int bufferLength = inStream.ReadByte();
I hope that someone call help. I am honestly not sure what the issues is because the "Write"-method is being used the same way both times the client attempts to write to the server.
Client is a Windows 10 computer and Server a Raspberry pi running Windows 10 IoT.
The class inside the client application which handles the connection looks like this.
StreamSocket socket;
HostName remoteHostName, localHostName;
string serviceAddress = "1337";
EndpointPair endPointPair;
Boolean isConnected;
Socket test;
public Connection()
{
remoteHostName = new HostName("172.16.20.202"); // might have to change based on pi's ip address
//remoteHostName = new HostName("172.16.20.4");
localHostName = new HostName(getLocalIpAddress());
endPointPair = new EndpointPair(localHostName, serviceAddress, remoteHostName, serviceAddress);
socket = new StreamSocket();
socket.Control.NoDelay = true;
socket.Control.QualityOfService = SocketQualityOfService.LowLatency;
}
private string getLocalIpAddress()
{
var icp = NetworkInformation.GetInternetConnectionProfile();
if (icp?.NetworkAdapter == null) return null;
var hostname =
NetworkInformation.GetHostNames()
.SingleOrDefault(
hn =>
hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId
== icp.NetworkAdapter.NetworkAdapterId);
// the ip address
return hostname?.CanonicalName;
}
public async Task StartConnection()
{
try
{
if (isConnected)
{
await socket.CancelIOAsync();
socket.Dispose();
socket = null;
isConnected = false;
}
await socket.ConnectAsync(endPointPair);
isConnected = true;
}
catch (Exception exc)
{
if (Windows.Networking.Sockets.SocketError.GetStatus(exc.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
Debug.WriteLine("Connect failed with error: " + exc.Message);
socket.Dispose();
isConnected = false;
socket = null;
//return null;
}
}
public async Task WriteToServer(Message msg)
{
try
{
using (DataWriter writer = new DataWriter(socket.OutputStream))
{
writer.WriteBytes(serialize(msg));
await writer.StoreAsync();
writer.DetachStream();
writer.Dispose();
}
}
catch (Exception exc)
{
Debug.WriteLine("Write failed with error: " + exc.Message);
}
}
public async Task<Library.Message> ReadFromServer()
{
try
{
Stream inStream = socket.InputStream.AsStreamForRead();
int bufferLength = inStream.ReadByte();
byte[] serializedMessage = new byte[bufferLength];
await inStream.ReadAsync(serializedMessage, 0, bufferLength);
await inStream.FlushAsync();
Library.Message incomingMessage;
using (var stream = new MemoryStream(serializedMessage))
{
var serializer = new XmlSerializer(typeof(Library.Message));
incomingMessage = (Library.Message)serializer.Deserialize(stream);
}
return incomingMessage;
}
catch (Exception exc)
{
Debug.WriteLine("Read failed with error: " + exc.Message);
return null;
}
}
private byte[] serialize(Message msg)
{
byte[] serializedMessage, returnArray;
var serializer = new XmlSerializer(typeof(Library.Message));
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, msg);
serializedMessage = stream.ToArray();
stream.Dispose();
}
int bufferLength = serializedMessage.Length;
returnArray = new byte[serializedMessage.Length + 1];
serializedMessage.CopyTo(returnArray, 1);
returnArray[0] = (byte)bufferLength;
Debug.WriteLine("ClientReturnArrayLength: " + returnArray.Length);
Debug.WriteLine("ClientFirstByte: " + returnArray[0]);
return returnArray;
}
The server side looks like this
public Task DispatcherPriority { get; private set; }
public MainPage()
{
this.InitializeComponent();
dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
hostName = new HostName(getLocalIpAddress());
clients = new List<Client>();
}
/// <summary>
/// Gets the ip address of the host
/// </summary>
/// <returns></returns>
private string getLocalIpAddress()
{
var icp = NetworkInformation.GetInternetConnectionProfile();
if (icp?.NetworkAdapter == null) return null;
var hostname =
NetworkInformation.GetHostNames()
.SingleOrDefault(
hn =>
hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId
== icp.NetworkAdapter.NetworkAdapterId);
// the ip address
return hostname?.CanonicalName;
}
async void setupSocketListener()
{
if (socketListener != null)
{
await socketListener.CancelIOAsync();
socketListener.Dispose();
socketListener = null;
}
socketListener = new StreamSocketListener();
socketListener.Control.QualityOfService = SocketQualityOfService.LowLatency;
socketListener.ConnectionReceived += SocketListener_ConnectionReceived;
await socketListener.BindServiceNameAsync("1337");
listBox.Items.Add("server started.");
clients.Clear();
}
private async void SocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
HostName ip = args.Socket.Information.RemoteAddress;
string port = args.Socket.Information.RemotePort;
try
{
Stream inStream = args.Socket.InputStream.AsStreamForRead();
int bufferLength = inStream.ReadByte();
byte[] serializedMessage = new byte[bufferLength];
await inStream.ReadAsync(serializedMessage, 0, bufferLength);
await inStream.FlushAsync();
Message incomingMessage;
using (var stream = new MemoryStream(serializedMessage))
{
var serializer = new XmlSerializer(typeof(Message));
incomingMessage = (Message)serializer.Deserialize(stream);
}
/// <summary>
/// 1 = Connected
/// 2 = SentNote
/// 3 = Login
/// </summary>
switch (incomingMessage.Mode)
{
case 1:
onClientConnect(ip, port, incomingMessage.Username, args.Socket);
break;
case 2:
onNoteReceived(incomingMessage);
break;
case 3:
//handle login
break;
}
}
catch (Exception msg)
{
Debug.WriteLine(msg);
}
}
private async void onNoteReceived(Message msg)
{
foreach (var client in clients)
{
//if (client.Username != msg.Username)
//{
using (DataWriter writer = new DataWriter(client.Socket.OutputStream))
{
writer.WriteBytes(serialize(msg));
await writer.StoreAsync();
writer.DetachStream();
writer.Dispose();
}
//}
}
}
private void buttonStartServer_Click(object sender, RoutedEventArgs e)
{
setupSocketListener();
}
private async void notifyClients(string username)
{
Message msg = new Message();
msg.Username = username;
foreach (var client in clients)
{
//if (client.Username != msg.Username)
//{
using (DataWriter writer = new DataWriter(client.Socket.OutputStream))
{
writer.WriteBytes(serialize(msg));
await writer.StoreAsync();
writer.DetachStream();
writer.Dispose();
}
//}
}
}
private async void onClientConnect(HostName ip, string port, string username, StreamSocket socket)
{
clients.Add(new Client(ip, port, username, socket));
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
listBox.Items.Add("User: " + username + " on IP " + ip + " is connected.");
});
notifyClients(username);
}
private byte[] serialize(Message msg)
{
byte[] serializedMessage, returnArray;
var serializer = new XmlSerializer(typeof(Message));
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, msg);
serializedMessage = stream.ToArray();
stream.Dispose();
}
int bufferLength = serializedMessage.Length;
returnArray = new byte[serializedMessage.Length + 1];
serializedMessage.CopyTo(returnArray, 1);
returnArray[0] = (byte)bufferLength;
Debug.WriteLine("ClientReturnArrayLength: " + returnArray.Length);
Debug.WriteLine("ClientFirstByte: " + returnArray[0]);
return returnArray;
}
}
This is the message class which I am using to send over the network.
public string Username { get; set; }
/// <summary>
/// 1 = startConnection
/// 2 = SendNote
/// 3 = Login
/// </summary>
public int Mode { get; set; }
public Piano PianoNote { get; set; }
public string Instrument { get; set; }
public Message()
{
}
}
public enum Piano { a1, a1s, b1, c1 };
**Edit: **
Message framing:
byte[] prefix = BitConverter.GetBytes(serializedMessage.Length);
returnArray = new byte[serializedMessage.Length + prefix.Length];
prefix.CopyTo(returnArray, 0);
serializedMessage.CopyTo(returnArray, prefix.Length);
Reading the message:
byte[] prefix = new byte[4];
await inStream.ReadAsync(prefix, 0, 4);
int bufferLength = BitConverter.ToInt32(prefix, 0);
Half-Open:
Instead of reading synchronous I switched to async reading the first 4 bytes as seen above.
I am able to initially connect to the server (ConnectAsync) and following to write and read the stream. If the client sends another stream to the server using the method WriteToServer the event on the server side is not being triggered (SocketListener_ConnectionReceived).
Take a good look at those names. You're calling ConnectAsync once and then WriteToServer twice, and only seeing SocketListener_ConnectionReceived once. There's only once connection, so yes, ConnectionReceived would only trigger once.
That's just scratching the surface, though. There's a few other very subtle issues wrong with this code.
One that sticks out is a lack of proper message framing, as I describe on my blog. You're using a single-byte length prefix, so on the wire it's OK (though limited to 256 bytes, which doesn't go far with XML). But the reading of the messages is incorrect; in particular, Stream.ReadAsync may read between 1 and bufferLength bytes, or it may return 0.
Another problem is that it's subject to the half-open problem, as I describe on my blog. In particular, int bufferLength = inStream.ReadByte(); will block indefinitely in a half-open situation. You should only use asynchronous methods for all network streams, and periodically write while waiting for data to arrive.
In summary, I strongly recommend that you use self-hosted SignalR instead of raw sockets. Raw socket programming is extremely difficult to do correctly, especially because incorrect code often happens to work correctly in a local environment.

Send objects over TCP with multiple clients and one server

Okay, I've been looking around and have found tutorials on both sending custom objects over TCP and also connecting multiple clients to one server, however I haven't been able to find an example of both so have decided to try it myself however I'm stuck. I so far have written a server that can handle an unlimited number of clients but have failed to implement sending of objects. This is the code I have so far for sending custom objects:
Client code:
Person p = new Person("Tyler", "Durden", 30); // create my serializable object
string serverIp = "127.0.0.1";
TcpClient client = new TcpClient(serverIp, 9050); // have my connection established with a Tcp Server
IFormatter formatter = new BinaryFormatter(); // the formatter that will serialize my object on my stream
NetworkStream strm = client.GetStream(); // the stream
formatter.Serialize(strm, p); // the serialization process
strm.Close();
client.Close();
Server code:
TcpListener server = new TcpListener(9050);
server.Start();
TcpClient client = server.AcceptTcpClient();
NetworkStream strm = client.GetStream();
IFormatter formatter = new BinaryFormatter();
Person p = (Person)formatter.Deserialize(strm); // you have to cast the deserialized object
strm.Close();
client.Close();
server.Stop();
This is the code I currently have for multiple clients:
Server code:
private ArrayList m_aryClients = new ArrayList(); //List of connected clients (Used for threading)
public Program()
{
//Empty constructor
}
static Program()
{
}
#region Main Enty Point
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//Set log file location
setLogFileLocation("/usr/local/bin/ClipCloud/" + DateTime.Now.ToShortDateString() + ".log");
//Start the application
printAndLog("Starting server");
Program appmain = new Program();
//Get local hostname
IPAddress[] addressList = null;
string hostName = "";
try
{
hostName = Dns.GetHostName();
addressList = Dns.GetHostByName(hostName).AddressList;
}
catch (Exception ex)
{
printAndLog("Failed to get local address (" + ex.Message + ")", 1);
}
//Start listening and set up threads
if ((addressList == null ? false : (int)addressList.Length >= 1))
{
object[] objArray = new object[] { "Listening on : (", hostName, ") ", addressList[0], ",", 399 };
printAndLog(string.Concat(objArray), 0);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(addressList[0], 399));
socket.Listen(10);
socket.BeginAccept(new AsyncCallback(appmain.OnConnectRequest), socket);
//Wait until user types exit
while (!(Console.ReadLine().ToLower() == "exit"))
{}
printAndLog("Shutting down server");
socket.Close();
//Clean up after yourself
GC.Collect();
GC.WaitForPendingFinalizers();
printAndLog("Application successfully shutdown");
}
else
{
printAndLog("Unable to obtain local address" , 2);
}
}
#endregion
#region Server requests
public void NewConnection(Socket sockClient)
{
//new client connected
ClientHandler clientHandler = new ClientHandler(sockClient);
m_aryClients.Add(clientHandler);
printAndLog(string.Concat("Client (",clientHandler.Sock.RemoteEndPoint, ") connected"));
//Send client welcome message
DateTime now = DateTime.Now;
string str = string.Concat("{", now.ToString("G"), "}");
byte[] bytes = Encoding.ASCII.GetBytes(str.ToCharArray());
clientHandler.Sock.Send(bytes, (int)bytes.Length, SocketFlags.None);
clientHandler.SetupRecieveCallback(this);
}
public void OnConnectRequest(IAsyncResult ar)
{
try
{
Socket asyncState = (Socket)ar.AsyncState;
NewConnection(asyncState.EndAccept(ar));
asyncState.BeginAccept(new AsyncCallback(OnConnectRequest), asyncState);
}
catch (Exception ex)
{
printAndLog(ex.Message);
}
}
public void OnRecievedData(IAsyncResult ar)
{
ClientHandler asyncState = (ClientHandler)ar.AsyncState;
byte[] recievedData = asyncState.GetRecievedData(ar);
if ((int)recievedData.Length >= 1)
{
foreach (ClientHandler mAryClient in this.m_aryClients)
{
try
{
//This is where all data is sent out to all users!
mAryClient.Sock.Send(recievedData);
}
catch
{
printAndLog(string.Concat("Failed to send data to client (", asyncState.Sock.RemoteEndPoint, ")"), 2);
mAryClient.Sock.Close();
this.m_aryClients.Remove(asyncState);
return;
}
}
asyncState.SetupRecieveCallback(this);
}
else
{
printAndLog(string.Concat("Client (", asyncState.Sock.RemoteEndPoint, ") disconnected"), 0);
asyncState.Sock.Close();
this.m_aryClients.Remove(asyncState);
}
}
#endregion
}
internal class ClientHandler
{
private Socket m_sock;
private byte[] m_byBuff = new byte[50];
public Socket Sock
{
get
{
return this.m_sock;
}
}
public ClientHandler(Socket sock)
{
this.m_sock = sock;
}
public byte[] GetRecievedData(IAsyncResult ar)
{
int num = 0;
try
{
num = this.m_sock.EndReceive(ar);
}
catch
{
}
byte[] numArray = new byte[num];
Array.Copy(this.m_byBuff, numArray, num);
return numArray;
}
public void SetupRecieveCallback(Program app)
{
try
{
AsyncCallback asyncCallback = new AsyncCallback(app.OnRecievedData);
this.m_sock.BeginReceive(this.m_byBuff, 0, (int)this.m_byBuff.Length, SocketFlags.None, asyncCallback, this);
}
catch (Exception ex)
{
Console.WriteLine("Recieve callback setup failed! {0}", ex.Message);
}
}
}
Can someone please help me connecting the two.

Server refuses to accept requests from clients

This is a program to search for strings from a file. The string required by the client is given from the client side, in my case, using telnet. The program I have written is a server side one. It accepts multiple clients.
But, the problems I am unable rectify are-
It doesn't check for strings from the file.
As soon as the client gets connected, the client cannot type in the strings they want to search in that particular file.
It doesn't send the reply back (i.e. If the string is present in the file or not) to the client. Its only shown on the server side.
How do I proceed further? Could someone tell me where am I going wrong? Could someone please help me out with the code?
This is my try at the program..
class Program
{
static void Main(string[] args)
{
IPAddress ipad = IPAddress.Parse("192.168.0.181");
TcpListener serversocket = new TcpListener(ipad, 8888);
TcpClient clientsocket = default(TcpClient);
Byte[] bytes = new Byte[256];
serversocket.Start();
Console.WriteLine(">> Server Started");
while(true)
{
clientsocket = serversocket.AcceptTcpClient();
Console.WriteLine("Accepted Connection From Client");
LineMatcher lm = new LineMatcher(clientsocket);
Thread thread = new Thread(new ThreadStart(lm.Run));
thread.Start();
Console.WriteLine("Client connected");
}
Console.WriteLine(" >> exit");
Console.ReadLine();
clientsocket.Close();
serversocket.Stop();
}
}
public class LineMatcher //I've jumbled it up here. Don't know what exactly to do..
{
public string fileName = "c:/myfile2.txt";
private TcpClient _client;
public LineMatcher(TcpClient client)
{
_client = client;
}
public void Run()
{
try
{
StreamReader sr = new StreamReader("c:/myfile2.txt");
using (var reader = new StreamReader(_client.GetStream()))
{
string line ="";
int lineNumber = 0;
while (null != (line = sr.ReadLine()))
{
lineNumber += 1;
byte[] data = new byte[1024];
NetworkStream stream = _client.GetStream();
//if (line.Equals(line))
for (int ct = stream.Read(data,0, data.Length-1); 0 < ct; ct = stream.Read(data,0,data.Length-1))
line += Encoding.ASCII.GetString(data, 0, ct);
line = line.Trim();
{
lineNumber.ToString();
data = Encoding.ASCII.GetBytes(line);
_client.Client.Send(data, data.Length, SocketFlags.None);
Console.WriteLine("Line {0} matches {1}", lineNumber, line);
}
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
Console.WriteLine("Closing client");
_client.Close();
}
}
I think you got some pieces in your Run-method swapped - here is a version that should do the job:
public void Run()
{
byte[] data;
try
{
using (var r = new StreamReader("c:/myfile2.txt"))
{
string line ="";
int lineNumber = 0;
while (null != (line = r.ReadLine()))
{
data = Encoding.ASCII.GetBytes(line + "\n");
_client.Client.Send(data, data.Length, SocketFlags.None);
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
Console.WriteLine("Closing client");
_client.Close();
}
Please note that I'm not 100% sure what you are trying to do (I think you want your textfile send line-by-line to your Terminal) - so you might have to change some bits here and there.
Don't know where the Stream-messes in your code came from but I guess you tried various tutorials/snippets and forgot to clean up ;)

TCP server causes MAX CPU utlization

i write this TCP communication library. the problem is that. when a client connects. the CPU usages boosts to maximum....this causes other application to become slow...
please take a look at the code and correct me where i did wrong..
the main code of my TCP library is
TCP Server Class
public class TCPServerEndPoint : ICommunication
{
private string channelName;
private string localIP;
private int localPort;
private string remoteIP;
private int remotePort;
private TcpListener tcpListenter;
/// <summary>
/// Accept the incomming connection and pass it to a thread to handle communication.
/// </summary>
private TCPServerWorker worker;
/// <summary>
/// List of threads created for connected clients.
/// </summary>
List<TCPServerWorker> workerThreads;
/// <summary>
/// Thread to keep listening process in seperate thread.
/// </summary>
private Thread serverThread;
/// <summary>
/// Flag to keep status of Endpoint.
/// </summary>
private bool keepRunning;
public TCPServerEndPoint()
{
this.keepRunning = false;
Guid guid = Guid.NewGuid();
channelName = guid.ToString();
workerThreads = new List<TCPServerWorker>();
}
public TCPServerEndPoint(string localIP, int localPort, string remoteIP, int remotePort)
{
this.localIP = localIP;
this.localPort = localPort;
this.remoteIP = remoteIP;
this.remotePort = remotePort;
workerThreads = new List<TCPServerWorker>();
this.keepRunning = false;
}
public event EventHandler<CommEventArgs> OnCommReceive;
public int CommStart()
{
if (this.IsStarted == true)
{
Console.WriteLine("TCP Server is already running");
return -1;
}
serverThread = new Thread(new ThreadStart(StartListening));
serverThread.IsBackground = true;
serverThread.Start();
return 0;
}
private void StartListening()
{
try
{
IPAddress localAddress = IPAddress.Parse(this.localIP);
tcpListenter = new TcpListener(localAddress, this.localPort);
tcpListenter.Start();
Console.WriteLine("TCP Server started");
Console.WriteLine("Server is listening on port : {0}", this.localPort);
this.keepRunning = true;
// look for incomming connections
while (this.keepRunning)
{
// connection received
TcpClient client = tcpListenter.AcceptTcpClient();
// create a new WorkerThread and pass the connected client to handle.
worker = new TCPServerWorker(client);
worker.dataReceived += new EventHandler<CommEventArgs>(worker_dataReceived);
workerThreads.Add(worker);
worker.Start();
}
tcpListenter.Stop();
Console.WriteLine("TCP Server stopped");
this.keepRunning = false;
}
catch
{
return;
}
}
void worker_dataReceived(object sender, CommEventArgs e)
{
if (this.OnCommReceive != null)
{
e.commChannel = this;
this.OnCommReceive(this, e);
}
}
public int CommStop()
{
if (this.IsStarted == false)
return -1;
// Close all worker threads created for connected clients.
foreach (TCPServerWorker item in workerThreads)
{
item.KeepRunning = false;
}
// break the listening loop
this.keepRunning = false;
// clear the worker thread list
workerThreads.Clear();
// force server to receive message to break while(keepRunning) loop
byte[] data = new byte[4];
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(this.localIP), localPort);
Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpClient.Connect(ipEndPoint);
tcpClient.SendTo(data, ipEndPoint);
tcpClient.Close();
return 0;
}
public int CommSend(CommEventArgs obj)
{
obj.destAddress = this.remoteIP;
obj.destPort = this.remotePort;
return CommSendTo(obj);
}
public int CommSendTo(CommEventArgs obj)
{
int n;
byte[] buf;
try
{
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(obj.destAddress), obj.destPort);
buf = (byte[])obj.data;
Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpClient.Connect(ipEndPoint);
n = tcpClient.SendTo(buf, ipEndPoint);
tcpClient.Close();
}
catch (Exception ex)
{
Console.WriteLine("Exception :: {0}", ex.Message);
return -1;
}
if (n == buf.Length)
{
if (OnCommSendComplete != null)
{
OnCommSendComplete(this, obj);
}
Console.WriteLine("Sent {0} bytes to {1}:{2}", n, obj.destAddress, obj.destPort);
}
else
{
return -1;
}
return n;
}
}
}
TCPServerWorker.cs
class TCPServerWorker
{
private TcpClient client;
private bool keepRunning;
public event EventHandler<CommEventArgs> dataReceived;
private const int MAX_TCP_DATA = 64000;
public bool KeepRunning
{
get
{
return this.keepRunning;
}
set
{
this.keepRunning = value;
}
}
public TCPServerWorker(TcpClient client)
{
this.client = client;
this.keepRunning = false;
}
public void Start()
{
Thread thread = new Thread(new ThreadStart(Process));
thread.IsBackground = true;
thread.Start();
}
private void Process()
{
if (client.Connected == true)
{
Console.WriteLine("Client connected :: {0}", client.Client.RemoteEndPoint);
this.keepRunning = true;
while (this.keepRunning)
{
// in my view. here is the main problem. this loop run for infinite time and causes CPU to reach at 100
byte[] buffer = new byte[MAX_TCP_DATA];
NetworkStream stream = client.GetStream();
StreamWriter writer = new StreamWriter(client.GetStream());
if (stream.DataAvailable == true)
{
int receivedBytesCount = stream.Read(buffer, 0, buffer.Length);
byte[] receivedBuffer = new byte[receivedBytesCount];
Array.Copy(buffer, receivedBuffer, receivedBytesCount);
String msg = Encoding.UTF8.GetString(receivedBuffer);
Console.WriteLine("Received MSG ::: " + msg);
writer.WriteLine("Server : Received {0} bytes", receivedBytesCount);
CommEventArgs comEventArg = new CommEventArgs();
comEventArg.data = (byte[])receivedBuffer;
IPEndPoint remoteIPEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
comEventArg.srcAddress = remoteIPEndPoint.Address.ToString();
comEventArg.srcPort = remoteIPEndPoint.Port;
comEventArg.length = receivedBytesCount;
this.OnDataReceived(comEventArg);
writer.Flush();
}
}
client.Close();
}
}
protected void OnDataReceived(CommEventArgs e)
{
if (this.dataReceived != null)
{
this.dataReceived(this, e);
}
}
}
}
You're using nonblocking I/O which leads to a loop (at least) in your client
while (this.keepRunning) {...}
which is consuming all your CPU resources by busy waiting.
You should consider to use blocking I/O or Socket.Select
Look at the first remark here
Details about select
One thing to note is that you never SET IsStarted .. you only GET it ._. Maybe you're spawning hundreds of threads =/ I'm talking about the TCPServerEndPoint class =/
Yes, you are busy waiting for a connection. I don't know socket programming so I can't give you details, but what you need to do is wait for a connection using the blocking system call.
I solved the issue after modifying the Process method of TCPServerWorker.cs
here is the changes
private void Process()
{
if (client.Connected == true)
{
Console.WriteLine("Client connected :: {0}", client.Client.RemoteEndPoint);
Byte[] bytes = new Byte[MAX_TCP_DATA];
String data = null;
NetworkStream stream = client.GetStream();
int i;
try
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// bytes contains received data in byte[].
// Translate data bytes to a UTF-8 string.
byte[] receivedBuffer = new byte[i];
Array.Copy(bytes, receivedBuffer, i);
data = System.Text.Encoding.UTF8.GetString(receivedBuffer);
Console.WriteLine("Received MSG ::: " + data);
// Process the data sent by the client.
byte[] msg = System.Text.Encoding.UTF8.GetBytes(data);
// Send back a response.
stream.Write(msg, 0, msg.Length);
CommEventArgs comEventArg = new CommEventArgs();
comEventArg.data = receivedBuffer;
IPEndPoint remoteIPEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
comEventArg.srcAddress = remoteIPEndPoint.Address.ToString();
comEventArg.srcPort = remoteIPEndPoint.Port;
comEventArg.length = i;
this.OnDataReceived(comEventArg);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
}
finally
{
client.Close();
}
}
}

Categories