Own C++ network protocol: How to define unique binary telegram values - c++

I want to exchange data over TCP/IP with C++ by writing my own small binary protocol. My idea was to implement the protocol by defining values in form of the following bytes:
BEGIN
LENGTH
FUNCTIONCODE
[data bytes] (optional)
CRC
END
How can I define a binary value for "BEGIN" that is unique allowing the receiver to recognize the beginning of a new telegram? E.g. if I do that:
static const int BEGIN = 0x41;
and the optional data bytes randomly include also the 0x41 value, this might be a problem for the receiver. Or am I wrong? If not, how may I define a unique BEGIN and END value?

I usually write something like this for simple C++ networking. (But I wrote this code from scratch this time and haven't tested it at all!)
class Packet : public std::string {
public:
struct __attibute__((__packed__)) Header {
static const int BEGIN = 0x41;
uint32_t begin;
uint16_t length;
uint16_t funcode;
void init(uint16_t len) { begin = BEGIN; length = len; }
bool verify() { return (BEGIN == begin); }
char *buffer() { return reinterpret_cast<char *>(this); }
};
class __attibute__((__packed__)) Footer {
uint32_t crc;
char term;
char newline;
void init() { term = ';'; newline = '\n'; }
bool verify() { return (';' == term && '\n' == newline); }
char *buffer() { return reinterpret_cast<char *>(this); }
};
public:
void init(size_t n = 0) {
resize(sizeof(Header) + n + sizeof(Footer));
Header * const h = header();
h->init(n);
Footer * const f = footer();
f->init();
}
// these two are valid only after init()
Header *header() { return reinterpret_cast<Header*>(&(*this)[0]); }
Footer *footer() { return reinterpret_cast<Footer*>(&(*this)[size() - sizeof(Footer)]); }
template <typename T>
T *buffer() { return reinterpret_cast<T *>(&(*this)[sizeof(Header)]); }
void extend(size_t n) { resize(size() + n); }
};
int sendMessage(int funcode) {
Packet p;
switch (funcode) {
case TIME: {
timeval tv;
gettimeofday(&tv, NULL);
p.init(sizeof tv);
Packet::Header * const h = p.header();
h->funcode = funcode;
timeval * const dst = p.buffer<timeval>();
*dst = tv;
} break;
case PID: {
const pid_t pid = getpid();
p.init(sizeof pid);
Packet::Header * const h = p.header();
h->funcode = funcode;
pid_t * const dst = p.buffer<pid_t>();
*dst = pid;
} break;
...
}
Packet::Footer * const f = p.footer();
f->crc = ...;
const ssize_t nSent = send(sock, p.data(), p.size(), 0);
...
}
Packet receiveMessage() {
Packet ret;
ret.init();
Packet::Header * const h = ret.header();
ssize_t nRecv = recv(sock, h->buffer(), sizeof *h, 0);
...
if ( ! h->verify()) ...
p.extend(h->length);
nRecv = recv(sock, p.buffer<char>(), h->length, 0);
switch (h->funcode) {
case TIME: {
timeval tv = *p->buffer<timeval>();
tm theTm;
localtime_r(&tv.tv_sec, &theTm);
char buf[128];
strftime(buf, sizeof buf, "%x %X", &tv);
cout << "Server time == " << buf << endl;
} break;
case PID: {
const pid_t pid = *p.buffer<pid_t>();
cout << "Server PID == " << pid << endl;
} break;
...
}
Packet::Footer * const f = ret.footer();
nRecv = recv(sock, f->buffer(), sizeof *f, 0);
if ( ! f->verify() || f->crc != ...) ...
return ret; // inefficient data copy, only for a sample code
}
I recommend you to define your BEGIN code as something like 0x48454c4f which can be read as ASCII "HELO" rather than a random int like 0x41. And I added two extra bytes ';' and '\n'to the footer; you will find them useful when you dump packet capture of your protocol to the console. Remember, you can always use sed, perl, python or whatever (but not grep; it won't accept arbitrary hex search strings) to analyse arbitrary binary data including your packet dump. If you design your protocol well, your debugging will be much easier. (First of all, you should consider using ASCII protocol rather than binary one.)

Frequently a protocol definition goes like this:
FUNCTIONCODE
LENGTH
DATA
CRC
and there's no need to fish for "BEGIN". Reading proceeds by reading FUNCTIONCODE and LENGTH using a fixed length, and then the data + CRC, according to LENGTH, and then, again...
However, if you think that the additional "BEGIN" marker helps you, select "BEGIN" as a value that's not a FUNCTIONCODE, and then you have
BEGIN
FUNCTIONCODE
LENGTH
DATA
CRC
And for every byte after BEGIN equal to BEGIN send BEGIN twice. But, in this case, reading is much more complicated...

Related

Why is my driver only reading part of the string?

I'm attempting to read a string from a dummy program with a kernel driver. But only the first 4 char's are being read, I can't figure out why.
Part of the IOCTL code for reading the string:
else if (ControlCode = IO_READ_STRING_REQUEST)
{
PREAD_REQUEST Values = (PREAD_REQUEST)buffer;
PREAD_REQUEST ValuesOutput = (PREAD_REQUEST)buffer;
PEPROCESS process;
if (NT_SUCCESS(PsLookupProcessByProcessId(PID, &process)))
{
KeReadProcessMemory(process, Values->Address, &ValuesOutput->buffer, Values->Size);
DbgPrint((PCSTR)Values->buffer);
status = STATUS_SUCCESS;
}
else
status = STATUS_INVALID_PARAMETER;
BytesIO = sizeof(READ_REQUEST);
}
This is the read struct:
typedef struct ReadStruct
{
ULONGLONG Address;
ULONGLONG Response;
ULONGLONG Size;
char buffer[128];
} READ_REQUEST, *PREAD_REQUEST;
The DbgPrint always prints stri when it's supposed to print stringChar, and stri is returned to the usermode.
This is how it's called from usermode:
void ReadString(std::string *string, DWORD64 address)
{
ReadValues Values;
std::memset(Values.buffer, '\0', 128);
Values.Address = address;
Values.Response = 0;
Values.Size = sizeof(128);
if (!(DeviceIoControl(hDriver, IO_READ_STRING_REQUEST, &Values, sizeof(Values), &Values, sizeof(Values), 0, 0)))
{
std::cout << "RPM Failed!\n";
exit(1);
}
*string = (std::string)Values.buffer;
}
The struct is the same:
struct ReadValues
{
ULONGLONG Address;
ULONGLONG Response;
ULONGLONG Size;
char buffer[128];
};
I thought it was the size, but when I specified the size to 11 (10 + \0) it also read only 4 chars.
The problem is here:
Values.Size = sizeof(128);
^^^^^^^^^^^
This is the same as sizeof(int) (which, I would guess, is 4 in your platform).
Either use 128 or sizeof(buffer) (the latter is arguably better since you won't be hard-coding the same constant in several places).

C++ LZMA SDK: Uncompress function for LZMA2 compressed file

I am trying to create a function that uncompresses LZAM2 compressed data. I inspired myself from this tutorial which works great for LZMA and I tried to adapt it for LZMA2. I successfully created the compression function for LZMA2, but i have no success for the uncompression one.
Here is the compression function:
static void Compress2Inc(std::vector<unsigned char> &outBuf,
const std::vector<unsigned char> &inBuf)
{
CLzma2EncHandle enc = Lzma2Enc_Create(&SzAllocForLzma, &SzAllocForLzma2);
assert(enc);
CLzma2EncProps props;
Lzma2EncProps_Init(&props);
props.lzmaProps.writeEndMark = 1; // 0 or 1
SRes res = Lzma2Enc_SetProps(enc, &props);
assert(res == SZ_OK);
unsigned propsSize = LZMA_PROPS_SIZE;
outBuf.resize(propsSize);
res = Lzma2Enc_WriteProperties(enc);
//cout << res;
//assert(res == SZ_OK && propsSize == LZMA_PROPS_SIZE);
VectorInStream inStream = { &VectorInStream_Read, &inBuf, 0 };
VectorOutStream outStream = { &VectorOutStream_Write, &outBuf };
res = Lzma2Enc_Encode(enc,
(ISeqOutStream*)&outStream, (ISeqInStream*)&inStream,
0);
assert(res == SZ_OK);
Lzma2Enc_Destroy(enc);
}
Where:
static void *AllocForLzma2(void *, size_t size) { return BigAlloc(size); }
static void FreeForLzma2(void *, void *address) { BigFree(address); }
static ISzAlloc SzAllocForLzma2 = { AllocForLzma2, FreeForLzma2 };
static void *AllocForLzma(void *, size_t size) { return MyAlloc(size); }
static void FreeForLzma(void *, void *address) { MyFree(address); }
static ISzAlloc SzAllocForLzma = { AllocForLzma, FreeForLzma };
typedef struct
{
ISeqInStream SeqInStream;
const std::vector<unsigned char> *Buf;
unsigned BufPos;
} VectorInStream;
SRes VectorInStream_Read(void *p, void *buf, size_t *size)
{
VectorInStream *ctx = (VectorInStream*)p;
*size = min(*size, ctx->Buf->size() - ctx->BufPos);
if (*size)
memcpy(buf, &(*ctx->Buf)[ctx->BufPos], *size);
ctx->BufPos += *size;
return SZ_OK;
}
typedef struct
{
ISeqOutStream SeqOutStream;
std::vector<unsigned char> *Buf;
} VectorOutStream;
size_t VectorOutStream_Write(void *p, const void *buf, size_t size)
{
VectorOutStream *ctx = (VectorOutStream*)p;
if (size)
{
unsigned oldSize = ctx->Buf->size();
ctx->Buf->resize(oldSize + size);
memcpy(&(*ctx->Buf)[oldSize], buf, size);
}
return size;
}
Here is what I have so far with the uncompression function but Lzma2Dec_DecodeToBuf function returns error code 1(SZ_ERROR_DATA) and I just couldn't find anything on the web regarding this on the web.
static void Uncompress2Inc(std::vector<unsigned char> &outBuf,
const std::vector<unsigned char> &inBuf)
{
CLzma2Dec dec;
Lzma2Dec_Construct(&dec);
SRes res = Lzma2Dec_Allocate(&dec, outBuf.size(), &SzAllocForLzma);
assert(res == SZ_OK);
Lzma2Dec_Init(&dec);
outBuf.resize(UNCOMPRESSED_SIZE);
unsigned outPos = 0, inPos = LZMA_PROPS_SIZE;
ELzmaStatus status;
const unsigned BUF_SIZE = 10240;
while (outPos < outBuf.size())
{
unsigned destLen = min(BUF_SIZE, outBuf.size() - outPos);
unsigned srcLen = min(BUF_SIZE, inBuf.size() - inPos);
unsigned srcLenOld = srcLen, destLenOld = destLen;
res = Lzma2Dec_DecodeToBuf(&dec,
&outBuf[outPos], &destLen,
&inBuf[inPos], &srcLen,
(outPos + destLen == outBuf.size()) ? LZMA_FINISH_END : LZMA_FINISH_ANY,
&status);
assert(res == SZ_OK);
inPos += srcLen;
outPos += destLen;
if (status == LZMA_STATUS_FINISHED_WITH_MARK)
break;
}
Lzma2Dec_Free(&dec, &SzAllocForLzma);
outBuf.resize(outPos);
}
I am using Visual Studio 2008 and LZMA SDK downloaded from here. Someone here had the exact same problem, but i couldn't make use of his code...
Did anyone ever successfully uncompressed LZMA2 compressed files using LZMA SDK?
Please help!
A temporary workaround would be to replace SRes res = Lzma2Dec_Allocate(&dec, outBuf.size(), &SzAllocForLzma); with SRes res = Lzma2Dec_Allocate(&dec, 8, &SzAllocForLzma); in Uncompress2Inc function where 8 is a magic number...
However this is not the right way to solve the problem...
First mistake is that Lzma2Enc_WriteProperties doesn't return a result but a property byte which will have to be used as the second parameter of the Lzma2Dec_Allocate call in the Uncompress2Inc function. As a result we replace the magic number 8 with the property byte and everything works as expected.
In order to achieve this a 5 byte header must be added to the encoded data which will be extracted in the decoding function. Here is an example that works in VS2008(not the most perfect code but it works...I will get back later, when I have time, with a better example):
void Lzma2Benchmark::compressChunk(std::vector<unsigned char> &outBuf, const std::vector<unsigned char> &inBuf)
{
//! \todo This is a temporary workaround, size needs to be added to the
m_uncompressedSize = inBuf.size();
std::cout << "Uncompressed size is: " << inBuf.size() << std::endl;
DWORD tickCountBeforeCompression = GetTickCount();
CLzma2EncHandle enc = Lzma2Enc_Create(&m_szAllocForLzma, &m_szAllocForLzma2);
assert(enc);
CLzma2EncProps props;
Lzma2EncProps_Init(&props);
props.lzmaProps.writeEndMark = 1; // 0 or 1
props.lzmaProps.level = 9;
props.lzmaProps.numThreads = 3;
//props.numTotalThreads = 2;
SRes res = Lzma2Enc_SetProps(enc, &props);
assert(res == SZ_OK);
// LZMA_PROPS_SIZE == 5 bytes
unsigned propsSize = LZMA_PROPS_SIZE;
outBuf.resize(propsSize);
// I think Lzma2Enc_WriteProperties returns the encoding properties in 1 Byte
Byte properties = Lzma2Enc_WriteProperties(enc);
//! \todo This is a temporary workaround
m_propByte = properties;
//! \todo Here m_propByte and m_uncompressedSize need to be added to outBuf's 5 byte header so simply add those 2 values to outBuf and start the encoding from there.
BenchmarkUtils::VectorInStream inStream = { &BenchmarkUtils::VectorInStream_Read, &inBuf, 0 };
BenchmarkUtils::VectorOutStream outStream = { &BenchmarkUtils::VectorOutStream_Write, &outBuf };
res = Lzma2Enc_Encode(enc,
(ISeqOutStream*)&outStream,
(ISeqInStream*)&inStream,
0);
std::cout << "Compress time is: " << GetTickCount() - tickCountBeforeCompression << " milliseconds.\n";
assert(res == SZ_OK);
Lzma2Enc_Destroy(enc);
std::cout << "Compressed size is: " << outBuf.size() << std::endl;
}
void Lzma2Benchmark::unCompressChunk(std::vector<unsigned char> &outBuf, const std::vector<unsigned char> &inBuf)
{
DWORD tickCountBeforeUncompression = GetTickCount();
CLzma2Dec dec;
Lzma2Dec_Construct(&dec);
//! \todo Heere the property size and the uncompressed size need to be extracted from inBuf, which is the compressed data.
// The second parameter is a temporary workaround.
SRes res = Lzma2Dec_Allocate(&dec, m_propByte/*8*/, &m_szAllocForLzma);
assert(res == SZ_OK);
Lzma2Dec_Init(&dec);
outBuf.resize(m_uncompressedSize);
unsigned outPos = 0, inPos = LZMA_PROPS_SIZE;
ELzmaStatus status;
const unsigned BUF_SIZE = 10240;
while(outPos < outBuf.size())
{
SizeT destLen = std::min(BUF_SIZE, outBuf.size() - outPos);
SizeT srcLen = std::min(BUF_SIZE, inBuf.size() - inPos);
SizeT srcLenOld = srcLen, destLenOld = destLen;
res = Lzma2Dec_DecodeToBuf(&dec,
&outBuf[outPos],
&destLen,
&inBuf[inPos],
&srcLen,
(outPos + destLen == outBuf.size()) ? LZMA_FINISH_END : LZMA_FINISH_ANY,
&status);
assert(res == SZ_OK);
inPos += srcLen;
outPos += destLen;
if(status == LZMA_STATUS_FINISHED_WITH_MARK)
{
break;
}
}
Lzma2Dec_Free(&dec, &m_szAllocForLzma);
outBuf.resize(outPos);
std::cout << "Uncompress time is: " << GetTickCount() - tickCountBeforeUncompression << " milliseconds.\n";
}

Best way to read data streams with different package sizes from serial port in c++

I am working on firmware of an ATMEL sensor board (accelerometer and gyro)and trying to read the data in a platform in Ubuntu.
Currently the firmware is like this:
Ubuntu sends a character "D" and the firmware in response sends back 20 bytes of data that ends in "\n" then ubuntu uses serialport_read_until(fd, buff, '\n') and assumes that buff[0] is byte zero and so on.The frequency of acquisition is 200hz.
BUT using this method sometimes I receive corrupted values and it is not working well. Also there are many "Unable to write on serial port" error in ubuntu.
I have found an example code from ATMEL for the firmware and there the data is sent in different packages and continuously (without waiting for the computer to ask for it) the structure is like this:
void adv_data_send_3(uint8_t stream_num, uint32_t timestamp,
int32_t value0, int32_t value1, int32_t value2)
{
/* Define packet format with 3 data fields */
struct {
adv_data_start_t start; /* Starting fields of packet */
adv_data_field_t field [3]; /* 3 data fields */
adv_data_end_t end; /* Ending fields of packet */
} packet;
/* Construct packet */
packet.start.header1 = ADV_PKT_HEADER_1;
packet.start.header2 = ADV_PKT_HEADER_2;
packet.start.length = cpu_to_le16(sizeof(packet));
packet.start.type = ADV_PKT_DATA;
packet.start.stream_num = stream_num;
packet.start.time_stamp = cpu_to_le32(timestamp);
packet.field[0].value = cpu_to_le32(value0);
packet.field[1].value = cpu_to_le32(value1);
packet.field[2].value = cpu_to_le32(value2);
packet.end.crc = 0x00; /* Not used */
packet.end.mark = ADV_PKT_END;
/* Write packet */
adv_write_buf((uint8_t *)&packet, sizeof(packet));
}
but I don't know how I can continuously read the data that is sent in a structure like above.
Sorry if it is a trivial question. I am not a programmer but I need to solve this and I could not find a solution (that I can understand!) after searching for a couple of days.
The reading function I use in linux:
int serialport_read_until(int fd, unsigned char* buf, char until){
char b[1];
int i=0;
do {
int n = read(fd, b, 1); // read a char at a time
if( n==-1) return -1; // couldn't read
if( n==0 ) {
usleep( 1 * 1000 ); // wait 1 msec try again
continue;
}
buf[i] = b[0]; i++;
} while( b[0] != until );
buf[i] = 0; // null terminate the string
return 0;}
The new Reading Func:
// Read the header part
adv_data_start_t start;
serial_read_buf(fd, reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
serial_read_buf(fd, data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_field_t* fields = reinterpret_cast<adv_data_field_t*>(data_and_end.data());
for (size_t i = 0; i < num_data_fields; i++)
std::cout << "Field #" << (i + 1) << " = " << fields[i].value << '\n';
The data packets that are sent from the firmware:
typedef struct {
uint8_t header1; // header bytes - always 0xFF5A
uint8_t header2; // header bytes - always 0xFF5A
uint16_t length; // packet length (bytes)
uint32_t time_stamp; // time stamp (tick count)
} adv_data_start_t;
typedef struct {
int32_t value; // data field value (3 VALUES)
} adv_data_field_t;
typedef struct {
uint8_t crc; // 8-bit checksum
uint8_t mark; // 1-byte end-of-packet marker
uint16_t mark2; // 2-byte end-of-packet marker (Added to avoid data structure alignment problem)
} adv_data_end_t;
Well you have the length of the packet in the packet "header", so read the header fields (the start structure) in one read, and in a second read you read the data and the end.
If the start and end parts are the same for all packets (which I guess they are), you can easily figure out the amount of data fields after the second read.
Something like this:
// Read the header part
adv_data_start_t start;
adv_read_buf(reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
adv_read_buf(data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_end_t* fields = reinterpret_cast<adv_data_end_t*>(data_and_end.data());
for (size_t i = 0; i < num_data_fields; i++)
std::cout << "Field #" << (i + 1) << " = " << fields[i] << '\n';
Possible read_buf implementation:
// Read `bufsize` bytes into `buffer` from a file descriptor
// Will block until `bufsize` bytes has been read
// Returns -1 on error, or `bufsize` on success
int serial_read_buf(int fd, uint8_t* buffer, const size_t bufsize)
{
uint8_t* current = buffer;
size_t remaining = bufsize
while (remaining > 0)
{
ssize_t ret = read(fd, current, remaining);
if (ret == -1)
return -1; // Error
else if (ret == 0)
{
// Note: For some descriptors, this means end-of-file or
// connection closed.
usleep(1000);
}
else
{
current += ret; // Advance read-point in buffer
remaining -= ret; // Less data remaining to read
}
}
return bufsize;
}

How can I send all data over a socket?

I am trying to send large amounts of data over a socket, sometimes when I call send (on Windows) it won't send all the data I requested, as expected. So, I wrote a little function that should have solved my problems- but it's causing problems where the data isn't being sent correctly and causing the images to be corrupted. I'm making a simple chat room where you can send images (screenshots) to each other.
Why is my function not working?
How can I make it work?
void _internal_SendFile_alignment_512(SOCKET sock, BYTE *data, DWORD datasize)
{
Sock::Packet packet;
packet.DataSize = datasize;
packet.PacketType = PACKET_FILETRANSFER_INITIATE;
DWORD until = datasize / 512;
send(sock, (const char*)&packet, sizeof(packet), 0);
unsigned int pos = 0;
while( pos != datasize )
{
pos += send(sock, (char *)(data + pos), datasize - pos, 0);
}
}
My receive side is:
public override void OnReceiveData(TcpLib.ConnectionState state)
{
if (state.fileTransfer == true && state.waitingFor > 0)
{
byte[] buffer = new byte[state.AvailableData];
int readBytes = state.Read(buffer, 0, state.AvailableData);
state.waitingFor -= readBytes;
state.bw.Write(buffer);
state.bw.Flush();
if (state.waitingFor == 0)
{
state.bw.Close();
state.hFile.Close();
state.fileTransfer = false;
IPEndPoint ip = state.RemoteEndPoint as IPEndPoint;
Program.MainForm.log("Ended file transfer with " + ip);
}
}
else if( state.AvailableData > 7)
{
byte[] buffer = new byte[8];
int readBytes = state.Read(buffer, 0, 8);
if (readBytes == 8)
{
Packet packet = ByteArrayToStructure<Packet>(buffer);
if (packet.PacketType == PACKET_FILETRANSFER_INITIATE)
{
IPEndPoint ip = state.RemoteEndPoint as IPEndPoint;
String filename = getUniqueFileName("" + ip.Address);
if (filename == null)
{
Program.MainForm.log("Error getting filename for " + ip);
state.EndConnection();
return;
}
byte[] data = new byte[state.AvailableData];
readBytes = state.Read(data, 0, state.AvailableData);
state.waitingFor = packet.DataSize - readBytes;
state.hFile = new FileStream(filename, FileMode.Append);
state.bw = new BinaryWriter(state.hFile);
state.bw.Write(data);
state.bw.Flush();
state.fileTransfer = true;
Program.MainForm.log("Initiated file transfer with " + ip);
}
}
}
}
It receives all the data, when I debug my code and see that send() does not return the total data size (i.e. it has to be called more than once) and the image gets yellow lines or purple lines in it — I suspect there's something wrong with sending the data.
I mis-understood the question and solution intent. Thanks #Remy Lebeau for the comment to clarify that. Based on that, you can write a sendall() function as given in section 7.3 of http://beej.us/guide/bgnet/output/print/bgnet_USLetter.pdf
int sendall(int s, char *buf, int *len)
{
int total = 0; // how many bytes we've sent
int bytesleft = *len; // how many we have left to send
int n = 0;
while(total < *len) {
n = send(s, buf+total, bytesleft, 0);
if (n == -1) {
/* print/log error details */
break;
}
total += n;
bytesleft -= n;
}
*len = total; // return number actually sent here
return n==-1?-1:0; // return -1 on failure, 0 on success
}
You need to check the returnvalue of send(). In particular, you can't simply assume that it is the number of bytes sent, there is also the case that there was an error. Try this instead:
while(datasize != 0)
{
n = send(...);
if(n == SOCKET_ERROR)
throw exception("send() failed with errorcode #" + to_string(WSAGetLastEror()));
// adjust pointer and remaining number of bytes
datasize -= n;
data += n;
}
BTW:
Make that BYTE const* data, you're not going to modify what it points to.
The rest of your code seems too complicated, in particular you don't solve things by aligning to magic numbers like 512.

How to read exactly one line?

I have a Linux file descriptor (from socket), and I want to read one line.
How to do it in C++?
I you are reading from a TCP socket you can't assume when the end of line will be reached.
Therfore you'll need something like that:
std::string line;
char buf[1024];
int n = 0;
while(n = read(fd, buf, 1024))
{
const int pos = std::find(buf, buf + n, '\n')
if(pos != std::string::npos)
{
if (pos < 1024-1 && buf[pos + 1] == '\n')
break;
}
line += buf;
}
line += buf;
Assuming you are using "\n\n" as a delimiter. (I didn't test that code snippet ;-) )
On a UDP socket, that is another story. The emiter may send a paquet containing a whole line. The receiver is garanted to receive the paquet as a single unit .. If it receives it , as UDP is not as reliable as TCP of course.
Pseudocode:
char newline = '\n';
file fd;
initialize(fd);
string line;
char c;
while( newline != (c = readchar(fd)) ) {
line.append(c);
}
Something like that.
Here is a tested, quite efficient code:
bool ReadLine (int fd, string* line) {
// We read-ahead, so we store in static buffer
// what we already read, but not yet returned by ReadLine.
static string buffer;
// Do the real reading from fd until buffer has '\n'.
string::iterator pos;
while ((pos = find (buffer.begin(), buffer.end(), '\n')) == buffer.end ()) {
char buf [1025];
int n = read (fd, buf, 1024);
if (n == -1) { // handle errors
*line = buffer;
buffer = "";
return false;
}
buf [n] = 0;
buffer += buf;
}
// Split the buffer around '\n' found and return first part.
*line = string (buffer.begin(), pos);
buffer = string (pos + 1, buffer.end());
return true;
}
It's also useful to setup signal SIGPIPE ignoring in reading and writing (and handle errors as shown above):
signal (SIGPIPE, SIG_IGN);
Using C++ sockets library:
class LineSocket : public TcpSocket
{
public:
LineSocket(ISocketHandler& h) : TcpSocket(h) {
SetLineProtocol(); // enable OnLine callback
}
void OnLine(const std::string& line) {
std::cout << "Received line: " << line << std::endl;
// send reply here
{
Send( "Reply\n" );
}
}
};
And using the above class:
int main()
{
try
{
SocketHandler h;
LineSocket sock(h);
sock.Open( "remote.host.com", port );
h.Add(&sock);
while (h.GetCount())
{
h.Select();
}
}
catch (const Exception& e)
{
std::cerr << e.ToString() << std::endl;
}
}
The library takes care of all error handling.
Find the library using google or use this direct link: http://www.alhem.net/Sockets/