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.
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 have created a TCP client and it connects fine but am a bit confused how to receive messages from the server without closing the connection ?
My current approach was to run a co-routine over the network stream read method but that freezes my program up so its obviously the wrong approach so am not sure how to fix it.
I want to keep the connection alive and read messages when ever they may arrive from the server.
This is what i have setup currently:
// the idea is to run a coroutine for recieving messages
private IEnumerator<float> _RunTCPSocket()
{
int timer = DateTime.Now.Second;
byte[] readBuffer = new byte[1024];
while (SocketManager.IsConnected)
{
// this is the keep alive packets to server to prevent timeout on server side
if (DateTime.Now.Second - timer > KeepAliveRate)
{
Debug.Log("Sending");
timer = DateTime.Now.Second;
SocketManager.Send(null);
}
int msgLength = SocketManager.Recieve(readBuffer);
if (msgLength > 0)
Debug.Log(Encoding.ASCII.GetString(readBuffer, 0, msgLength));
yield return Timing.WaitForOneFrame;
}
}
This is the code for the receive method:
public int Recieve(byte[] readBuffer)
{
if (!IsConnected)
return -1; //-1 signifies an error aka we are disconnected
try
{
// NetworkStream is from TcpClient.GetStream()
bytesRead = _networkStream.Read(readBuffer, 0, readBuffer.Length);
}
catch (Exception e)
{
IsConnected = false;
Debug.Log(e);
bytesRead = -1;
}
return bytesRead;
}
How do i prevent this from locking up my program ?
You can use Begin/End method to make your program responsible:
Document from microsoft
You can see that the using of BeginReceive method is so complex so personally, i don't think it's easy to use.
An alternative is to call the read/write method inside a Task.
The third option is use TcpClient which used on client side and TcpListener which used on server side. Those two class is just a wrapper for an underline TCP socket. Those wrapper can make your life much more easier with Stream and Async methods.
If you want to learn more about network programming with C#, i highly recomment this book: C# Network Programming by Richard Blum
Update
Code for working with Task:
public event EventHandler<ReceiveDataEventArgs> DataReceived = null;
public void StartReceive()
{
Task.Run(() =>
{
while (true)
{
var bytesRead = _networkStream.Read(readBuffer, 0, readBuffer.Length);
DataReceived?.Invoke(this, new ReceiveDataEventArgs
{
Data = bytesRead
});
}
});
}
public class ReceiveDataEventArgs : EventArgs
{
public byte[] Data { get; set; }
}
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've developing (or trying to, anyway) a program that uses Asynchronous Socket to, supposedly, pass strings to and fro the server and client, at any time.
This program requires no more than one client be connected to a server. I tried Socket Programming, but I found out it blocks the program until either one receives something.
Since I have only a basic understanding of Asynchronous socket programming, I just went for the simplest one I could find, or at least, the simplest one I could understand.
Here's my code for the Server:
public Socket g_server_conn;
public byte[] g_bmsg;
public bool check = false;
private void net_As_Accept(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
g_server_conn = server_conn.EndAccept(iar);
g_bmsg = new byte[1024];
check = true;
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_As_Send(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
try
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndReceive(iar);
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
public void net_Data_Send(string msg2snd) // Function for sending through socket
{
MessageBox.Show(msg2snd);
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_server_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_server_conn);
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do something
}
}
And here's my code for the Client:
private void net_As_Connect(IAsyncResult iar)
{
try
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndConnect(iar);
g_bmsg = new byte[1024];
check = true;
string toSendData = "&" + net_Name;
net_Data_Send(toSendData);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
private void net_As_Send(IAsyncResult iar)
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
public void net_Data_Send(string msg2snd)
{
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_client_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_client_conn);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do Something
}
else if (txt.StartsWith("$"))
{
// Do something Else
}
}
Now, the Client can connect to the Server fine. The Client can even send in a string containing the user's name to the Server, which will then be displayed on the Server. The Server then sends out the name of its user to the Client, which the client receives and displays. Whatever is sent is stored in a Label (lblBuffer)
But afterwards, say I have the following code:
private void btnSendData_Click(object sender, EventArgs e)
{
string posMov = "Stuff to send";
net_Data_Send(posMov);
}
The Client receives nothing. Putting a Message Box in net_Data_Send(msg2snd) function reveals that the server does in fact send out the message. In fact, putting in the Message Box in that function makes it work (the Client receives it), for reasons I don't know. Since I haven't tried sending a message from the Client to the Server (other than the name when the Client Connects), I assume the Client will have the same problem sending to the Server.
Also, when it does send the second message (by putting a Message Box in the net_Data_Send function), only parts of the Label (lblBuffer) are overwritten. So if I my name is "Anon E. Moose", and the Server sends that when the Client connects, and I try to send out, say, "0.0" (via button press) the Label on the Client would then read "0.0n E. Moose".
What did I do wrong? Can I have some help on this, please?
Perhaps I have a problem with net_Data_Receive and net_Data_Send?
I think you need to call BeginReceive on your client again, it looks like you are only calling it once, so after it has received the server name, it isn't listening for any more data from the server
private void net_As_Receive(IAsyncResult iar)
{
var bytesRead = g_client_conn.EndReceive(iar);
if (bytesRead != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, bytesRead));
check = false;
}
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
also, as I mentioned in my comment, use the bytesRead value to work out how much of the buffer you need to use.
You will need to work out if the data you have received from the socket is the full amount, or if you need to read more data to make up the current message from the other side.
BeginReceive doesn't just call its callback whenever a new packet (string in your case arrives). In fact. BeginReceive or any raw socket method works in a stream based fasion, not packet based. See http://msdn.microsoft.com/en-us/library/bew39x2a.aspx for an example.
What you need to do, is in your 'net_As_Receive' callback method (naming is terrible imo), you need to make a call first to socket.EndRecieve(IAsyncResult), which in turn returns the total bytes currently available. After that, you have to make a decision whether to receive more data or not.
For example:
private StringBuilder packetBuilder;
{
if (packetBuilder == null)
packetBuilder = new StringBuilder();
// finalyze the receive
int length = g_server_conn.EndReceive(iar);
if (length != 0)
{
// get the total bytes received. Note that the length property is of that of the number of bytes received rather than that of the buffer
packetBuilder.Append(Encoding.ASCII.GetString(g_bmsg, 0, length));
net_Data_Receive(packetBuilder.ToString());
check = false;
}
// receive the next part
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
Note that this example doesnt care about packages. It will work if your lucky but there is a good change either a part of a string will be shown or 2 different strings will be combined. A good implementation will look for a string end and only show that part while buffering the rest untill a new string end is found. You can also use a StreamReader for making your life much easier
I have been banging my head against my code for the better part of the day, and I am completely stumped. Basically, the source game engine has a documented protocol for its RCON (Remote Console Over Network?) which I am trying to reproduce. There are hundreds of examples, but all of them are from the client side (establishing a connection to the game server's RCON) where as I am trying to actually re-create the server portion to reply to clients.
Here is the information on the RCON Protocol. The problem I am having with the code is, when I receive the Authentication request everything is fine. When I attempt to reply to it and okay the connection, the connection fails. So I am doing something wrong when replying but not sure what.
http://developer.valvesoftware.com/wiki/Source_RCON_Protocol
private void ReadClientPacket(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
while (true)
{
try
{
int packetsize;
// Create a new Packet Object and fill out the data from the incoming TCP Packets
RCONPacket packet = new RCONPacket();
using (BinaryReader reader = new BinaryReader(clientStream))
{
// First Int32 is Packet Size
packetsize = reader.ReadInt32();
packet.RequestId = reader.ReadInt32();
packet.RconDataReceived = (RCONPacket.RCONDATA_rec)reader.ReadInt32();
Console.WriteLine("Packet Size: {0} RequestID: {1} ServerData: {2}", packetsize, packet.RequestId, packet.RconDataReceived);
// Read first and second String in the Packet (UTF8 Null Terminated)
packet.String1 = ReadBytesString(reader);
packet.String2 = ReadBytesString(reader);
Console.WriteLine("String1: {0} String2: {1}", packet.String1, packet.String2);
switch (packet.RconDataReceived)
{
case RCONPacket.RCONDATA_rec.SERVERDATA_AUTH:
{
ReplyAuthRequest(packet.RequestId, tcpClient);
break;
}
case RCONPacket.RCONDATA_rec.SERVERDATA_EXECCOMMAND:
{
//ReplyExecCommand(packet.RequestId, tcpClient);
break;
}
default:
{
break;
}
}
}
break;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
break;
}
}
tcpClient.Close();
}
private void ReplyAuthRequest(int RequestID, TcpClient client)
{
Console.WriteLine("Replying to Auth Request");
// Authentication Reply
using (NetworkStream clientStream = client.GetStream())
using (MemoryStream stream = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((int)10); // Packet Size
writer.Write(RequestID); // Mirror RequestID if Authenticated, -1 if Failed
writer.Write((int)RCONPacket.RCONDATA_sent.SERVERDATA_AUTH_RESPONSE);
writer.Write(ConvertStringToByteArray("" + char.MinValue));
writer.Write(ConvertStringToByteArray("" + char.MinValue));
byte[] buffer = stream.ToArray();
Console.WriteLine("size of full auth response packet is {0}", buffer.Length);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
}
Have you been using Wireshark?
This tool is essential when you try to copy/reverse-engineer existing protocols. Just do a normal authentication with a known-working client and save the log. Then use your own code and try to see where the bits sent on the wire are different from those in the log.
Sometimes its pretty difficult things to see just by looking at the code, like a '\n' getting inserted at the wrong point, or an extra line after the whole message
I've had a similar problem a while ago
In your Reply function :
using (NetworkStream clientStream = client.GetStream())
(...)
The disposal of the clientStream could be the culprit. In my case the disposal of the stream caused the connection termination, GetStream() doesn't return a new instance of a stream, it returns the Stream that is owned by the TCPClient. See if that helps.