I'm about to write an IRCBot using Boost.Asio and I have the function getMsg:
std::string getMsg()
{
buffer.clear(); //make sure buffer is empty
buffer.resize(512); //make sure it's big enough for 512char
socket.read_some(boost::asio::buffer(&buffer[0],buffer.size()));
std::size_t pos = buffer.find("PING :");
if(pos != std::string::npos)
{
sendMsg("PONG :" + buffer.substr(pos + 6));
}
return buffer;
}
In my main function when using std::cout << Text; I get an output, but when trying std::cout << "Hello", nothing seems to happen:
while(true)
{
std::string Text = Test.getMsg();
std::cout << Text; //OUTPUT
}
while(true)
{
std::string Text = Test.getMsg();
std::cout << "TEST"; //NO OUTPUT ---- WHY?
}
The error you are asking about most likely occurs because you don't flush the stdout: std::cout << "TEST" << std::flush; This has nothing to do with boost::asio.
However your asio code also has a possible error: You are looking for PING : there in a single read call which might never be received within a single read call, due to the fact of how TCP works (it's a stream, not packets). If it's UDP socket it would work.
Related
I am encountering a problem when I am trying to write to sysfs node.
In the below code I am trying to wite to a trace_marker file. In the ftrace log, the first write is successful. But after that the write fails.
The file descriptor seemingly closes.
I do not want to open file every time before writing as writes are too frequent.
class Logger {
int mFileFd;
void logFromAnotherThread(std::string s) {
std::unique_lock<std::mutex> ul(mLogMu);
...
int count = write(mFileFd, s.c_str(), s.length());
if (count > 0)
std::cout << "Wrote n bytes: " << count << std::endl;
else
std::cout << "Errornum: " << strerror(errno) << std::endl;
...
}
Logger() {
mFileFd = open(SYSFS_NODE_WRITE, O_WRONLY);
....
}
}
First write is succesful.
I get output as-
Errornum: Bad file descriptor
My expectation is file open should be once, file descrtiptor should remain open for entire duration, and close on exit.
Edit 1:
Thank you for the suggestions on object getting destroyed. But I ensure that object is not getting destroyed.
For debugging, I had removed class/structure. Logging is now in simple C++ function calls. The file descriptor is a global variable, initialized once in main.
It does not works.
My confusion was is it something to do with the way write operations are performed on sysfs node.
Or can this be because of the number of writes are high (about 2-3 logs in 10us).
I am doing this like below, but this has an overhead of two added system calls.
#define TRACE_MARKER_FILE "/sys/kernel/debug/tracing/trace_marker"
void logdata(pid_t tid, std::string mystring) {
if(useLogger) {
std::stringstream ss;
if (funcname.length() > 0)
ss << LOGTAG << mystring;
int tempfd = open(TRACE_MARKER_FILE, O_WRONLY);
int count = write(tempfd, ss.str().c_str(), ss.str().length());
if (count == 0) {
std::cout << "Errornum: " << strerror(errno) <<std::endl;
}
close(tempfd);
}
}
I would suggest using std::ofstream which allows for proper RAII, in other words it will open the file in your constructor, then will automatically close the file in your destructor
class Logger {
std::ofstream mFile;
void logFromAnotherThread(std::string s) {
std::unique_lock<std::mutex> ul(mLogMu);
...
mFile << s;
...
}
Logger() : mFile(SYSFS_NODE_WRITE, O_WRONLY) {
....
}
}
I am trying to setup a tcp socketing server with boost. For some reason the function read_until seems to be stopping at white space rather then going all the way until the delimiter.
for example:
sent
┼N▀]»ü rx}q╠Cä≥è┘Y\║ï2æ╨╬ΣfV╠παÇ/S┬0è3à ╫VR∞{εoÆ?LeN≡╬.lÖnÖ1⌡&âm&ù╫ä╛'°≈L▀_ °çF¿P»2ß|╪+96#3kα≥
╟¬─╣╩í▄¢hú╤fûº╢5~AcbF┌Zd╒∞?╓)a.ƒ¿B■αZº=■uΣ╔nÜ┌╬▌╝>┌iE┌y≈ÿ≤┴Kå ²å£∩¢R>╒S(y╙cPjA▀▀Z2O╓? ÆÉ#τß╢ªy╗▒*Γ▓σ&K₧#╦╩∙⌠%ßΩ-x*Ü╞7ε_█zâ╡C
╧╩║╗Q■═TM╠<æ┤päi^▓'àiUóα<«3Çÿ ─╗E Σ]ππa╒εk»╣╕(╔╡╙ä╝y≡╥¡╠▌╪┼¡Ö
a|MC├₧\y╚üßσ√⌡ÿ±2<æq}ÿ┌Mzçα∩òΣÆ{end}
recived:
┼N▀]»ü
My code that reads from the socket is
string read_(tcp::socket& socket, CryptoPP::RSA::PrivateKey *privateKey) {
boost::asio::streambuf buf;
boost::asio::read_until(socket, buf, "{end}");
string data = boost::asio::buffer_cast<const char*>(buf.data());
std::cout << "recived:\n" << data << std::endl;
return data
}
solution:
string read_(tcp::socket& socket, CryptoPP::RSA::PrivateKey *privateKey) {
boost::asio::streambuf buf;
boost::asio::read_until(socket, buf, "{end}");
streambuf::const_buffers_type buf2 = buf.data();
string data(buffers_begin(buf2), buffers_begin(buf2) + buf.size());
std::cout << "recived:\n" << data << std::endl;
return data
}
You should output the characters in a safe way, e.g. by printing each byte as 2 hex digits:
auto read_(tcp::socket& socket, CryptoPP::RSA::PrivateKey * /*privateKey*/) {
std::vector<uint8_t> data;
boost::asio::read_until(
socket,
boost::asio::dynamic_buffer(data),
"{end}");
std::cout << "received:\n";
for (int ch : data) {
std::cout << " " << std::hex << std::setw(2) << std::setfill('0') << ch;
}
return data;
}
Note that I elected to:
omit the intermediate streambuf
used the opportunity to demonstrate that you can easily use more apt data structures (std::vector<uint8_t>), although you can, of course, replace that with std::string
Salutations fellow programmers,
I am trying to write a program that allows you input what you want and the program will send your input to the server.
At the moment, my goal is sending HTTP requests to a web page. It connects fine. But when the while loop runs in immediately sends something through the cin.getline procedure without me inputting anything. I thought this was weird but it seemed to be work anyway.
Every time I send something like: "GET / HTTP/1.1\r\n\r\n" it will return the correct thing, but anything else I input, like "OPTIONS" returns the source code + "application blocked" (I am at school so it makes sense).
So, I connected to hotspot shield VPN and tested the application, but to my horror when I input something to send it returns nothing.
I searched through stack overflow and google but I haven't been able to find anything so far; probably because I'm searching for the wrong solutions to the problem.
Anyway, if you have time, please scan through the code send some help. It could just be a VPN and school issue and I could try at home if the code seems to be working for you, so just let me know.
SPECIFIC OUTLINE OF PROBLEM:
When I use this outside the school network nothing is returned and the while loop doesn't seem to execute. I can connect but the program seems to be in an endless time-out or something.
cout << "Connected to " << hostName << endl;
while (true) {
cout << ">";
cin.getline(sendBuf, sizeof(sendBuf));
string s(sendBuf);
cout << s.c_str() << endl;
send(connectSocket, s.c_str(), sizeof(s.c_str()), 0);
int rec = recv(connectSocket, recvBuf, sizeof(recvBuf), 0);
if (rec > 0) {
cout << recvBuf << endl;
}
else if (rec <= 0) {
cout << "nothing" << endl;
}
}
system("pause");
}
system("pause");
}
my goal is sending HTTP requests to a web page
The code you showed does not attempt to implement any semblance of the HTTP protocol, not even close.
For one thing, if you look at your own example more carefully, you will see that the GET request (which BTW, is missing a required Host header, due to your use of HTTP 1.1) contains 2 line breaks, but cin.getline() (why not std::getline()?) reads only 1 line at a time. So, you read in one line, send it, and wait for a response that doesn't arrive since you didn't finish sending a complete request yet. That would explain why your while loop is hanging.
If you want the user to type in a complete HTTP request and then you send it as-is, you have to read in the ENTIRE request from the user, and then send it entirely to the server, before you can then attempt to receive the server's response. That means you have to handle line breaks between individual message headers, handle the terminating line break that separates the message headers from the message body, and detect the end of the body data.
I would suggest not relying on the user typing in a complete HTTP request as-is. I suggest you prompt the user for relevant pieces and let the user type normal text, and then your code can format that text into a proper HTTP request as needed.
When you are reading the server's response, you can't just blindly read arbitrary chunks of data. You have to process what you read, per the rules of the HTTP protocol. This is particularly important in order to determine when you have reached the end of the response and need to stop reading. The end of the response can be signaled in one of many different ways, as outlined in RFC 2616 Section 4.4 Message Length.
You are also making some common newbie mistakes in your TCP handling in general. TCP is a streaming transport, you are not taking into account that send() and recv() can sent/receive fewer bytes than requested. Or that recv() does not return null-terminated data.
With that said, try something like this:
void sendAll(SOCKET sckt, const void *buf, int buflen)
{
// send all bytes until buflen has been sent,
// or an error occurs...
const char *pbuf = static_cast<const char*>(buf);
while (buflen > 0)
{
int numSent = send(sckt, pbuf, buflen, 0);
if (numSent < 0) {
std::ostringstream errMsg;
errMsg << "Error sending to socket: " << WSAGetLastError();
throw std::runtime_error(errMsg.str());
}
pbuf += numSent;
buflen -= numSent;
}
}
int readSome(SOCKET sckt, void *buf, int buflen)
{
// read as many bytes as possible until buflen has been received,
// the socket is disconnected, or an error occurs...
char *pbuf = static_cast<char*>(buf);
int total = 0;
while (buflen > 0)
{
int numRecvd = recv(sckt, pbuf, buflen, 0);
if (numRecvd < 0) {
std::ostringstream errMsg;
errMsg << "Error receiving from socket: " << WSAGetLastError();
throw std::runtime_error(errMsg.str());
}
if (numRecvd == 0) break;
pbuf += numRecvd;
buflen -= numRecvd;
total += numRecvd;
}
return total;
}
void readAll(SOCKET sckt, void *buf, int buflen)
{
// read all bytes until buflen has been received,
// or an error occurs...
if (readSome(sckt, buf, buflen) != buflen)
throw std::runtime_error("Socket disconnected unexpectedly");
}
std::string readLine(SOCKET sckt)
{
// read a line of characters until a line break is received...
std::string line;
char c;
do
{
readAll(sckt, &c, 1);
if (c == '\r')
{
readAll(sckt, &c, 1);
if (c == '\n') break;
line.push_back('\r');
}
else if (c == '\n') {
break;
}
line.push_back(c);
}
while (true);
return line;
}
...
inline void ltrim(std::string &s) {
// erase whitespace on the left side...
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
return !std::isspace(ch);
}));
}
inline void rtrim(std::string &s) {
// erase whitespace on the right side...
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
return !std::isspace(ch);
}).base(), s.end());
}
inline void trim(std::string &s) {
// erase whitespace on both sides...
ltrim(s);
rtrim(s);
}
inline void upperCase(std::string &s)
{
// translate all characters to upper-case...
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
}
...
std::string makeRequest(const std::string &host, const std::string &method, const std::string &resource, const std::vector<std::string> &extraHeaders, const void *body, int bodyLength)
{
std::ostringstream oss;
oss << method << " " << resource << " HTTP/1.1\r\n";
oss << "Host: " << host << "\r\n";
oss << "Content-Length: " << bodyLength << "\r\n";
for(auto &hdr : extraHeaders)
{
// TODO: ignore Host and Content-Length...
oss << hdr << "\r\n";
}
oss << "\r\n";
oss.write(static_cast<const char*>(body), bodyLength);
return oss.str();
}
bool getHeaderValue(const std::vector<std::string> &headers, const std::string &headerName, std::string &value)
{
value.clear();
std::string toFind = headerName;
upperCase(toFind);
// find the requested header by name...
for(auto &s : headers)
{
std::string::size_type pos = s.find(':');
if (pos != std::string::npos)
{
std::string name = s.substr(0, pos-1);
trim(name);
upperCase(name);
if (name == toFind)
{
// now return its value...
value = s.substr(pos+1);
trim(value);
return true;
}
}
}
// name not found
return false;
}
...
std::cout << "Connected to " << hostName << std::endl;
try
{
std::string method, resource, hdr, data;
std::string status, version, reason;
std::vector<std::string> headers;
int statusCode, rec;
do
{
headers.clear();
data.clear();
// get user input
std::cout << "Method > " << std::flush;
if (!std::getline(std::cin, method))
throw std::runtime_error("Error reading from stdin");
upperCase(method);
std::cout << "Resource > " << std::flush;
if (!std::getline(std::cin, resource))
throw std::runtime_error("Error reading from stdin");
std::cout << "Extra Headers > " << std::flush;
while (std::getline(std::cin, hdr) && !hdr.empty())
headers.push_back(hdr);
if (!std::cin)
throw std::runtime_error("Error reading from stdin");
std::cout << "Data > " << std::flush;
// use Ctrl-Z or Ctrl-D to end the data, depending on platform...
std::ios_base::fmtflags flags = std::cin.flags();
std::cin >> std::noskipws;
std::copy(std::istream_iterator<char>(std::cin), std::istream_iterator<char>(), std::back_inserter(data));
if (!std::cin)
throw std::runtime_error("Error reading from stdin");
std::cin.flags(flags);
std::cin.clear();
// send request
std::string request = makeRequest(hostName, method, resource, headers, data.c_str(), data.length());
std::cout << "Sending request: << std::endl << request << std::endl;
// TODO: reconnect to hostName if previous request disconnected...
sendAll(connectSocket, request.c_str(), request.length());
// receive response
headers.clear();
data.clear();
// read the status line and parse it...
status = readLine(connectSocket);
std::cout << status << std::endl;
std::getline(std::istringstream(status) >> version >> statusCode, reason);
upperCase(version);
// read the headers...
do
{
hdr = readLine(connectSocket);
std::cout << hdr << std::endl;
if (hdr.empty()) break;
headers.push_back(hdr);
}
while (true);
// The transfer-length of a message is the length of the message-body as
// it appears in the message; that is, after any transfer-codings have
// been applied. When a message-body is included with a message, the
// transfer-length of that body is determined by one of the following
// (in order of precedence):
// 1. Any response message which "MUST NOT" include a message-body (such
// as the 1xx, 204, and 304 responses and any response to a HEAD
// request) is always terminated by the first empty line after the
// header fields, regardless of the entity-header fields present in
// the message.
if (((statusCode / 100) != 1) &&
(statusCode != 204) &&
(statusCode != 304) &&
(method != "HEAD"))
{
// 2. If a Transfer-Encoding header field (section 14.41) is present and
// has any value other than "identity", then the transfer-length is
// defined by use of the "chunked" transfer-coding (section 3.6),
// unless the message is terminated by closing the connection.
if (getHeaderValue(headers, "Transfer-Encoding", hdr))
upperCase(hdr);
if (!hdr.empty() && (hdr != "IDENTITY"))
{
std::string chunk;
std::string::size_type oldSize, size;
do
{
chunk = readLine(connectSocket);
std::istringstream(chunk) >> std::hex >> size;
if (size == 0) break;
oldSize = data.size();
chunkData.resize(oldSize + size);
readAll(connectSocket, &data[oldSize], size);
std::cout.write(&data[oldSize], size);
readLine(connectSocket);
}
while (true);
std::cout << std::endl;
do
{
hdr = readLine(connectSocket);
std::cout << hdr << std::endl;
if (hdr.empty()) break;
headers.push_back(hdr);
}
while (true);
}
// 3. If a Content-Length header field (section 14.13) is present, its
// decimal value in OCTETs represents both the entity-length and the
// transfer-length. The Content-Length header field MUST NOT be sent
// if these two lengths are different (i.e., if a Transfer-Encoding
// header field is present). If a message is received with both a
// Transfer-Encoding header field and a Content-Length header field,
// the latter MUST be ignored.
else if (getHeaderValue(headers, "Content-Length", hdr))
{
std::string::size_type size;
if ((std::istringstream(hdr) >> size) && (size > 0))
{
data.resize(size);
readAll(connectSock, &data[0], size);
std::cout << data;
}
}
// 4. If the message uses the media type "multipart/byteranges", and the
// transfer-length is not otherwise specified, then this self-
// delimiting media type defines the transfer-length. This media type
// MUST NOT be used unless the sender knows that the recipient can parse
// it; the presence in a request of a Range header with multiple byte-
// range specifiers from a 1.1 client implies that the client can parse
// multipart/byteranges responses.
else if (getHeaderValue(headers, "Content-Type", hdr) &&
(hdr.compare(0, 10, "multipart/") == 0))
{
// TODO: extract 'boundary' attribute and read from
// socket until the terminating boundary is reached...
}
// 5. By the server closing the connection.
else
{
do
{
rec = readSome(connectSocket, recvBuf, sizeof(recvBuf));
if (rec == 0) break;
data.append(recvBuf, rec);
std::cout.write(recvBuf, rec);
}
while (rec == sizeof(recvBuf));
}
}
std::cout << std::endl;
// use status, headers, and data as needed ...
getHeaderValue(headers, "Connection", hdr);
upperCase(hdr);
if (version == "HTTP/1.0")
{
if (hdr != "KEEP-ALIVE")
break;
}
else
{
if (hdr == "CLOSE")
break;
}
}
while (true);
}
catch (const std::exception &e)
{
std::cerr << e.what() << std::endl;
}
closesocket(connectSocket);
std::cout << "Disconnected from " << hostName << std::endl;
std::system("pause");
Isn't HTTP fun? :-) This is, by far, not a complete HTTP implementation, but it should get you started. However, as you can see, HTTP can be quite complex to implement from scratch, and it has many rules and restrictions that you have to follow. You are better off not implementing HTTP manually at all. There are plenty of 3rd party HTTP libraries that are available for C++. Use one of them instead, and let them handle the hard work for you, so you can focus on your own business logic.
I managed to get the wpa_supplicant C API to work. But it behaves completly different each time I restart my Program.
The Connection succeeds every time. But then the troubles begin:
Sometimes SCAN replies an empty String but returns 0 (Ok).
In another run it replies "OK\n" and returns 0. When I loop and wait for an return of 0 and a "OK\n"-reply it runs forever with an empty reply and a 0 return.
In rare cases when SCAN returns 0 and replies "OK\n" I move on and wait for SCAN_RESULTS to return 0. At this point it behaves completely random. Sometimes it replies the whole Scan-Results. Sometimes it replies nothing but return 0 and the Scan-results are in my Event-Pipeline.
Or like in most cases: It returns 0 but does nothing. No reply, no Events. Nothing.
For debugging I reduced my Code to this snippet and try to figure out whats wrong. Im done, tried everything and I am somewhat frustrated with the Documentation of the ctrl-interface which doesn't define any workflow or tips. Im sick of reverse engineering the wpa_cli.c to figure out their flow.
I have to attach that mostly the first PING works well. Every other PING results in empty Strings.
/* some includes */
wpa_ctrl* _wpac;
static void callback(char* rply, size_t rplylen){
std::cout << std::string(rply,rplylen) << std::endl;
}
bool ScanResults() {
if(_wpac)
{
char rply[4096]; //same as in wpa_cli.c
size_t rplylen;
int retval = wpa_ctrl_request(_wpac,"SCAN_RESULTS",12,rply,&rplylen,callback);
if(retval == 0) {
std::string rplystring = std::string(rply,rplylen);
std::string message = std::string("wpa_ctrl(SCAN_RESULTS) replied: '").append(rplystring).append("' (").append(std::to_string(retval)).append(")");
std::cout << message << std::cout;
std::cout << std::string("wpa_ctrl(SCAN_RESULTS): Available (").append(std::to_string(retval)).append(")") << std::endl;
return true;
}
else
std::cout << std::string("wpa_ctrl(SCAN_RESULTS): Unavailable (").append(std::to_string(retval)).append(")") << std::endl;
return false;
}
return false;
}
bool InitScan() {
if(_wpac)
{
char rply[4096]; //same as in wpa_cli.c
size_t rplylen;
int retval = wpa_ctrl_request(_wpac,"SCAN",4,rply,&rplylen,callback);
if(retval == 0) {
std::string rplystring = std::string(rply,rplylen);
std::string message = std::string("wpa_ctrl(SCAN) replied: '").append(rplystring).append("' (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
if(rplystring == "OK\n") {
std::string message = std::string("wpa_ctrl(SCAN): Scan initiated (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
return true;
}
}
std::string message = std::string("wpa_ctrl(SCAN) failed: (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
}
return false;
}
int main(){
std::string connection_string = std::string("/var/run/wpa_supplicant/").append(_interface);
wpa_ctrl* _wpac = wpa_ctrl_open(connection_string.c_str());
if(!_wpac)
return 1;
/* Well Working Attach to as Eventlistener omitted */
while(!InitScan())
sleep(1);
while(!ScanResults())
sleep(1)
return 0;
}
Try doing something like this in the appropriate places in your code
char rply[4096];
size_t rplylen = sizeof(rply);
static char cmd[] = "SCAN"; //maybe a bit easier to deal with since you need a command length
int retval = wpa_ctrl_request(_wpac, cmd, sizeof(cmd)-1, rply, &rplylen, NULL);
NULL, because I suspect you really don't need a callback routine. But put one in if you want to.
I've been learning sockets, and I have created a basic server where you can telnet into and type messages, then press enter and the message is printed on the server.
Since it's telnet, every key press gets sent to the server. So I basically hold all sent bytes in a buffer, and then when a carriage return ("\r\n") is received, I discard that, and print out the clients current buffer. Then I clear the clients buffer.
My problem is that every once in a while (and I'm not quite sure how to replicate it), the first "line" of data I send in gets an extra space tacked onto each character. For example, I'll type "Test" on the telnet client, but my server will receive it as "T e s t ". I always clear the receiving buffer before receiving any data. One obvious solution is just to remove all spaces serverside, but then that messes up my ability to send more than one word. Is this just an issue with my telnet, or is there something I can do on the server to fix this?
I am using the WinSock2 API and Windows 10 Telnet.
EDIT:
I have checked the hex value of the extra character, and it is 0x20.
EDIT:
Here is the code that receives and handles the incoming telnet data.
// This client is trying to send some data to us
memset(receiveBuffer, sizeof(receiveBuffer), 0);
int receivedBytes = recv(client->socket, receiveBuffer, sizeof(receiveBuffer), 0);
if (receivedBytes == SOCKET_ERROR)
{
FD_CLR(client->socket, &masterFDSet);
std::cerr << "Error! recv(): " << WSAGetLastError() << std::endl;
closesocket(client->socket);
client->isDisconnected = true;
continue;
}
else if (receivedBytes == 0)
{
FD_CLR(client->socket, &masterFDSet);
std::cout << "Socket " << client->socket << " was closed by the client." << std::endl;
closesocket(client->socket);
client->isDisconnected = true;
continue;
}
// Print out the hex value of the incoming data, for debug purposes
const int siz_ar = strlen(receiveBuffer);
for (int i = 0; i < siz_ar; i++)
{
std::cout << std::hex << (int)receiveBuffer[i] << " " << std::dec;
}
std::cout << std::endl;
std::string stringCRLF = "\r\n"; // Carraige return representation
std::string stringBS = "\b"; // Backspace representation
std::string commandBuffer = receiveBuffer;
if (commandBuffer.find(stringCRLF) != std::string::npos)
{
// New line detected. Process message.
ProcessClientMessage(client);
}
else if (commandBuffer.find(stringBS) != std::string::npos)
{
// Backspace detected,
int size = strlen(client->dataBuffer);
client->dataBuffer[size - 1] = '\0';
}
else
{
// Strip any extra dumb characters that might have found their way in there
commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\r'), commandBuffer.end());
commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\n'), commandBuffer.end());
// Add the new data to the clients data buffer
strcat_s(client->dataBuffer, sizeof(client->dataBuffer), commandBuffer.c_str());
}
std::cout << "length of data buffer is " << strlen(client->dataBuffer) << std::endl;
You have two major problems.
First, you have a variable, receivedBytes that knows the number of bytes you received. Why then do you call strlen? You have no guarantee that the data you received is a C-style string. It could, for example, contain embedded zero bytes. Do not call strlen on it.
Second, you check the data you just received for a \r\n, rather than the full receive buffer. And you receive data into the beginning of the receive buffer, not the first unused space in it. As a result, if one call to recv gets the \r and the next gets the \n, your code will do the wrong thing.
You never actually wrote code to receive a message. You never actually created a message buffer to hold the received message.
Your code, my comments:
memset(receiveBuffer, sizeof(receiveBuffer), 0);
You don't need this. You shouldn't need this. If you do there is a bug later in your code.
int receivedBytes = recv(client->socket, receiveBuffer, sizeof(receiveBuffer), 0);
if (receivedBytes == SOCKET_ERROR)
{
FD_CLR(client->socket, &masterFDSet);
std::cerr << "Error! recv(): " << WSAGetLastError() << std::endl;
closesocket(client->socket);
client->isDisconnected = true;
continue;
You mean 'break'. You got an error. You closed the socket. There is nothing to continue.
}
else if (receivedBytes == 0)
{
FD_CLR(client->socket, &masterFDSet);
std::cout << "Socket " << client->socket << " was closed by the client." << std::endl;
closesocket(client->socket);
client->isDisconnected = true;
continue;
Ditto. You mean 'break'. You got an error. You closed the socket. There is nothing to continue.
}
// Print out the hex value of the incoming data, for debug purposes
const int siz_ar = strlen(receiveBuffer);
Bzzzzzzzzzzzzt. There is no guarantee there is a null anywhere in the buffer. You don't need this variable. The correct value is already present, in receivedBytes.
for (int i = 0; i < siz_ar; i++)
That should be `for (int i = 0; i < receivedBytes; i++)
{
std::cout << std::hex << (int)receiveBuffer[i] << " " << std::dec;
}
std::cout << std::endl;
std::string stringCRLF = "\r\n"; // Carraige return representation
No. That is a carriage return (\r) followed by a line feed (\n), often called CRLF as indeed you have yourself in the variable name. This is the standard line terminator in Telnet.
std::string stringBS = "\b"; // Backspace representation
std::string commandBuffer = receiveBuffer;
Bzzt. This copy should be length-delimited by receivedBytes.
if (commandBuffer.find(stringCRLF) != std::string::npos)
As noted by #DavidShwartz you can't assume you got the CR and the LF in the same buffer.
{
// New line detected. Process message.
ProcessClientMessage(client);
}
else if (commandBuffer.find(stringBS) != std::string::npos)
{
// Backspace detected,
int size = strlen(client->dataBuffer);
client->dataBuffer[size - 1] = '\0';
This doesn't make any sense. You are using strlen() to tell you where the trailing null is, and then you're putting a null there. You also have the problem that there may not be a trailing null. In any case what you should be doing is removing the backspace and the character before it, which requires different code. You're also operating on the wrong data buffer.
}
else
{
// Strip any extra dumb characters that might have found their way in there
commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\r'), commandBuffer.end());
commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\n'), commandBuffer.end());
// Add the new data to the clients data buffer
strcat_s(client->dataBuffer, sizeof(client->dataBuffer), commandBuffer.c_str());
}