I have the following method which sends an rcon command to a game server.
public string sendCommand(string command)
{
byte[] bufferTemp = Encoding.ASCII.GetBytes(command);
byte[] bufferSend = new byte[bufferTemp.Length + 4];
//big enough to receive response
byte[] bufferRec = new byte[65000];
//intial 4 characters as per standard
bufferSend[0] = byte.Parse("255");
bufferSend[1] = byte.Parse("255");
bufferSend[2] = byte.Parse("255");
bufferSend[3] = byte.Parse("255");
int j = 4;
for (int i = 0; i < bufferTemp.Length; i++)
{
bufferSend[j++] = bufferTemp[i];
}
//send rcon command and get response
try
{
this.server.Socket.Send(bufferSend, SocketFlags.None);
this.server.Socket.Receive(bufferRec);
}
catch (SocketException e)
{
MessageBox.Show(e.ToString(), "Error occured");
}
string response = Encoding.ASCII.GetString(bufferRec);
return response;
}
out of all the commands I can possibly send, 1 of them returns a lot more data than the others and it seems that the *buffer_rec* byte array only gets about 1/4 of the message, but that array has been declared big enough to contain all the data.
On the subsequent 3 requests, the rest of the data gets output, as if it was buffered in some way.
I don't know why this is taking place. If you do, could you please let me know how to remedy the problem?
Thank you
Crouz
Ok, it seems that after 6 hours of looking into this I have finally come up with a solution, so here is a small change made to the receiving of data, note the thread sleeping for 10ms, this seems to be needed to let the datagrams arrive in due time.
//send rcon command and get response
string response = "";
try
{
this.server.Socket.Send(bufferSend, SocketFlags.None);
do
{
int bytesReceived = this.server.Socket.Receive(bufferRec);
response += Encoding.ASCII.GetString(bufferRec, 0, bytesReceived);
System.Threading.Thread.Sleep(10);
} while (this.server.Socket.Available > 0);
}
catch (SocketException e)
{
MessageBox.Show(e.ToString(), "Error occured");
}
If you think there is a better or more elegant way of handling this, don't hesitate to slam my code, but do so by offering an alternative as I am always happy to learn new things. Regards, Crouz
Related
I have an SSL connection to a server and post requests to it. The act of posting a message should be instant with no delay to read the response, because there would be consequent posts that should come without the delay.
That's why i just do
this.sslStream.Write(byteArray, 0, byteArray.Length);
However, I need some responses to be actually received by my app, so I have a parallel thread:
this.threadReadStream = new Thread(new ThreadStart(this.ThreadReadStream));
this.threadReadStream.Start();
In that thread I have a loop which reads data from sslStream:
public void ThreadReadStream()
{
int bufferLen = 4096, byteCount, pos, pos2;
var buffer = new byte[bufferLen];
string responseBuffer = "", responsePart, response = "";
bool err = true;
while (true)
{
err = true;
byteCount = 0;
while (err)
{
err = false;
byteCount = 0;
try
{
byteCount = this.sslStream.Read(buffer, 0, bufferLen);
}
catch (Exception exception)
{
err = true;
this.TcpConnect();
}
}
if (byteCount > 0)
{
// do something
}
}
}
The problem is that sslStream.Read always returns 0 while being used in separate thread, but works fine if called in the same thread with sslStream.Write.
Few comments about the things that are not likely to influece the problem, however, it is better to clarify the stuff:
1) I use while(err) cycle to check that the connection was not broken. If so, I reconnect and read again.
2) For the ones who don't like while(true) thing. Yes, I have the exit condition from the loop in the parallel thread which shut downs all the tcp-related threads.
How does the server identify that the write process was ended?
Maybe there is actually a data on the way to the server so it don't starting to send the response.
In addition, sharing a TCP connection between different threads is not a good practice.
We have a requirement to upload large firmware files to printers to upgrade the firmware of the device. The printer device is in the same network as my server, and the size of firmware that we are trying to upload is approximately to 200 - 500 MB. The approach that we have chosen is to load the firmware (.bin file) into Memory stream and write it in chunks over the network using TcpClient.
Based on the response from the network stream, we are displaying the status of firmware upgrade to the client. Following is the code snippet that we have used for firmware upgrade. I want to know if it is the best approach, as wrong one may harm the device.
EDIT:
class MyClass
{
int port = 9100;
string _deviceip;
byte[] m_ReadBuffer = null;
TcpClient _tcpclient;
NetworkStream m_NetworkStream;
static string CRLF = "\r\n";
public event EventHandler<DeviceStatus> onReceiveUpdate;
public async Task<bool> UploadFirmware(Stream _stream)
{
bool success = false;
try
{
_tcpclient = new TcpClient();
_tcpclient.Connect(_deviceip, port);
_stream.Seek(0, SeekOrigin.Begin);
m_NetworkStream = _tcpclient.GetStream();
byte[] buffer = new byte[1024];
m_ReadBuffer = new byte[1024];
int readcount = 0;
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length,
new AsyncCallback(EndReceive), null);
await Task.Run(() =>
{
while ((readcount = _stream.Read(buffer, 0, buffer.Length)) > 0)
{
m_NetworkStream.Write(buffer, 0, readcount);
m_NetworkStream.Flush();
}
});
success = true;
}
catch (Exception ex)
{
upgradeStatus = false;
}
return success;
}
private void EndReceive(IAsyncResult ar)
{
try
{
int nBytes;
nBytes = m_NetworkStream.EndRead(ar);
if (nBytes > 0)
{
string res = Encoding.UTF8.GetString(m_ReadBuffer, 0, nBytes);
DeviceStatus status = new DeviceStatus();
string[] readlines = res.Split(new string[] { CRLF },
StringSplitOptions.RemoveEmptyEntries);
foreach (string readline in readlines)
{
if (readline.StartsWith("CODE"))
{
//read readline string here
break;
}
}
}
if (m_NetworkStream.CanRead)
{
do
{
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new
AsyncCallback(EndReceive), null);
} while (m_NetworkStream.DataAvailable);
}
}
catch (ObjectDisposedException ods)
{
return;
}
catch (System.IO.IOException ex)
{
}
}
}
Any help will be really appreciated.
Your code is basically fine with a few issues:
m_NetworkStream.Flush(); AFAIK this does nothing. If it did something it would harm throughput. So delete that.
_stream.Seek(0, SeekOrigin.Begin); seeking is the callers concern, remove that. This is a layering violation.
Use bigger bigger buffers. Determine the right size experimentally. I usually start at 64KB for bulk transfers. This makes the IOs less chatty.
Turn on nagling which helps with bulk transfers because it saves you from spurious small packets.
You can replace the entire read-write-loop with Stream.Copy.
The way you report exceptions to the callers hides a lot of information. Just let the exception bubble out. Don't return a bool.
Use using for all resource to ensure they are cleaned up in the error case.
nBytes = m_NetworkStream.EndRead(ar); here, you assume that a single read will return all data that will be coming. You might receive just the first byte, though. Probably, you should use StreamReader.ReadLine in a loop until you know you are done.
catch (System.IO.IOException ex) { } What is that about? If firmware updates are such a critical thing suppressing errors appears very dangerous. How else can you find out about bugs?
I would convert the reading code to use await.
As the maximum length for a TcpPacket is 65535 (2^16-1), if you send any packet breaching this lenght it will be truncated. If I were you, I think the best way of sending large packets, is setting a Header of every packet and enumerating them. For example:
C->S ; [P1] <content>
and then the same structure, just plus 1 [P2] <content>
To do so, just use few substrings to truncate the data and sending them.
Cheers!
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'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
For some program, I want to send data over from a python program to a c# program. I put all of my data into a list in python and send it over after converting to bytes (packing them as doubles, so I have 8 bytes per number I am sending over). Having some understanding of how sockets and TCP streams work, the very first number in the list is the amount of bytes that the rest of the list takes up. Hence, the first 8 bytes of my stream tell me how many bytes I need to read to get all other data.
However, when I call BeginRead and it calls the callback, it has read 8 bytes less than I asked it to read. For instance, those first 8 bytes tell me there is 116432 bytes to read, but when I call EndRead it returns 116424.
Okay, so what? There's eight bytes missing, this amounts to one double being lost. This is a problem in and of itself, but I even figured out where this double has gone.
In python, at a specific point (while it is still doubles) in my data, I see I am sending this: "...,1961.0, 0.0128, 2033.0, 0.0442, 2034.0,..." when I inspect that same point in c# (after converting my bytes back to doubles), I see this: "..,1961.0, 2033.0002, 0.0442,2034.0,...".
To me, it seems clear that somehow these 8 bytes got mashed together into one, fusing the two number (bit-wise maybe?) together somehow. I have also noticed that the index of where this occurs in the byte data is roughly at the 64k-65k mark. So I'm suspecting that with 64kbytes being the max packet size of TCP packets, the stream has some kind of hiccup there and overwrites one part of my buffer without clearing it, leading to some literal mix up? Does anybody have any idea how I could fix this problem or what mistake I made that is causing this to happen?
I will paste the two relevant functions here.
private void Listen(int port)
{
try
{
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
var client = _server.AcceptTcpClient();
// Get a stream object for reading and writing
var stream = client.GetStream();
var pLength = new byte[8];
// Loop to receive all the data sent by the client.
while(_running)
{
if(!stream.DataAvailable && stream.Read(pLength, 0, 8) <= 0)
continue;
var nOfBytes = (int) BitConverter.ToDouble(pLength, 0);
pLength = new byte[8];
if (nOfBytes <= 0)
{
continue;
}
var localBytes = new byte[nOfBytes];
var scriptData = new ScriptData(stream, localBytes);
stream.BeginRead(localBytes, 0, nOfBytes, new AsyncCallback(GotAllBytes), scriptData);
}
// Shutdown and end connection
client.Close();
}
catch (SocketException e)
{
_errorBool = true;
_errorString = "Port: " + port + "\n" + e.Message + e.StackTrace;
}
finally
{
// Stop listening for new clients.
_server.Stop();
}
}
private void GotAllBytes(IAsyncResult result)
{
var scriptData = (ScriptData)result.AsyncState;
if (OnlinePaused)
{
scriptData.Stream.EndRead(result);
return;
}
var bytesRead = scriptData.Stream.EndRead(result);
_rawDataQueue.Enqueue(scriptData.Buffer.ToList());
}
Thanks for reading, I hope you can help out.
Thanks to Jeroen Mostert in the comments of the original question, this problem has been resolved by not working async but instead implementing a BinaryReader.
private void Listen(int port)
{
try
{
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
var client = _server.AcceptTcpClient();
// Get a stream object for reading and writing
var stream = client.GetStream();
var pLength = new byte[8];
// Loop to receive all the data sent by the client.
while(_running)
{
if(!stream.DataAvailable && stream.Read(pLength, 0, 8) <= 0)
continue;
var nOfBytes = (int) BitConverter.ToDouble(pLength, 0);
pLength = new byte[8];
if (nOfBytes <= 0)
{
continue;
}
var localBytes = new byte[nOfBytes];
var reader = new BinaryReader(stream);
ProcessData(reader);
}
// Shutdown and end connection
client.Close();
}
catch (SocketException e)
{
_errorBool = true;
_errorString = "Port: " + port + "\n" + e.Message + e.StackTrace;
}
finally
{
// Stop listening for new clients.
_server.Stop();
}
}
Thank you for everybody who was trying to help.