Attempting to write a basic proxy I have implemented code along the following lines as a start.
private Thread _proxyThread;
public void Start()
{
this._proxyThread = new Thread(StartListener);
this._proxyThread.Start();
}
protected void StartListener(object data)
{
this._listener = new TcpListener(IPAddress.Any, 1234);
this._listener.Start();
while (true)
{
TcpClient client = this._listener.AcceptTcpClient();
while (client.Connected)
{
NetworkStream stream = client.GetStream();
StringBuilder request = new StringBuilder();
byte[] bytes = new byte[1024];
while (stream.DataAvailable && stream.CanRead)
{
int i = stream.Read(bytes, 0, bytes.Length);
request.Append(System.Text.Encoding.ASCII.GetString(bytes, 0, i));
}
if (stream.CanWrite)
{
byte[] response = System.Text.Encoding.Default.GetBytes("HTTP/1.1 200 OK" + Environment.NewLine + "Content-length: 4\r\n\r\n" + "Test");
stream.Write(response, 0, response.Length);
}
client.Close();
}
}
}
I then set my LAN proxy like this
My current goal (as you can hopefully deduce from my code) is to return just a text value with no headers or anything.
The problem that I am running into is that any of my attempts to run the code as is results in an intermittent behaviour where Chrome and IE9 will usually just return a ERR_CONNECTION_ABORTED or Internet Explorer cannot display the webpage respectively and very occasionally display the expected output "TEST".
I originally tried using a asynchronous approach (Using BeginAcceptTcpClient()) but have reverted back to this simpler technique because I thought this problem might stem from me doing something incorrectly in the asynchronous implementation but it seems that the cause is something else.
Could anyone please provide any guidance?
After your first iteration of the loop while (client.Connected) you close the connection. Only close it when you are done transferring all data.
It's not whether it's synchronous or asynchronous, you're just not responding with a proper HTTP response. Take a tool like Fiddler and look at the data being received when you request a regular webpage.
If you return this, it should work:
HTTP/1.1 200 OK
Content-length: 4
TEST
Related
Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...who knows for what reason. If anyone else stumbles upon this problem (in a program like Unity, where functions are looping constantly and data is always being processed), consider that you're moving too much irrelevant data.
Original Post
I've run into quite the problem, and I'm hoping I can receive some guidance.
In short, I'm wondering how to use TCP to communicate two Unity apps over the same computer. I've gotten it functioning in editor, but when both apps are built, communication quickly breaks down.
This is really stumping me, because I don't understand why an app would work in the Editor environment, but not in the official build.
When I use TCP to communicate between two Unity apps (on the same computer), it works so long as one of them is kept in the Unity environment. That is, if I build one app, and open the other in the Unity editor, TCP communication works flawlessly.
Here is some more background: One of my apps is functioning as a User Interface, and the other is interfacing with a Looking Glass to provide a holographic display of in-game objects. Originally, they were combined into one App - but I had a lot of trouble getting Unity's multidisplay support to function between two monitors of different resolutions. Looking Glass factory even provides a prefab to do just this, but it is broken in the current SDK. So I have resorted to using sockets to interface between two apps, one for each monitor.
I'm using C#'s TCP listener class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.8
And TCP client class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient?view=netframework-4.8
Presently, the UI is acting as the TCPListener, and the application that produces holograms is the TCPClient. Within each of these applications, I'm using two Queues - an IncomingMessages queue and an Outgoing Messages queue - which are global variables shared between the main thread and the networking thread.
TCP Listener:
private void Start()
{
incomingMessages = new Queue();
outgoingMessages = new Queue();
Application.runInBackground = true;
thread = new Thread(new ThreadStart(Receive));
thread.Start();
//stuff happens that's irrelevant to this question. And then...
}
void Receive()
{
TcpListener server = null;
try
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
Debug.Log("About to reenter main while in Server...");
while (threadContinue)
{
Debug.Log("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Debug.Log("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
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.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Debug.Log("Received from Client: " + data);
lock (this)
incomingMessages.Enqueue(data);
string response = supplyData();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Debug.Log("Sent to Client: " + response);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Debug.Log("Exiting 'Receive'");
}
And here is the TCP Client. It attempts to connect a regular intervals, and also whenever new data is available. This is so that it can receive information from the server regularly and share new data whenever it is available:
void Start()
{
//prepare networking
Application.runInBackground = true;
outgoingMessages = new Queue();
incomingMessages = new Queue();
thread = new Thread(new ThreadStart(Connect));
thread.Start();
//stuff happens that's irrelevant to this question...
}
private void Connect()
{
String server = "127.0.0.1";
Int32 port = 13000;
string message = "";
while (threadContinue == true)
{
if (timeToConnect())
{
lastConnection = ourTime;
if (outgoingMessages.Count > 0)
message = outgoingMessages.Dequeue().ToString();
else
message = "Nothing to report.";
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Debug.Log("Sent to Server: " + message);
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
lock (this)
incomingMessages.Enqueue(responseData);
Debug.Log("Received from Server: " + responseData);
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Debug.Log("ArgumentNullException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
}
}
}
private bool timeToConnect()
{
if ((ourTime - lastConnection > NETWORK_DELAY) || (outgoingMessages.Count > 0))
return true;
return false;
}
Instantiated in separate threads so that Unity's main thread can continue unhindered.
Again - it works in Editor, but when I build it, it breaks.
Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...just in the exported app. Who knows for what reason. If anyone else stumbles upon this problem...where you're bypassing Unity's multidisplay functionality by building multiple apps that communicate over network...consider that you're burdening your queues with too much data.
I've written a TcpClient and Server which are communicating via an SslStream.
The communication works, but when i send a message from the Client to the Server, first the Server reads 1 Byte, and in the next step the rest. Example: I want to send "test" via Client, and the Server receives first "t", and then "est"
Here is the code for the Client to send
public void Send(string text) {
byte[] message = Encoding.UTF8.GetBytes(text);
SecureStream.BeginWrite(message, 0, message.Length, new AsyncCallback(WriteCallback), null);
}
private void WriteCallback(IAsyncResult AR) {
}
And here the code the Server uses to read
private SslStream CryptedStream = ...;
private byte[] buffer = new byte[1024];
public void BeginReadCallback(IAsyncResult AsyncCall) {
// initialize variables
int bytesRead = 0;
try {
// retrieve packet
bytesRead = CryptedStream.EndRead(AsyncCall);
// check if client has disconnected
if (bytesRead > 0) {
// copy buffer to a temporary one
var temporaryBuffer = buffer;
Array.Resize(ref temporaryBuffer, bytesRead);
string read = Encoding.ASCII.GetString(temporaryBuffer);
SetText(read);
// read more data
CryptedStream.BeginRead(buffer, 0, 1024, new AsyncCallback(BeginReadCallback), null);
// client is still connected, read data from buffer
//ProcessPacket(temporaryBuffer, temporaryBuffer.Length, helper);
} else {
// client disconnected, do everything to disconnect the client
//DisconnectClient(helper);
}
} catch (Exception e) {
// encountered an error, closing connection
// Program.log.Add(e.ToString(), Logger.LogLevel.Error);
// DisconnectClient(helper);
}
}
Did i miss something?
Thanks for your help
As Lasse explained streaming APIs do not promise you to return a specific number of bytes per read.
The best fix for this is to not use sockets. Use a higher level API such as WCF, SignalR, HTTP, ...
If you insist you probably should use BinaryReader/Writer to send your data. That makes it quite easy. For example, it has string sending built-in. You also can manually length-prefix easily with those classes.
Probably, you don't need async IO and should not use it. If you insist you can at least get rid of the callbacks by using await.
I cannot get this example to run:
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient%28v=vs.110%29.aspx
The only thing Ive changed with their code is to put everything in the main method, and my port name of course. I Can connect to my server, and even send data. But on the line
Int32 bytes = networkStream.Read(data, 0, data.Length);
The program stops running without an exception. How can microsoft own code not work? My server doesnt send anything yet, but I dont think that should matter? (It recieves perfectly though.) Ive Read something that you cannot se exceptions in other threads, but I dont have any.
I also tried this thread:
C# tcp socket (networkstream.read won't work with 8.1)
It doesnt work. I run win 7 thoguh. But I wish this to work an all new windows.
NetworkStream.Read blocks until data is available, the connection is closed (it will return 0 in that case) or an exception occurs. It is designed that way.
If your server would send data, your client program would continue and be able to process the response.
NetworkStream.Read() is a synchronous call, it will wait till a response is received. To read data of different lengths, you can do something like below.
NOTE: I'm assuming server is sending only one response for a request.
private string GetResponse(string command)
{
//Send request
TcpClient client = new TcpClient(HOST, PORT);
Byte[] data = Encoding.ASCII.GetBytes(command);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
//Read response
data = new Byte[BUFFER_SIZE];
String response = String.Empty;
stream.ReadTimeout = READ_TIMEOUT;
while (!response.EndsWith(RESPONSE_END))
{
int bytes = stream.Read(data, 0, data.Length);
response += Encoding.ASCII.GetString(data, 0, bytes);
}
response = response.Remove(response.Length - RESPONSE_END.Length);
stream.Close();
client.Close();
//Return
return response;
}
I've been messing around with TCP sockets in C#, and I'm having some trouble communicating with an FTP server I have set up. I can connect initially and get the 220 message, but when I send the "USER nrcrast" command, I never get a response, and the DataAvailable property returns false. Anybody know what I'm doing wrong? Here's my code so far:
namespace TCPClient
{
public partial class TCPClientForm : Form
{
private TcpClient myClient;
NetworkStream stream;
public TCPClientForm()
{
InitializeComponent();
send();
}
void send()
{
while (true)
{
try
{
myClient = new TcpClient("nrcrast.dyndns.info", 21);
break;
}
catch (SocketException ex)
{
Console.WriteLine(ex.ToString());
}
}
stream = myClient.GetStream();
int sendOffset = 0;
int recOffset=0;
int dataLength;
Byte[] receiveData = new Byte[256];
// wait for a response
dataLength = stream.Read(receiveData, recOffset, receiveData.Length);
String recvdMessage = System.Text.Encoding.ASCII.GetString(receiveData, 0, dataLength);
Console.WriteLine(recvdMessage.ToString());
recOffset+=dataLength;
String message = "USER nrcrast";
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
stream.Write(data, 0, data.Length);
sendOffset += data.Length;
// wait for a response
while (!stream.DataAvailable)
{
}
dataLength = stream.Read(receiveData, 0, receiveData.Length);
recvdMessage = System.Text.Encoding.ASCII.GetString(receiveData, 0, dataLength);
Console.WriteLine(recvdMessage.ToString());
}
}
A shoot in the dark, you need to put a carriage return and new line at the end of the command
String message = "USER nrcrast\r\n";
If you're interested in looking over someone's shoulder on a similar project (not saying its perfect), I did the same thing a long time ago (this was back in .net 1.1, and ported it to .net 2.0 when the ssl stream stuff was added).
there are some tricky pieces to the FTP protocols with respect to timings of when you send commands, when the server expects you to open the data connection, when you read the server response, and so forth (depending on active / passive modes).
anyway, feel free to look over My FTP Client Library source code for reference as you do your own implementation. it's a pretty complete implementation and does auth ssl/tls as well.
I have recently started getting into NetworkStreams, and I had a question. I am currently creating a thread, and processing all incoming messages as they come in.
Here is the code to illustrate this:
client.Connect(serverEndPoint);
clientStream = client.GetStream();
client.NoDelay = true;
ctThread = new Thread(getMessage);
ctThread.Start();
private void getMessage()
{
while (true)
{
Byte[] data = new Byte[800];
String responseData = String.Empty;
Int32 bytes = clientStream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
MessageReceived(this, new ClientMessageEventArgs(responseData));
}
}
In the above, I raise an event "MessageReceived" which is handled according the the packet data. This works great, but also have a seperate case where I need to retrieve data immediately after I send my request.
Is it ok to have two streams per client? Is this even possible to do on the same port? How should this be handled? Essentially, I want to be able to Send and then Receive data immediately after (blocking way).
You can read and write from network streams independently and in a thread safe manner. i.e. reading from one thread and writing from another.
If you checkout the open source network communication library networkComms.net you can see how this is achieved independently in the sending method SendPacket() (line 1304) and receiving method IncomingPacketHandler() (line 802).
Mx