C# Socket Receive to String - c#

Following this guide I wrote the following function:
public string ConnectToHost(string ip, int port)
{
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
IPAddress host = IPAddress.Parse(ip);
IPEndPoint ipep = new IPEndPoint(host, port);
socket.Connect(ipep);
byte[] msg = Encoding.Unicode.GetBytes("<Client Quit>");
int msgSend = socket.Send(msg);
byte[] bytes;
int byteRecieve;
String msgRecieved = Encoding.Unicode.GetString(bytes, 0, byteRecieve);
while (socket.Available > 0)
{
byteRecieve = socket.Receive(bytes);
msgRecieved += Encoding.Unicode.GetString(bytes, 0, byteRecieve);
}
socket.Shutdown(SocketShutdown.Both);
socket.Close();
return msgRecieved;
}
But I get error
Use of unassigned local variable 'bytes' and Use of unassigned local variable 'byteRecieve'
on the lie of code
String msgRecieved = Encoding.Unicode.GetString(bytes, 0, byteRecieve);
Removing the Encoding.Unicode.GetString(bytes, 0, byteRecieve); from the line and only have String msgRecieved; gave the same error but within the while loop.
How do I get this function to return the recieved bytes as string?

Well, it's true that you are using those variables without them being assigned, no? That's not allowed because it's unclear what that means. In this case you can make it work by saying:
byte[] bytes = new byte[4096];
int byteRecieve = 0;
When you have solved those compiler errors, which have nothing to do with sockets, you'll find that using Available underestimates the amount of data that's incoming. It is almost never useful.
Also, you'll find that Unicode encoded strings cannot be broken up at arbitrary boundaries so this does not work either. Use StreamReader.
You probably should keep looking for a different tutorial. Most are very broken. TCP is very hard to get right.

Look at these two lines of code:
int byteRecieve;
and
String msgRecieved = Encoding.Unicode.GetString(bytes, 0, byteRecieve);
In the first line you declare byteRecieve, but do not assign a value to it. In the second line you make use of it. Since you have not assigned a value to it you get the error message.
Since you do the right thing inside the while loop, you can simply delete the second line above....
Also
byte [] bytes;
should become something like
byte [] bytes = new bytes[256];
or similar. Again your version merely declared bytes to be an array of byte, but did not allocate/assign that an actual array value to it.

Related

Can't get the text correctly over TCP

I know my title is not good but I did not know how to write this problem. I'm receiving a XML over tcp connection but the problem is the string is full of "\0" so I can't see the message when I use that string variable. I can do string.length and see It's filled but can't reach the text. I've tried replace method and it worked but I know this is not the correct solution. So here is my question how can I receive the text correctly?
TcpListener tcpListener = new TcpListener(Convert.ToInt16(_Port));
tcpListener.Start();
while (true)
{
Socket handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
Control.CheckForIllegalCrossThreadCalls = false;
NetworkStream networkStream = new NetworkStream(handlerSocket);
byte[] myReadBuffer = new byte[102400000];
int numberOfBytesRead = 0;
string myCompleteMessage = "";
do
{
numberOfBytesRead = networkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage += Encoding.UTF8.GetString(myReadBuffer,
}
while (networkStream.DataAvailable);
Console.WriteLine("Text: "+myCompleteMessage);
}
}
I've tried to change the Encoding.UTF8 to Encoding.ASCII and Encoding.UNICODE but it did not worked.
Update: My problem is still continue, the data I receive has "\0" before every character. I couldn't find why.. I use replace and inserting \0 again once I want to send data back. Can this be because of some sort of encoding? I've tried getEncodings aswell and tried many code pages but non work
It turns all the "\0"'s inside my string was because the data I receive was encoded with BigEndian.. when I used
myCompleteMessage += Encoding.BigEndianUnicode.GetString(myReadBuffer, 0, numberOfBytesRead);
everything worked well. Wanted to share this here in case if someone receive the same problem.

How do you fill a Byte header with empty characters?

Im currently trying to add additional bytes to a byte array.
Im trying to send a header to a server that contains the computer name. However because the computer name could change for every machine im trying to create a byte array that is a specific length like 100 bytes.
Which means once i have my string header "rdmstreamĀ§" + Dns.GetHostName()" I need to add x amounts of bytes at the end or start as padding so the overall byte length = 100.
I was wondering if this was possible?
Below is an example of my code for having a set header length:
public static void SendMultiScreen(byte[] img)
{
try
{
//string command = ("rdmstreamĀ§" + Dns.GetHostName()); //This is what I want to add.
byte[] send = new byte[img.Length + 16]; //Create a new buffer to send to the server
byte[] header = Encoding.Unicode.GetBytes("rdmstrea"); //Get the bytes of the header
Buffer.BlockCopy(header, 0, send, 0, header.Length); //Copy the header to the main buffer
fps = 800;
Buffer.BlockCopy(img, 0, send, header.Length, img.Length); //Copy the image to the main buffer
_clientSocket.Send(send, 0, send.Length, SocketFlags.None); //Send the image to the server
}
As you can see as long as the message is only 8 Characters long this works fine. However I want the characters in the message to be variable.
I don't have much knowledge on bytes if im honest so any additional help would be much appreciated.
Thankyou in advance.
One can argue about it if padding is the right way to go, but you could pad the name of your host
string hostName = "OhWhatEver".PadRight(100)
then use this as input for your GetBytes call.
Edit:
If you can't live with the spaces use that:
byte[] header = new byte[100];
byte[] hostname = System.Text.Encoding.Unicode.GetBytes("rdmstreamĀ§" + System.Net.Dns.GetHostName());
Array.Copy(hostname, header, hostname.Length);
If your concern is packet fragmentation: Socket has overloads to send a list of buffer segments in a single operation. That means you can do something like:
var segments = new List<ArraySegment<byte>>();
segments.Add(header);
segments.Add(img);
Note that it is not necessary for the header to be the full array; you can send a part of an array, which allows you to re-use the same buffer; for example:
byte[] buffer = new byte[MaxLength];
var segments = new List<ArraySegment<byte>>();
segments.Add(default); // placeholder
segments.Add(img);
foreach(...) {
string val = ...
int len = encoding.GetBytes(val, 0, val.Length, buffer, 0);
segments[0] = new ArraySegment<byte>(buffer, 0, len);
thisSocket.Send(segments);
}
However! to do this usually requires some kind of framing on the header - either a sentinel value (perhaps a trailing CR/LF/CRLF), or a prefix of the number of bytes that are the string - len here.
If that really isn't possible... just loop over the unused part of the array and set it to what you want, or use Array.Clear if zero is OK.

Why removig TextBox.AppendText() from my code changes socket communication here?

I have a simple client/server communication between C++ and C# where the C# program sends a string to the C++.
Sending of a string is done on 3 stages. Send the length of the length of the string--> Send the length of string --> Send the String.
For debugging purposes I have a TextBox called textbox1 on my C# program to print
the sent values using textbox1.AppendText() three times for the three values sent.
Everything was sent and received correctly, When I remove two of the three AppendText() lines from my code it still works but the strange thing is when I remove the third one (Commented as //<--This Line, The C++ server receives 0!
C# Client (Code Snippet):
private void button1_Click(object sender, EventArgs e)
{
try
{
MemoryStream ms;
NetworkStream ns;
TcpClient client;
BinaryWriter br;
byte[] tosend;
string AndroidId = "2468101214161820";
string len = AndroidId.Length.ToString();
string lol = len.Length.ToString();
ms = new MemoryStream();
client = new TcpClient("127.0.0.1", 8888);
ns = client.GetStream();
br = new BinaryWriter(ns);
//****************Send Length Of Length***************
tosend = System.Text.Encoding.ASCII.GetBytes(lol);
br.Write(tosend);
textBox1.AppendText(Encoding.ASCII.GetString(tosend));//<---THIS LINE
//****************Send Length***************
tosend = System.Text.Encoding.ASCII.GetBytes(len);
br.Write(tosend);
//****************Send Length Of Length***************
tosend = System.Text.Encoding.ASCII.GetBytes(AndroidId);
br.Write(tosend);
ns.Close();
client.Close();
}
C++ Server Code Snippet:
//***********Recieve Length Of Length*****************
char* lol_buff0 = new char[1];
int nullpoint= recv(s, lol_buff0, strlen(lol_buff0), 0);
lol_buff0[nullpoint] = '\0';
int lengthoflength = atoi(lol_buff0);
//***********Recieve Length*****************
char* l_buff0 = new char[lengthoflength];
int nullpoint2=recv(s, l_buff0, strlen(l_buff0), 0);
l_buff0[nullpoint2] = '\0';
int length = atoi(l_buff0);
//***********Recieve AndroidID*****************
char* AndroidID = new char[length];
valread0 = recv(s, AndroidID, strlen(AndroidID), 0);
if (valread0 == SOCKET_ERROR)
{
int error_code = WSAGetLastError();
if (error_code == WSAECONNRESET)
{
//Somebody disconnected , get his details and print
printf("Host disconnected unexpectedly , ip %s , port %d \n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
closesocket(s);
client_socket[i] = 0;
}
else
{
printf("recv failed with error code : %d", error_code);
}
}
if (valread0 == 0)
{
//Somebody disconnected , get his details and print
printf("Host disconnected , ip %s , port %d \n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
closesocket(s);
client_socket[i] = 0;
}
else
{
//add null character, if you want to use with printf/puts or other string handling functions
AndroidID[valread0] = '\0';
printf("%s:%d Your Android ID is - %s \n", inet_ntoa(address.sin_addr), ntohs(address.sin_port), AndroidID);
}
I know I can accommodate the TextBox as long as it works but It is so weird and I'd like to know what is the explanation for that. Thanks.
You're assuming that the data will be received in one recv call (or alternatively, that one send corresponds to one receive). That is a false assumption. You need to keep reading until you read length of bytes of data. TCP doesn't have any messaging built in, it only deals with streams.
Adding the line may mean that some small delay is added which makes the receive happen in a single call - it's hard to tell, since you're dealing with something that isn't quite deterministic. Handle TCP properly, and see if the problem persists.

String concat failed in tcp thread

This is my code
while (true)
{
byte[] btServerReceive = new byte[256];
TcpClient tcpclient = tcp.AcceptTcpClient();
NetworkStream ns = tcpclient.GetStream();
int intReceiveLength = ns.Read(btServerReceive, 0, btServerReceive.Length);
string recv = Encoding.GetEncoding("GB2312").GetString(btServerReceive) + "_01";
tcpclient.Close();
MessageBox.Show(recv.ToString());
// Create a new thread to handle the data associate with recv
Thread sendUpThread = new Thread(new ParameterizedThreadStart(SendThread));
sendUpThread.Start(recv);
}
The string recv only get the value of Encoding.GetEncoding("GB2312").GetString(btServerReceive), can't add "_01".
You can add the "_01". It's just that you don't notice, because the string is being displayed in a context where the embedded nulls prevent you from seeing it.
I.e. you've passed a 256-byte array to the GetString() method, where only the first N bytes have actually been modified and the rest still have their initial value of 0. So GetString() interprets those as '\0' characters and faithfully includes those in the returned string.
At a minimum, you would have to do something like this:
string recv = Encoding.GetEncoding("GB2312")
.GetString(btServerReceive, 0, intReceiveLength) + "_01";
I.e. take into account the number of bytes you actually received and only decode that many.
Now that said, even that doesn't solve your problem entirely. The above will probably work most of the time, but TCP could return to you only part of a whole string that was sent. Since you're using UTF8 encoding, some characters are represented by more than one byte and so of course the last byte in the data received could be only part of a character.
To fix this, you need to have some way to know when you're done reading a string (e.g. send null-terminated strings, send length-prefixed strings, fixed-length strings, etc.), or maintain a single instance of a Decoder which you use to decode the text as it comes in (Decoder maintains an internal buffer of incompletely decoded data so that on subsequent calls to decode text, it can correctly handle the partial character).

Mind boggling problem regarding RCON Protocol implementation in C#

I once again need your help figuring out this problem of mine...Been already a day and I can't seem to find out why this is happening in my code and output.
Ok.....so basically I am trying to implement the RCON Protocol of Valve in C#, so far I am getting the expected output given the code and sample usage below:
Usage:
RconExec(socket, "cvarlist");
Code:
private string RconExec(Socket sock, string command)
{
if (!sock.Connected) throw new Exception("Not connected");
//sock.DontFragment = true;
sock.ReceiveTimeout = 10000;
sock.SendTimeout = 10000;
//sock.Blocking = true;
Debug.WriteLine("Executing RCON Command: " + command);
byte[] rconCmdPacket = GetRconCmdPacket(command);
sock.Send(rconCmdPacket); //Send the request packet
sock.Send(GetRconCmdPacket("echo END")); //This is the last response to be received from the server to indicate the end of receiving process
RconPacket rconCmdResponsePacket = null;
string data = null;
StringBuilder cmdResponse = new StringBuilder();
RconPacket packet = null;
int totalBytesRead = 0;
do
{
byte[] buffer = new byte[4]; //Allocate buffer for the packet size field
int bytesReceived = sock.Receive(buffer); //Read the first 4 bytes to determine the packet size
int packetSize = BitConverter.ToInt32(buffer, 0); //Get the packet size
//Now proceed with the rest of the data
byte[] responseBuffer = new byte[packetSize];
//Receive more data from server
int bytesRead = sock.Receive(responseBuffer);
//Parse the packet by wrapping under RconPacket class
packet = new RconPacket(responseBuffer);
totalBytesRead += packet.String1.Length;
string response = packet.String1;
cmdResponse.Append(packet.String1);
Debug.WriteLine(response);
Thread.Sleep(50);
} while (!packet.String1.Substring(0,3).Equals("END"));
Debug.WriteLine("DONE..Exited the Loop");
Debug.WriteLine("Bytes Read: " + totalBytesRead + ", Buffer Length: " + cmdResponse.Length);
sock.Disconnect(true);
return "";
}
The Problem:
This is not yet the final code as I am just testing the output in the Debug window. There are a couple of issues occuring if I modify the code to it's actual state.
Removing Thread.Sleep(50)
If I remove Thread.Sleep(50), the output doesn't complete and ends up throwing an exception. I noticed the 'END' termination string is sent by the server pre-maturely. This string was expected to be sent by the server only when the whole list completes.
I tested this numerous times and same thing happens, if I don't remove the line, the list completes and function exits the loop properly.
Removing Debug.WriteLine(response); within the loop and outputting the string using Debug.WriteLine(cmdResponse.ToString()); outside the loop, only partial list data is displayed. If I compare the actual bytes read from the loop with the length of the StringBuilder instance, they're just the same? Click here for the output generated.
Why is this happening given the two scenarios mentioned above?
You are not considering that Socket.Receive very well could read fewer bytes than the length of the supplied buffer. The return value tells you the number of bytes that was actually read. I see that you are properly storing this value in a variable, but I cannot see any code that use it.
You should be prepared to make several calls to Receive to retrieve the entire package. In particular when you receive the package data.
I'm not sure that this is the reason for your problem. But it could be, since a short delay on the client side could be enough to fill the network buffers so that the entire package is read in a single call.
Try using the following code to retrieve package data:
int bufferPos = 0;
while (bufferPos < responseBuffer.Length)
{
bufferPos += socket.Receive(responseBuffer, bufferPos, responseBuffer.Length - bufferPos, SocketFlags.None);
}
Note: You should also support the case when the first call to Receive (the one where you receive the package's data length) doesn't return 4 bytes.

Categories