I'd like to start with the fact that I'm still learning C++ and some of the things still baffles me.
What I'm trying to accomplish is to build a byte stream to send over a socket. I'm trying to create a packet 1536 bytes in length for a handshake
std::stringstream s1Stream;
char randData[1528], zeroVal[4] = {0, 0, 0, 0};
memset(&randData, 1, sizeof(randData)); // Fill the buffer with data
s1Stream << timestampVal; // 4 bytes
s1Stream << zeroVal; // 4 bytes
s1Stream << randData; // 1528 bytes
When I convert s1Stream to string and check the size() of that string the program says that the size is 1541.
What am I doing wrong?
std::stringstream's operator<<(char const*), which is used here, treats its argument as zero-terminated C-style strings, and your randData array is not zero-terminated.
Since randData is not really a C-style string and looks like it could end up containing null bytes, the fix is to use
s1Stream.write(randData, sizeof(randData));
Note that this problem applies with zeroVal as well, except nothing of zeroVal will be written to s1Stream because it is zero-terminated at the first byte.
Related
I have a client and a server communicating using a named pipe.
I'm trying to pass the address stored by an LPCWSTR variable from the client to the server.
To do this, I first write the address onto a wchar_t buffer, then I send the server the size of that buffer (as a DWORD), so now the server knows how many bytes it has to read. I managed to send the buffer size successfully, I'm unable to send the complete string though.
Even though the server says it has read the required number of bytes, the buffer on the server side doesn't have the entire string.
Client:
wchar_t msgBuffer[1024];
LPCWSTR lpName = L"NameString";
_swprintf(msgBuffer, _T("%p\0"), lpName); //Write data to the buffer
DWORD nBytesToWrite = wcslen(msgBuffer); //Number of bytes to be written
bWriteFile = WriteFile( //Send the buffer size
hCreateFile,
&nBytesToWrite,
(DWORD)sizeof(nBytesToWrite),
&dwNoBytesWritten,
NULL
);
bWriteFile = WriteFile( //Send the data
hCreateFile,
msgBuffer,
(DWORD)wcslen(msgBuffer),
&dwNoBytesWritten,
NULL
);
Server:
DWORD dwBytesToRead = 0;
bReadFile = ReadFile( //Read the size of the next message
hCreateNamedPipe,
&dwBytesToRead,
sizeof(DWORD),
&dwNoBytesRead,
NULL);
std::cout << "\nBytes to be read: " << dwBytesToRead;
wchar_t msg[] = L"";
bReadFile = ReadFile( //Read the data
hCreateNamedPipe,
&msg,
dwBytesToRead,
&dwNoBytesRead,
NULL);
std::cout << "\nBytes Read: " << dwNoBytesRead;// << '\n' << msg;
wprintf(L"\nMessage: %s\nSize: %zu", msg, wcslen(msg));
This is what the output on the server side is:
Bytes to be read: 9
Bytes Read: 9
Message: 78E7
Size: 5
The address is 78E7325C on the client side, but my server only prints 78E7
Even though the server says to have read 9 bytes, the size of the resultant wchar_t is just 5, why is this?
EDIT: I've checked the buffer on the client side, it has the correct address stored. And is it okay to be sending the DWORD variable using the address-of (&) operator in WriteFile()?
The Solution
Changed (DWORD)wcslen(nBytesToWrite) to (DWORD)sizeof(nBytesToWrite)
wcslen gives the number of characters, whereas sizeof gives the number of bytes, and these aren't the same.
C-style strings are represented as pointers to a character array, with an implied length. The length is the number of characters in the array up to the first NUL character. When you interpret binary data as a C-style string (which your call to wprintf does), it stops writing characters once it finds the first character with a value of zero.
You are indeed able to read the entire message. The bug is that your code to verify this condition is based on a wrong assumption. You'll have to output dwNoBytesRead bytes in a loop, and cannot take advantage of the built-in string facilities of wprintf.
Besides that, you are reading into unallocated memory. wchar_t msg[] = L"" allocates an array of exactly one character, but you are reading into it, as if it were able to grow. That's not how things work in C. You'll need to familiarize yourself with the basics of the programming language you are using.
In addition, you are sending only half of your payload. WriteFile expects the number of bytes to write, but you are passing the return value of wcslen, i.e. the number of characters. On Windows, a wchar_t is 2 bytes wide.
I'm having a string is not null terminated error, though I'm not entirely sure why. The usage of std::string in the second part of the code is one of my attempt to fix this problem, although it still doesn't work.
My initial codes was just using the buffer and copy everything into client_id[]. The error than occurred. If the error is correct, that means I've got either client_ id OR theBuffer does not have a null terminator. I'm pretty sure client_id is fine, since I can see it in debug mode. Strange thing is buffer also has a null terminator. No idea what is wrong.
char * next_token1 = NULL;
char * theWholeMessage = &(inStream[3]);
theTarget = strtok_s(theWholeMessage, " ",&next_token1);
sendTalkPackets(next_token1, sizeof(next_token1) + 1, id_clientUse, (unsigned int)std::stoi(theTarget));
Inside sendTalkPackets is. I'm getting a string is not null terminated at the last line.
void ServerGame::sendTalkPackets(char * buffer, unsigned int buffersize, unsigned int theSender, unsigned int theReceiver)
{
std::string theMessage(buffer);
theMessage += "0";
const unsigned int packet_size = sizeof(Packet);
char packet_data[packet_size];
Packet packet;
packet.packet_type = TALK;
char client_id[MAX_MESSAGE_SIZE];
char theBuffer[MAX_MESSAGE_SIZE];
strcpy_s(theBuffer, theMessage.c_str());
//Quick hot fix for error "string not null terminated"
const char * test = theMessage.c_str();
sprintf_s(client_id, "User %s whispered: ", Usernames.find(theSender)->second.c_str());
printf("This is it %s ", buffer);
strcat_s(client_id, buffersize , theBuffer);
Methinks that problem lies in this line:
sendTalkPackets(next_token1, sizeof(next_token1) + 1, id_clientUse, (unsigned int)std::stoi(theTarget));
sizeof(next_token1)+1 will always gives 5 (on 32 bit platform) because it return size of pointer not size of char array.
One thing which could be causing this (or other problems): As
buffersize, you pass sizeof(next_token1) + 1. next_token1 is
a pointer, which will have a constant size of (typically) 4 or 8. You
almost certainly want strlen(next_token1) + 1. (Or maybe without the
+ 1; conventions for passing sizes like this generally only include
the '\0' if it is an output buffer. There are a couple of other
places where you're using sizeof, which may have similar problems.
But it would probably be better to redo the whole logic to use
std::string everywhere, rather than all of these C routines. No
worries about buffer sizes and '\0' terminators. (For protocol
buffers, I've also found std::vector<char> or std::vector<unsigned char>
quite useful. This was before the memory in std::string was
guaranteed to be contiguous, but even today, it seems to correspond more
closely to the abstraction I'm dealing with.)
You can't just do
std::string theMessage(buffer);
theMessage += "0";
This fails on two fronts:
The std::string constructor doesn't know where buffer ends, if buffer is not 0-terminated. So theMessage will potentially be garbage and include random stuff until some zero byte was found in the memory beyond the buffer.
Appending string "0" to theMessage doesn't help. What you want is to put a zero byte somewhere, not value 0x30 (which is the ascii code for displaying a zero).
The right way to approach this, is to poke a literal zero byte buffersize slots beyond the start of the buffer. You can't do that in buffer itself, because buffer may not be large enough to accomodate that extra zero byte. A possibility is:
char *newbuffer = malloc(buffersize + 1);
strncpy(newbuffer, buffer, buffersize);
newbuffer[buffersize] = 0; // literal zero value
Or you can construct a std::string, whichever you prefer.
I currently have the following code
char my_stream[800];
std::string my_string;
iResult = recv(clntSocket,my_stream,sizeof(my_stream),0);
my_string = std::string(my_stream);
Now when I attempt to convert the char array to string I get the present of weird characters in the string any suggestions on what I might be doing wrong
You're getting weird characters because your strings length is not equal to the number of bytes received.
You should initialize the string like so:
char* buffer = new char[512];
ssize_t bytesRead = recv(clntSocket,buffer,512,0);
std::string msgStr = std::string(buffer,bytesRead);
delete buffer;
The most common solution is to zero every byte of the buffer before reading anything.
char buffer[512];
buffer = { 0 };
If you're reading in a zero-terminated string from your socket, there's no need for a conversion, it's already a char string. If it's not zero-terminated already, you'll need some other kind of terminator because sockets are streams (assuming this is TCP). In other words, you don't need my_string = std::string(my_stream);
have you tried to print my_stream directly without converting to string.
According to me it may be the case of mismatch in format of data sent and received.
data on other side may be in other format like Unicode and you may be trying to print it as single byte array
if only part of string is in weird characters than it is definitely error related to null terminator at the end of my_stream missing tehn increase the size of array of my_stream.
I am doing a simple communication between sockets and here is my C++ code
while(1)
{
string buffer = "23,45\n";
const char* foo = buffer.c_str();
cout << "size of buffer is " << sizeof(buffer)<<endl;
send (s, foo, sizeof(buffer), 0);
}
weird thing is the fist iteration, the size of the buffer is 5 as expected, but since the second iteration and so on, the size dramatically jumped to 32. any idea why? Thank you very much. By the way, the added size comes from leading while spaces.
The sizeof operator returns the size (in bytes) of the object. It doesn't return the length of a container type. You need to use std::string::length() or std::string::size() to determine the length of the string.
I think you misunderstood the 5 of the first iteration: it must be coming from somewhere else. sizeof(buffer) is figured out at compile time - it is the size of std::string, so you should see 32 on every iteration.
If you are looking for the length of the string, use buffer.size() instead.
sizeof(buffer) tells you the size of your std::string object, which has nothing to do with the number of characters you store in it (which by the way are 7 in your example, not 5).
I'm trying to use istringstream to recreate an encoded wstring from some memory. The memory is laid out as follows:
1 byte to indicate the start of the wstring encoding. Arbitrarily this is '!'.
n bytes to store the character length of the string in text format, e.g. 0x31, 0x32, 0x33 would be "123", i.e. a 123-character string
1 byte separator (the space character)
n bytes which are the wchars which make up the string, where wchar_t's are 2-bytes each.
For example, the byte sequence:
21 36 20 66 00 6f 00 6f 00
is "!6 f.o.o." (using dots to represent char 0)
All I've got is a char* pointer (let's call it pData) to the start of the memory block with this encoded data in it. What's the 'best' way to consume the data to reconstruct the wstring ("foo"), and also move the pointer to the next byte past the end of the encoded data?
I was toying with using an istringstream to allow me to consume the prefix byte, the length of the string, and the separator. After that I can calculate how many bytes to read and use the stream's read() function to insert into a suitably-resized wstring. The problem is, how do I get this memory into the istringstream in the first place? I could try constructing a string first and then pass that into the istringstream, e.g.
std::string s((const char*)pData);
but that doesn't work because the string is truncated at the first null byte. Or, I could use the string's other constructor to explicitly state how many bytes to use:
std::string s((const char*)pData, len);
which works, but only if I know what len is beforehand. That's tricky given that the data is variable length.
This seems like a really solvable problem. Does my rookie status with strings and streams mean I'm overlooking an easy solution? Or am I barking up the wrong tree with the whole string approach?
Try setting your stringstream's rdbuf:
char* buffer = something;
std::stringbuf *pbuf;
std::stringstream ss;
std::pbuf=ss.rdbuf();
std::pbuf->sputn(buffer, bufferlength);
// use your ss
Edit: I see that this solution will have a similar problem to your string(char*, len) situation. Can you tell us more about your buffer object? If you don't know the length, and it isn't null terminated, it's going to be very hard to deal with.
Is it possible to modify how you encode the length, and make that a fixed size?
unsigned long size = 6; // known string length
char* buffer = new char[1 + sizeof(unsigned long) + 1 + size];
buffer[0] = '!';
memcpy(buffer+1, &size, sizeof(unsigned long));
buffer should hold the start indicator (1 byte), the actual size (size of unsigned long), the delimiter (1 byte) and the text itself (size).
This way, you could get the size "pretty" easy, then set the pointer to point beyond the overhead, and then use the len variable in the string constructor.
unsigned long len;
memcpy(&len, pData+1, sizeof(unsigned long)); // +1 to avoid the start indicator
// len now contains 6
char* actualData = pData + 1 + sizeof(unsigned long) + 1;
std::string s(actualData, len);
It's low level and error prone :) (for instance if you read anything that isn't encoded the way that you expect it to be, the len can get pretty big), but you avoid dynamically reading the length of the string.
It seems like something on this order should work:
std::wstring make_string(char const *input) {
if (*input != '!')
return "";
char length = *++input;
return std::wstring(++input, length);
}
The difficult part is dealing with the variable length of the size. Without something to specify the length it's hard to guess when to stop treating the data as specifying the length of the string.
As for moving the pointer, if you're going to do it inside a function, you'll need to pass a reference to the pointer, but otherwise it's a simple matter of adding the size you found to the pointer you received.
It's tempting to (ab)use the (deprecated but nevertheless standard) std::istrstream here:
// Maximum size to read is
// 1 for the exclamation mark
// Digits for the character count (digits10() + 1)
// 1 for the space
const std::streamsize max_size = 3 + std::numeric_limits<std::size_t>::digits10;
std::istrstream s(buf, max_size);
if (std::istream::traits_type::to_char_type(s.get()) != '!'){
throw "missing exclamation";
}
std::size_t size;
s >> size;
if (std::istream::traits_type::to_char_type(s.get()) != ' '){
throw "missing space";
}
std::wstring(reinterpret_cast<wchar_t*>(s.rdbuf()->str()), size/sizeof(wchar_t));