I need to create an application which requires communicating to an existent software using TCP/IP, where both mine and the other application will be using the port number specified below.
private void frmScan_Load(object sender, EventArgs e)
{
clientSocket.Connect("100.100.100.30", 76545);
}
public void msg(string mesg)
{
textBox1.Text = textBox1.Text + Environment.NewLine + " >> " + mesg;
}
private void cmdSCANok_Click(object sender, EventArgs e)
{
msg("Client Started");
NetworkStream serverStream = clientSocket.GetStream();
byte[] outStream = Encoding.ASCII.GetBytes("PCK|SCAN|5025066840471");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
byte[] inStream = new byte[10025];
serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize);
string returndata = Encoding.ASCII.GetString(inStream, 0, inStream.Length);
msg("Data from Server : " + returndata);
}
What happens is, the program I am communicating with has some in-built language where it will understand the code that I send, and it will return data according to the code that I have sent. So in the code above, I sent three bits of code: ("PCK|SCAN|5025066840471"
) which will find a specific item in the database. When it runs, I get an error on the line:
serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize);
the error shows the following:
"Specified argument was out of the range of valid values.
Parameter name: size"
I followed the tutorial I saw on this website: http://csharp.net-informations.com/communications/csharp-client-socket.htm - But I did slightly different. So instead of putting
string returndata = Encoding.ASCII.GetString(inStream);
I wrote:
string returndata = Encoding.ASCII.GetString(inStream, 0, inStream.Length);
I am extremely confused on why I am getting those problems, and to be honest I am not understanding much of what the code is doing, I just have a rough idea, but not enough to troubleshoot this. Can someone help please?
Much appreciated!
PS: I am programming for Windows CE (portable device) on Visual Studio 2010.
Your code is a great example of how not to do TCP communication. I've seen this code copied over and over many times, and I'd be very happy to point you to a good tutorial on TCP - too bad I haven't seen one yet :)
Let me point out some errors first:
- TCP doesn't guarantee you the packet arrives as one bunch of bytes. So (theoretically) the
Write
operation could result in a split, requiring two reads on the other side. Sending data without headers over TCP is a very bad idea - the receiving side has no idea how much it has to read. So you've got two options - either write the length of the whole bunch of data before the data itself, or use a control character to end the "packet"
- The first point should also clarify that your reading is wrong as well. It may take more than a single read operation to read the whole "command", or a single read operation might give you two commands at once!
- You're reading
ReceiveBufferSize
bytes into a 10025
long buffer. ReceiveBufferSize
might be bigger than your buffer. Don't do that - read a max count of inStream.Length
. If you were coding in C++, this would be a great example of a buffer overflow.
- As you're converting the data to a string, you're expecting the whole buffer is full. That's most likely not the case. Instead, you have to store the return value of the read call - it tells you how many bytes were actually read. Otherwise, you're reading garbage, and basically having another buffer overflow.
So a much better (though still far from perfect) implementation would be like this:
NetworkStream serverStream = clientSocket.GetStream();
byte[] outStream = Encoding.ASCII.GetBytes("PCK|SCAN|5025066840471");
// It would be much nicer to send a terminator or data length first,
// but if your server doesn't expect that, you're out of luck.
serverStream.Write(outStream, 0, outStream.Length);
// When using magic numbers, at least use nice ones :)
byte[] inStream = new byte[4096];
// This will read at most inStream.Length bytes - it can be less, and it
// doesn't tell us how much data there is left for reading.
int bytesRead = serverStream.Read(inStream, 0, inStream.Length);
// Only convert bytesRead bytes - the rest is garbage
string returndata = Encoding.ASCII.GetString(inStream, 0, bytesRead);
Oh, and I have to recommend this essay on TCP protocol design.
It talks about many of the misconceptions about TCP, most importantly see the Message Framing part.
NetworkStream.Read method has the following check inside:
if(size < 0 || size > (buffer.Length - offset))
throw new ArgumentOutOfRanveException("size");
In your case:
size = clientSocket.ReceiveBufferSize
offset = 0
buffer = inStream
The error you received means that clientSocket.ReceiveBufferSize > inStream.Length. In other words you are trying to read more bytes than are available. Try to use the following code:
...
var count = serverStream.Read(inStream, 0, inStream.Length);
string returndata = Encoding.ASCII.GetString(inStream, 0, count);
See also an example here.