I'm trying to Identify a message by using the getKind() function, I've previously defined my own DATA_KIND
for sending:
DataM *data = new DataM();
data ->setKind(DATA_KIND);
data ->setSrc(this->getParentModule()->getIndex());
socket.sendTo(data, destAddr, destPort);
for receiving which it bypasses but is received as as a UDP_I_DATA
bypasses this:
else if (msg->getKind() == DATA_KIND) {
// process incoming packet;
}
and uses this:
else if (msg->getKind() == UDP_I_DATA) {
// process incoming packet;
}
Please help!
I have tried adding the DATA_KIND value to the .h files and to the .cc files, I thought it was about the scope, it didn't work
You cannot use setKind()/getKind() to carry out own information across the whole network in INET. Kind is a metadata that is used mainly between modules inside a device - to inform about the status of a packet. Therefore UDP layer always sets the value of kind for received packets (e.g.UDP_I_DATA, UDP_I_ERROR).
I suggest introducing own packet with the field that will determine the type, e.g. 1 - MY_DATA, 2 - MY_CONTROL, etc.
An example:
packet DataM {
int myType; // own type
// other data
int otherData;
}
Then in your code you may use:
// somewhere in *.h
#define MY_DATA 1
#define MY_CONTROL 2
// in *.cc - sending
DataM *data = new DataM();
data->setMyType(MY_DATA);
// in *.cc - receiving
DataM *data2 = dynamic_cast<DataM*>(msg);
if (data2 != nullptr) {
if (data2->getMyType == MY_DATA) {
// MY_DATA - do something
}
}
References:
OMNeT++ Simulation Manula - 6 Message Definitions
TicToc Tutorial - 4.4 Defining our message class
Related
I am now implementing the Raft algorithm, and I want to use gRPC stream to do this. My main idea is to create 3 streams for each node to every other peers, one stream will transmit one type of RPCs, there are AppendEntries, RequestVote and InstallSnapshot. I write some code with limited help from route_guide, because in its bidirectional stream demo RouteChat, the client send all its data before it starts to read.
Firstly, I want to write to a stream at any time, so I write the following codes
void RaftMessagesStreamClientSync::AsyncRequestVote(const RequestVoteRequest& request){
std::string peer_name = this->peer_name;
debug("GRPC: Send RequestVoteRequest from %s to %s\n", request.name().c_str(), peer_name.c_str());
request_vote_stream->Write(request);
}
Meanwhile, I want a thread keep reading from a stream, like the following codes, which is called immediately after RaftMessagesStreamClientSync is constructed.
void RaftMessagesStreamClientSync::handle_response(){
// strongThis is a must
auto strongThis = shared_from_this();
t1 = new std::thread([strongThis](){
RequestVoteResponse response;
while (strongThis->request_vote_stream->Read(&response)) {
debug("GRPC: Recv RequestVoteResponse from %s, me %s\n", response.name().c_str(), strongThis->raft_node->name.c_str());
...
}
});
...
In order to initialize 3 streams, I have to write the constructor like this, I use 3 ClientContext here because the document says one ClientContext for one RPC
struct RaftMessagesStreamClientSync : std::enable_shared_from_this<RaftMessagesStreamClientSync>{
typedef grpc::ClientReaderWriter<RequestVoteRequest, RequestVoteResponse> CR;
typedef grpc::ClientReaderWriter<AppendEntriesRequest, AppendEntriesResponse> CA;
typedef grpc::ClientReaderWriter<InstallSnapshotRequest, InstallSnapshotResponse> CI;
std::unique_ptr<CR> request_vote_stream;
std::unique_ptr<CA> append_entries_stream;
std::unique_ptr<CI> install_snapshot_stream;
ClientContext context_r;
ClientContext context_a;
ClientContext context_i;
std::thread * t1 = nullptr;
std::thread * t2 = nullptr;
std::thread * t3 = nullptr;
...
}
RaftMessagesStreamClientSync::RaftMessagesStreamClientSync(const char * addr, struct RaftNode * _raft_node) : raft_node(_raft_node), peer_name(addr) {
std::shared_ptr<Channel> channel = grpc::CreateChannel(addr, grpc::InsecureChannelCredentials());
stub = raft_messages::RaftStreamMessages::NewStub(channel);
// 1
request_vote_stream = stub->RequestVote(&context_r);
// 2
append_entries_stream = stub->AppendEntries(&context_a);
// 3
install_snapshot_stream = stub->InstallSnapshot(&context_i);
}
~RaftMessagesStreamClientSync() {
raft_node = nullptr;
t1->join();
t2->join();
t3->join();
delete t1;
delete t2;
delete t3;
}
Then I implement the server side
Status RaftMessagesStreamServiceImpl::RequestVote(ServerContext* context, ::grpc::ServerReaderWriter< ::raft_messages::RequestVoteResponse, RequestVoteRequest>* stream){
RequestVoteResponse response;
RequestVoteRequest request;
while (stream->Read(&request)) {
...
}
return Status::OK;
}
Then 2 problems happen:
When I test with 3 nodes, which actually creates 2 RaftMessagesStreamServiceImpl for each node, the statement from 1 to 3 cost a long time to execute.
There is no RPC received from server side.
There are similar problems when using Bidi Aysnc Server, However I can't figure out how this post can help me.
UPDATE
After some debugging, I found request_vote_stream->Write(request) returns 0, which, according to the document, means the stream is closed. However why is it closed?
After some debugging, I found that the two problem are all due to one problem that I create a client before I create a server.
Because I originally uses unary RPC calls, so a previous call from client only causes a gRPC error code 14. The program continues because every call sent after the server is created can be handled correctly.
However, when it comes to streaming calls, stub->RequestVote(&context_r) will end up calling a blocking function ClientReaderWriter::ClientReaderWriter, which will try to connect to the server, which is not created now.
/// Block to create a stream and write the initial metadata and \a request
/// out. Note that \a context will be used to fill in custom initial metadata
/// used to send to the server when starting the call.
ClientReaderWriter(::grpc::ChannelInterface* channel,
const ::grpc::internal::RpcMethod& method,
ClientContext* context)
: context_(context),
cq_(grpc_completion_queue_attributes{
GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
GRPC_CQ_DEFAULT_POLLING}), // Pluckable cq
call_(channel->CreateCall(method, context, &cq_)) {
if (!context_->initial_metadata_corked_) {
::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
ops;
ops.SendInitialMetadata(context->send_initial_metadata_,
context->initial_metadata_flags());
call_.PerformOps(&ops);
cq_.Pluck(&ops);
}
}
As a consequence, the connection has not yet been established.
I'm having an issue where I send a message to user-mode from kernel-mode using FltSendMessage that expects a reply. The struct being passed contains an int that is set to either 0 or 1. User-mode replies by setting this flag and calling FilterReplyMessage. However, when the message is received by the kernel, its value is always 56. No matter what number I set the flag to in user-mode, the kernel always receives the value 56. I'm confused as to where my error is.
I've tried changing the data type of passFlag from int to other types (USHORT etc..) which I knew probably wouldn't make a difference, but was worth a try.
Because the kernel message is replied to successfully (Checking user-mode HRESULT returns no errors and there is no timeout so if no reply is received the system would hang, which it does not), I know the error must be with the buffers being passed between user-mode and kernel-mode. I can't seem to find the reason why the passFlag is not being interpreted correctly in kernel-mode.
Can anyone help?
Shared Structure:
typedef struct _REPLY_MESSAGE_STRUCT {
// Message header.
FILTER_REPLY_HEADER header;
// Flag to be set
// by user mode.
int passFlag;
}REPLY_MESSAGE_STRUCT, *PREPLY_MESSAGE_STRUCT;
Kernel Code:
DbgPrint("Sending Message...\n");
replyBuffer.passFlag = 0;
ULONG replySize = ((ULONG)sizeof(replyBuffer.header)) + ((ULONG)sizeof(replyBuffer));
REPLY_MESSAGE_STRUCT replyBuffer;
// Note: Try-catch statement has been omitted in this question to save time.
// In the actual code there is a try-catch statement surrounding FltSendMessage.
status = FltSendMessage(imageFilterData.filterHandle,
&imageFilterData.clientPort,
(PVOID)&sendingBuffer.messageBuffer,
sizeof(sendingBuffer.messageBuffer),
(PVOID)&replyBuffer,
&replySize,
0
);
// Always returns 56
// When a reply has been received.
DbgPrint("Message received: %i\n", replyBuffer.passFlag);
User code:
// User-mode buffer is the same size as kernel-mode buffer.
ULONG replySize = ((ULONG)sizeof(replyBuffer.header)) + ((ULONG)sizeof(replyBuffer));
replyMessage.header.MessageId = messageFromKernel.header.MessageId;
REPLY_MESSAGE_STRUCT replyMessage;
// User-mode setting flag.
replyMessage.passFlag = 1;
// Flag is changed to 1 successfully.
printf("Test: %i\n", replyMessage.passFlag);
// Reply is sent successfully, but flag value on kernel end is always 56
hResult = FilterReplyMessage(port,
&replyMessage.header,
replySize);
_com_error err2(hResult);
errorMessage = err2.ErrorMessage();
// No errors.
printf("Result: %s\n", errorMessage);
What I have tried:
Changing the datatype of passFlag.
Going through every step before and after FltSendMessage and FilterReply message to find if the value is being changed before being sent back to the kernel.
you are using error data in call FltSendMessage:
ReplyBuffer is pointer to custom user defined data. it must not begin from FILTER_REPLY_HEADER
SenderBuffer is pointer to custom user defined data. it must not begin from FILTER_MESSAGE_HEADER
first of all you need define structures, that are shared between kernel and user mode, for message and reply. for example
struct SCANNER_NOTIFICATION {
// any custom data
int someData;
};
struct SCANNER_REPLY {
// any custom data
int passFlag;
};
and in kernel mode you direct use it as is:
SCANNER_NOTIFICATION send;
SCANNER_REPLY reply;
ULONG ReplyLength = sizeof(reply);
FltSendMessage(*, *, &send, sizeof(send), &reply, &ReplyLength, *);
in user mode you need define 2 additional structures:
struct SCANNER_MESSAGE : public FILTER_MESSAGE_HEADER, public SCANNER_NOTIFICATION {};
struct SCANNER_REPLY_MESSAGE : public FILTER_REPLY_HEADER, public SCANNER_REPLY {};
(i use c++ style here, when here used c style)
and in user mode we need use next, for example:
SCANNER_MESSAGE* mesage;
FilterGetMessage(*, mesage, sizeof(SCANNER_MESSAGE), *);
and
SCANNER_REPLY_MESSAGE reply;
reply.MessageId = mesage->MessageId;
FilterReplyMessage(*, &reply, sizeof(reply));
I'm trying to use Google's ProtocolBuffer to send/receive data in a server/client architecture. I am able to connect the two with winsock and I am able to send and receive data from ProtocolBuffer with it but there seems to be a problem with deserializing the data on the server (I haven't done the opposite so I can't tell if that works yet)
Here's the code I use to create a sample packet of data to send to the server:
MessageID *message = new MessageID();
message->set_type(MessageID::Type::MessageID_Type_PLAYERDATA);
PlayerData::Vector2 *position = new PlayerData::Vector2();
position->set_x(10.0f);
position->set_y(25.0f);
PlayerData *data = new PlayerData();
data->set_health((UINT32)50);
data->set_allocated_position(position);
auto inventory = data->add_items();
inventory->set_itemid((INT32)1);
inventory->set_count((INT32)5);
message->set_allocated_playerdata(data);
m_pNetworkObject->Send(message);
This is the code that actually sends the data over a TCP connection:
// Serialize to string.
std::string sendData;
data->SerializeToString(&sendData);
// Convert data to const char*
const char* dataToSend = sendData.c_str();
int iResult;
iResult = send( ConnectSocket, dataToSend, data->ByteSize(), 0 );
if (iResult == SOCKET_ERROR)
{
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
exit(1);
}
So, this was code that is run on the client. Now, when I receive this on the server I get the following errors from ProtocolBuffer:
Can't parse message of type "MessageID" because it is missing required fields: type
Can't parse message of type "PlayerData" because it is missing required fields: Position, Health
The code on the server that decompiles the winsock data is like this:
// decompile to a proto file and check ID
MessageID* receivedData = new MessageID();
receivedData->ParseFromArray(&recv, strlen(recv));
// check and redeserialize
switch (receivedData->type())
{
case MessageID::Type::MessageID_Type_PLAYERDATA:
{
PlayerData* playerData = new PlayerData();
playerData->ParseFromArray(&recv, strlen(recv));
UsePlayerData(sock, playerData);
break;
}
case MessageID::Type::MessageID_Type_WORLDDATA:
{
WorldData* worldData = new WorldData();
worldData->ParseFromArray(&recv, strlen(recv));
UseWorldData(sock, worldData);
break;
}
}
The weird thing is that, although the error says that the data doesn't contain the Type value it does set it to 1 and the switch statement works. After that when I try to take a look at the Position and Health data everything is 0.
I have no idea what I'm doing wrong. I read most of the ProtocolBuffer tutorials and help but it seems to no avail.
EDIT:
I tried serializing and then deserializing the data in the same application (without the networking involved) and with the following code it worked to do just that:
std::string sendData;
message->SerializeToString(&sendData);
MessageID* receivedData = new MessageID();
receivedData->ParseFromString(sendData);
If I check the data in receivedData it is exactly the same (so, the right values) as in the original object "message"
Your problem is that you are using strlen() to find the length of the message. strlen() just looks for the first zero-valued byte and assumes that's the end. This is the convention for text strings, but does not apply to binary messages like Protocol Buffers. In this case, strlen() is returning a size that is too short and so some fields are missing from the message, but the opposite can happen too -- strlen() can run off the end of the buffer and return a size that is too long, or even crash your program.
So, you need to make sure the actual exact size of the message is communicated from the sender to the receiver.
I've been stuck on this issue for awhile where I'm unable to send a file through a socket. I've sent other information just fine using this method, but the problem seems to appear when I try to send a PNG file as a string.
These are the methods I use to to send and receive information:
// Sends a Message to the specified Socket
void Server::sendMessage(int socket, string message)
{
// Write the Message Size to the Socket
send(socket, itoa((message.length() + 1)), sizeof(size_t));
// Wait for Write Confirmation
bool response;
receive(socket, &response, 2);
// Write the Message to the Socket
send(socket, (char*) message.c_str(), message.length() + 1);
// Wait for Write Confirmation
receive(socket, &response, 2);
}
// Receives Message from the specified Socket
string Server::receiveMessage(int socket)
{
// Read the Message Size from the Socket
int size;
receive(socket, &size, sizeof(size_t));
// Send Write Confirmation
send(socket, itoa(true), 2);
// Receive the Message from the Socket
char message[size];
receive(socket, message, size);
// Send Write Confirmation
send(socket, itoa(true), 2);
// Return the Message as a String
string msg(message);
return msg;
}
The send and receive methods are just relays for write and read respectively. I'm only doing error checking in those methods, and it's the send method that's telling me that the write isn't working. In case it matters, this is my send method:
// Sends a Data Packet to the specified Socket
int Server::send(int socket, void* data, int size)
{
// Write the Data to the Socket
int count = write(socket, data, size);
// Make sure the Write Succeeded
if(count == -1)
{
print("$f1Error: $f0Unable to Write to Socket $t1%i$t0\n", socket);
exit(1);
}
return count;
}
I should note that the Server operates as a Thread, therefore the above three functions are static. The Client also contains the same four networking functions.
The command line breaking this happens in a separate static function which I use to handle Clients. Here is the relevant portion of said method:
// Handles each Client with a Thread
void* Server::server_handleClient(void* arg)
{
// Determine the Socket Descriptor
int socket = *((int*) arg);
free(arg);
// Create the Rover
Rover* rover = new Rover();
// Loop Indefinitely
while(true)
{
...
// Take a Picture and Send it
sendMessage(socket, rover -> takePicture());
...
}
// Delete the Rover
delete rover;
// Close the Socket
close(socket);
// Return a Successful Status
return (void*) new int(0);
}
Here you can see that I make use of a method from another class I've created. Here is the takePicture method from the Rover class, which is where I actually grab the picture:
// Takes a Picture and Returns the Photo as a String
inline string Rover::takePicture()
{
// Open the Picture File
ifstream picture;
string filepath = "./Server/Pictures/" + getDirection() + ".png";
picture.open(filepath.c_str());
// Make sure the File Opened
if(!picture.is_open())
return "";
// Read the File into a String Buffer
stringstream buffer;
buffer << picture.rdbuf();
return buffer.str();
}
So in short, the Server gets a picture from the Rover which it then sends to a Client. When I check the contents of the string for the photo, it's all there. All possible photos are reasonable in size (the photo used for testing is 674,962 bytes, and the buffer size sent is 674,963 which is expected).
I've used these methods for sending various messages, and all of that worked fine. I'm able to send strings (Like "Hello World!") and integers just fine.
Is there something that I'm doing wrong? Is the file that I'm trying to send simply too large? Is there some information that I'm missing? I need help...
Edit:
I've made a few changes with a little progress. I made one small change to the sendMessage command. The current problem is that the picture isn't being sent properly.
New sendMessage function:
// Sends a Message to the specified Socket
void Server::sendMessage(int socket, string message, bool data = false)
{
// Write the Message Size to the Socket
send(socket, itoa((message.length() + 1)), sizeof(size_t));
// Wait for Write Confirmation
bool response;
receive(socket, &response, 2);
// Determine the Type of Data to Send
if(data)
{
// Write the Message Data to the Socket
send(socket, (char*) message.data(), message.length() + 1);
}
else
{
// Write the Message to the Socket
send(socket, (char*) message.c_str(), message.length() + 1);
}
// Wait for Write Confirmation
receive(socket, &response, 2);
}
The Client's copy of this function has been updated to match as well.
Now that we're working on getting the PNG file saved, here's the function that deals with that as well:
// Handles each Client with a Thread
void* Client::client_handleServer(void* arg)
{
// Define Socket Variables
int socket = *((int*) arg);
free(arg);
...
// Export the Picture to the Client's Directory
message = receiveMessage(socket);
ofstream picture;
picture.open("./Client/Pictures/Picture.png", std::ifstream::binary);
picture << message;
picture.close();
...
}
Currently you are opening the file in textmode. that means any characters in the files which contain newlines "\n" are converted to new line + carriage returns "\r\n".
Open your file in binary mode, like so
picture.open(filepath.c_str(), std::ifstream::binary);
then it may work.
void Server::sendMessage(int socket, string message)
The problem is right here. Don't use string as a container for binary data. Pass the image around as a byte array. Same applies to this:
string Server::receiveMessage(int socket)
I eventually figured everything out in the long run.
Pictures are binary files, and I was using Strings which use ASCII Characters. The issue with this is that binary data does not always translate to ASCII, and Strings are terminated by null characters, whereas binary data can contain null data within it. Long story short, strings do not work.
To preserve the message handling I had in place, I ended up just converting the binary data to hexadecimal data (0-F) which could be displayed in a String.
I am trying to implement an authentication system using C++/QtTcpSocket for a personal project (A Multiplayer Chess Game).
My friend suggested a method for verifying a user but I wanted to ask if there was an easier or better way. Coming from a Python background and mostly doing this project to develop a deeper understanding of C++.
I will post the method my friend suggested and ask for maybe a better solution.
He built it in a kind of pseudo code fashion. The server is mostly built, I am now hoping to implement Authentication
*cheers
void process_packet(PACKET *pkt)
{
switch(pkt->PacketID)
{
case 0: // let's say packet id 0 is the logon packet; packet contents are username and password
{
//let's say packet size is 101 bytes; packet id was already received, so get the other 100 bytes
unsigned char BUFFER[101] = {0}; // i always add an extra byte to the end of the buffer to allow for off-by-one errors ^_^
int result = recv_packet(pkt->cSocket, 100, BUFFER);
if(result <= 0)
return; // connection error; no packet data was received
unsigned char *UserName = BUFFER+0; //+0 is not neccessary, but the username starts at the beginning. just getting the point across.
unsigned char *PassWord = BUFFER+50;
//side note: if we did "unsigned long *blah = BUFFER+4" or something, we would have to make sure the byte order is right. network byte order is BIG ENDIAN
// WINDOWS byte order is LITTLE ENDIAN
result = QueryDatabase("SELECT username, password FROM chess_players WHERE username = '%s'", FILTER_INVALID_CHARS(UserName));
// check result
unsigned char ServerResponse[2] = {0};
if(result['password'] == PassWord)
{
ServerResponse[0] = 1; // packet id will be 1. the next byte can be 1 or 0 to indicate logon success or failure.
ServerResponse[1] = true; // so packet 0x0101 mean logon success, packet 0x0100 means logon failure
send_packet(pkt->cSocket, ServerResponse, 2);
} else {
ServerResponse[0] = 1;
ServerResponse[1] = false;
send_packet(pkt->cSocket, ServerResponse, 2);
}
}
break;
default:
{
// received an unknown packet id; send a packet to the client that indicates an error_status_t
unsigned char *ServerResponse[2] = {0};
ServerResponse[0] = 2; // packet id 2 means server error
ServerResponse[1] = 0; // error code 0 means 'unknown packet id'
send_packet(pkt_cSocket, ServerResponse, 2);
}
break;
}
delete pkt; // must delete pkt, was created with 'new' in get_client_packets()
}
This seems rather C-stylish and not like the Qt way of doing things.
There is no general answer to your question but my suggestions are the following:
Listen to the newConnection() signal of the QTcpServer. Your handler has to call the nextPendingConnection() to get the next client waiting in the queue. The first thing you will do is probably your user authentication.
Once authenticated, you keep the QTcpSocket in your list of active connections.
Take a look at e.g. the fortune client/server examples how to actually write/read packets.
You might also want to look into the stream operators << to serialize your objects. This is much easier and less error prone than the low-level method you posted. ALso, QDataStream will take care of host and network byte orders automatically.
If you have followed the fortune client/server examples, you should have a QTcpServer (Rfserver) with a QThread subclass (Rfdevice, its instance variable is called thread in the following code) that contains a QTcpSocket (listenSocket).
Having said that, in your server class, listen for incoming connections, my setup looks like this:
void Rfserver::incomingConnection(int socketDescriptor){
if(thread){ //if thread exists, there is probably still an open connection
if(thread->listenSocket){//if thread exists and the listenSocket is filled, there is definately an open connection
if(thread->listenSocket->state() == QAbstractSocket::UnconnectedState){
//but alas, it could just be in the unconnected state, if so kill it.
this->disconnect();
thread->terminate();
thread=0;
connected=false;
}//otherwise, do nothing, because the software is happily connected to a device
}
}
if(!thread){ //if no thread exists, we are by no means connected
thread = new rfdevice(socketDescriptor, this); //set up a new thread
//this first connection communicates the string from your socket to the server parent...use it if you want.
connect( thread, SIGNAL(RemoteButton(QString)),this,SLOT(remoteButton(QString)),Qt::BlockingQueuedConnection);
connect( thread, SIGNAL(error(QTcpSocket::SocketError)),this,SLOT(tcpError(QTcpSocket::SocketError)),Qt::AutoConnection);
connect( thread, SIGNAL(finished()), this, SLOT(threadZero())); //I have a threadZero function that deletes all the data then schedules the socket for deletion.
thread->start();
connected=true;
QString *welcome = new QString("Enter your password:\r\n");
echoCommand(welcome); //this is a function you will implement that sends the welcome message to the pending device.
}
}
Okay, so now, when a device tries to connect to the server the device is presented with "Enter your password:\r\n". Your device will respond to this with a password and username perhaps. But the Qt side of things would look like this:
/*
FUNCTION:read
this is a polling runloop that listens for data as long as the socket is connected or connecting. If a
write is ever scheduled, it will be called from this runloop..
*/
void Rfdevice::read(void){
while((listenSocket->state() == QAbstractSocket::ConnectedState) || (listenSocket->state() == QAbstractSocket::ConnectingState)){
//if there is data available to send write it to the socket
if(dataToSend) this->write();
if(listenSocket->waitForReadyRead(50)) readBytes();
//wait for 50ms for data from the device
//if there is ever data available to be read, read it.
}
}
Your device responds with a username/password in the format username---password\r\n. Then the socket does this:
/*
FUNCTION:readBytes
this is like a callback function because it only gets called when there is data available for read.
It basically converts the data to a string.
*/
void Rfdevice::readBytes(void){
QByteArray newData;
newData = listenSocket->readAll();
QString *recieved = new QString(newData);
QStringList userAndPass = recieved.split("---");//this is your delimiter
QString username = userAndPass.at(0);
QString password = userAndPass.at(1);
//NOW, check the username and password vs your SQL or wherever it's saved.
}
The pseudo-code is pretty complete on the particulars. Hopefully you can put it all together! Let me know if you need more code.