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).
Related
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.
After reading the TCP SSL example from MSDN, they use a byte array to read data into the stream. Is there a reason the array is limited to 2048? What if TCP sends a longer array than 2048? Also, how does the buffer.Length property continue to read the stream, as it is changing. It doesn't make complete sense to me. Why read the length of the buffer, wouldn't you want to read the length of the incremental bytes coming into the stream?
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the client.
// The client signals the end of the message using the
// "<EOF>" marker.
byte [] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
// Read the client's test message.
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer,0,bytes)];
decoder.GetChars(buffer, 0, bytes, chars,0);
messageData.Append (chars);
// Check for EOF or an empty message.
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes !=0);
return messageData.ToString();
}
When reading from any kind of Stream (not just SslStream) just because you asked for X bytes they are allowed to give you anywhere between 1 and X bytes back. that is what the bytes returned from Read is for, to see how many you read.
If you give Read a giant buffer it is only going to fill in the first few 1000ish bytes, giving it a bigger buffer is a waste of space. If you need more data than the buffer is large you make multiple requests for data.
A important thing to remember when dealing with network streams, one write on the sender does not mean one read on the receiver. Writes can be combined together or split apart at arbitrary points, that is why it is to have some form of Message Framing to be able to tell when you have "all the data". In the example you posted they use the marker <EOF> to signal "end of message".
There are two programs involved. The first one has a string like "##########". The second one is a config tool to find "##########" and replace this string with user input from a textbox.
Now I have trouble in the replacing part. Here is the code.
//This is code from first program:
string myIP = "####################";
string myPort = "%%%%%%%%";
int port = Int32.Parse(myIP );
tcpClient.Connect(myIP , port);
//This is code from second program:
//Get bytes from textbox:
byte[] byte_IP = new byte[60];
byte_IP = System.Text.Encoding.ASCII.GetBytes(textBox1_ip.Text);
//Get all bytes in the first program:
byte[] buffer = File.ReadAllBytes(#"before.exe");
//Replace string with textbox input, 0x1c00 is where the "#" starts:
Buffer.BlockCopy( byte_IP, 0, buffer, 0x1c00, byte_IP.Length);
//Build a new exe:
File.WriteAllBytes(#"after.exe", buffer);
However, I get "127.0.0.1#.#.#.#.#.#." in the new exe. But I need "1.2.7...0...0...1........." to process as a valid host.
First I'd like to reiterate what has already been said in the comments: there are simpler ways to handle this stuff. That's what config files are for, or registry settings.
But if you absolutely must...
First, you have to match the encoding that the framework expects. Is the string stored as UTF8? UTF16? ASCII? Writing data in the wrong encoding will turn it into pure garbage, almost every time. Generally for strings in code like you're looking for you'll be wanting to use Encoding.UNICODE.
Next, you need some way to deal with strings of different lengths. The buffer you define needs to be large enough to contain the widest string you want to be able to set - 15 bytes for dotted numeric IPv4 addresses - but you have to allow for the minimum of 7 characters. Padding the remainder and removing that padding before using the value will probably suffice.
The minimum program I could think to use for testing this was:
class Program
{
static void Main(string[] args)
{
var addr = "###.###.###.###".TrimEnd();
Console.WriteLine("Address: [{0}]", addr);
}
}
Now in your patcher you will need to locate the starting position in the file and overwrite the bytes with the new string's bytes. Here's a Patch method, which calls a FindString method that you will have to write yourself:
static void PatchFile(string filename, string searchString, string replaceString)
{
// Open the file
using (var file = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// Locate the search string in the file (needs to be implemented)
long pos = FindString(file, searchString);
if (pos < 0)
return;
// Pad and limit replacement string, then convert to bytes
string rep = string.Format("{0,-" + searchString.Length + "}", replaceString).Substring(0, searchString.Length);
byte[] replaceBytes = Encoding.Unicode.GetBytes(rep);
// Overwrite the located bytes with the replacement
file.Position = pos;
file.Write(replaceBytes, 0, replaceBytes.Length);
}
}
Hopefully it makes sense.
I am trying to read an email from POP3 and change to the correct encoding when I find the charset in the headers.
I use a TCP Client to connect to the POP3 server.
Below is my code :
public string ReadToEnd(POP3Client pop3client, out System.Text.Encoding messageEncoding)
{
messageEncoding = TCPStream.CurrentEncoding;
if (EOF)
return ("");
System.Text.StringBuilder sb = new System.Text.StringBuilder(m_bytetotal * 2);
string st = "";
string tmp;
do
{
tmp = TCPStream.ReadLine();
if (tmp == ".")
EOF = true;
else
sb.Append(tmp + "\r\n");
//st += tmp + "\r\n";
m_byteread += tmp.Length + 2; // CRLF discarded by read
FireReceived();
if (tmp.ToLower().Contains("content-type:") && tmp.ToLower().Contains("charset="))
{
try
{
string charSetFound = tmp.Substring(tmp.IndexOf("charset=") + "charset=".Length).Replace("\"", "").Replace(";", "");
var realEnc = System.Text.Encoding.GetEncoding(charSetFound);
if (realEnc != TCPStream.CurrentEncoding)
{
TCPStream = new StreamReader(pop3client.m_tcpClient.GetStream(), realEnc);
}
}
catch { }
}
} while (!EOF);
messageEncoding = TCPStream.CurrentEncoding;
return (sb.ToString());
}
If I remove this line:
TCPStream = new StreamReader(pop3client.m_tcpClient.GetStream(), realEnc);
Everything works fine except that when the e-mail contains different charset characters I get question marks as the initial encoding is ASCII.
Any suggestions on how to change the encoding while reading data from the Network Stream?
You're doing it wrong (tm).
Seriously, though, you are going about trying to solve this problem in completely the wrong way. Don't use a StreamReader for this. And especially don't read 1 byte at a time (as you said you needed to do in a comment on an earlier "solution").
For an explanation of why not to use a StreamReader, besides the obvious "because it isn't designed to switch between encodings during the process of reading", feel free to read over another answer I gave about the inefficiencies of using a StreamReader here: Reading an mbox file in C#
What you need to do is buffer your reads (such as a 4k buffer should be fine). Then, as you are already having to do anyway, scan for the '\n' byte to extract content on a line-by-line basis, combining header lines that were folded.
Each header may have multiple encoded-word tokens which may each be in a separate charset, assuming they are properly encoded, otherwise you'll have to deal with undeclared 8-bit data and try to massage that into unicode somehow (probably by having a set of fallback charsets). I'd recommend trying UTF-8 first followed by a selection of charsets that the user of your library has provided before finally trying iso-8859-1 (make sure not to try iso-8859-1 until you've tried everything else, because any sequence of 8-bit text will convert properly to unicode using the iso-8859-1 character encoding).
When you get to text content of the message, you'll want to check the Content-Type header for a charset parameter. If no charset parameter is defined, it should be US-ASCII, but in practice it could be anything. Even if the charset is defined, it might not match the actual character encoding used in the text body of the message, so once again you'll probably want to have a set of fallbacks.
As you've probably guessed by this point, this is very clearly not a trivial task as it requires the parser to do on-the-fly character conversion as it goes (and the character conversion requires internal parser state about what the expected charset is at any given time).
Since I've already done the work, you should really consider using MimeKit which will parse the email and properly do charset conversion on the headers and the content using the appropriate charset encoding.
I've also written a Pop3Client class that is included in my MailKit library.
If your goal is to learn and write your own library, I'd still highly recommend reading over my code because it is highly efficient and does things in a proper way.
There are some ways you can detect the encoding by looking at the Byte Order Mark, which are the firts few bytes of the stream. These will tell you the encoding. However, the stream might not have a BOM, and in these cases it could be ASCII, UTF without BOM, or others.
You can convert your stream from one encoding to another with the Encoding Class:
Encoding textEncoding = Encoding.[your detected encoding here];
byte[] converted = Encoding.UTF8.GetBytes(textEncoding.GetString(TCPStream.GetBuffer()));
You may select your preferred encoding when converting.
Hope it answers your question.
edit
You may use this code to read your stream in blocks.
MemoryStream st = new MemoryStream();
int numOfBytes = 1024;
int reads = 1;
while (reads > 0)
{
byte[] bytes = new byte[numOfBytes];
reads = yourStream.Read(bytes, 0, numOfBytes);
if (reads > 0)
{
int writes = ( reads < numOfBytes ? reads : numOfBytes);
st.Write(bytes, 0, writes);
}
}
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.