how to process asynchronous messages with libuv

Started by
-1 comments, last by busho 9 years, 9 months ago

Hello, I'm a long time gamedev lurker and now it's time for my first post smile.png

I have recently began working on my first C/C++ project. As part of it I am developing a networking functionality, and to not reinvent the wheel I have decided to integrate libuv library. I have a very specific question about asynchronous message processing. As for documentation on how to integrate libuv networking, my code currently looks like this:


void onConnectCb ( .......) {
......

uv_read_start((uv_stream_t*)(&_client->tcp), ClientConnection::locCbOnAlloc, ClientConnection::locCbOnRead);
}

And locCbOnAlloc and locCbOnRead look simply like this:


/* On allocation */
void ClientConnection::locCbOnAlloc(uv_handle_t *handle, size_t suggestedSize, uv_buf_t* buf) {
	*buf = uv_buf_init((char*)malloc(suggestedSize), suggestedSize);
}

/* On read */
void ClientConnection::locCbOnRead(uv_stream_t *tcp, ssize_t nread, const uv_buf_t* buf) {
	if (nread < 0) {	//ERROR
		uv_close((uv_handle_t *)tcp, ClientConnection::locCbOnClose);
	}	
	else if (nread > 0) {
		usninfo("usnetwork", "Reading %d bytes from NS", nread); //is nread bytes or chars or what?
		fwrite(buf->base, sizeof(char), nread, stdout);
	}
	free(buf->base);
}

My question is, because libuv invokes these callback asynchronisly the variable nread varies. Supposedly I will have some sort of standard protocol for communication between my services, the messages will have a length parameter. What should happen if locCbOnRead reads too few bytes or too many (truncates the message or includes the next message).

I have thought about using one huge double way buffer per client connection like this:


struct cli_buffer_t {
	char *buffer;
	int readLoc;
	int writeLoc;
	int allocSize;
	int logicalSize;
};

void initBuffer(cli_buffer_t *buffer) {
	buffer->buffer = (char *)malloc(sizeof(char)* 2048 * 2048);
	buffer->readLoc = buffer->writeLoc = 0;
	buffer->allocSize = 2048 * 2048;
	buffer->logicalSize = 0;
}

char *getWriteLoc(cli_buffer_t *buffer) {
	return &buffer->buffer[buffer->writeLoc];
}

void advanceWrite(cli_buffer_t *buffer, int nread) {
	buffer->writeLoc += nread; //SAMPLE CODE, NOT CHECKING OUT OF BUFFER BOUNDS YET.
}

Reading from buffer would increment readLoc, writing would increment writeLoc(both required to check if buffer has too few bytes or is full).

So allocation and reading callbacks become this:


/* On allocation */
void ClientConnection::locCbOnAlloc(uv_handle_t *handle, size_t suggestedSize, uv_buf_t* buf) {
    buf->base = getWriteLoc(memory);
    buf->len = suggestedSize;
}

/* On read */
void ClientConnection::locCbOnRead(uv_stream_t *tcp, ssize_t nread, const uv_buf_t* buf) {
	....	
	advanceWrite(memory, nread); //WE HAVE READ NREAD BYTES INTO BUFFER THAT STARTED AT GETWRITELOC(MEMORY). aDVANCE THE WRITE LOCATION FOR NEXT CALL.
}

Would something like this be appropriate approach? This way, I can invoke app level cb from advanceWrite() and try reading only desired amount of bytes (however much the message length will be). Does libuv provide any means to do this without this code? (ea, it stores data in internal buffer and I can read exact number of bytes from it).

Also this code would be extremely buggy without locking mechanisms as there is no guarantee that onRead will be called exactly after onAlloc (maybe onAlloc gets called 2 times before onRead which would overwrite data etc). It could get complicated and messy really fast so I decided to ask first smile.png

TL;DR: How to work with asynchronous messages that can have too many or too few bytes (according to my own length parameter in message).

Thanks for help smile.png

This topic is closed to new replies.

Advertisement