LSP Project
Author: Yangcheng Gu, Zhen Tong
Implement the Live Sequence Protocol (LSP) is a homegrown protocol for providing reliable communication with simple client and server APIs on top of the Internet UDP protocol. In this project of Go language, we implement
The project will be divided into 2 checkpoints:
- In the first part of the project, no robust design is implement, only basic functions implement.
- Second part of the project will talk about the LSP implementation in unstable networking environment.
Part I Basic
First, we will implement the very basic Server, Client that can be connected, read, and write, with a naive lsp buffer. When the connect establish, each client has one lsp buffer, and each server add one new lsp buffer. The lsp buffer contain the state of sending and receive between the Client and Server.
Now, let’s talk about the Write(), and Read(). The Client and Server are similar in the Read and Write() behavior. Therefore, we used the Instance as a super class for them. Whenever the instance receive data from UDP read, it will receive it using the receiveMessages
, check the correctness of the message. Then, it will pass the udpMessage
struct to the processMessages
. The processMessages
is used to detect what kind of request it is, and pass the Request with type and send to the manageRequest
. The manageRequest
is will call specific function to handle the task.
Now, we will talk about the detail of saveInboundMessage()
and relayOutboundMessage()
. These two functions are actually calling the lsp buffer’s method of the same function name.
When the saveInboundMessage(msg *Message)
is called, it will immediately response the message with Ack
. When the instance is not asked to close, the lsp buffer will add the message into the recvMsgBuffer
, and then check all the message in the recvMsgBuffer
, and read them out into inboxChan
. Then the read() function can return the message from the inboxChan
.(We will talk about the readQueue
and inMsgQueue
later when we encounter the slow Read problem)
When the relayOutboundMessage(msg *Message)
function is called, the lsp buffer will add the message to the outMsgQueue
, then call the updateOutbox()
to check if the message can be send. The updateOutbox()
will check if the the message to be sent is not exceeding the Max unacknowledged message. Until the outMsgQueue
is not empty and the the Sequence Number of message to be send is not reaching the window maximum, the updateOutbox()
will send the message to the outboxChan
.
Ack & CAck
After the instance received an Ack, the manageRequest
will call the ackMessage
that delete the message of that sequence number in the lspBuffer.sentMsgBuffer
. After that, the lsbBuffer
will update the new nextUnackedSeqNum
and call updateOutbox()
, so that some more message can ready to send.
CAck is similar to Ack, the manageRequest
will call the cAckMessage()
. This function will delete all the message of the sequence number from nextUnackedSeqNum
to cAck.seqNum
Part II Robustness
When the network between the server and client is not stable, the message of data-sending and acknowledge.
Epoch
Epoch is a strategy in LSP, a timer to send message to let the other side know that the connection is still on. It basically do three things:
- Send reconnection if the the instance hasn’t receive message for a long time
- Send Ack as a heat beat, if the instance is not sending any message in this epoch. This is to make the other side know it is still alive and connected.
- For each unacknowledged message, if the message is reaching it back off limit, it will be resend again, and it will double the backoff time interval, which is to avoid too frequent unnecessary messaging.
Part III Close Instance
When the instance want to appropriately close the resource, it needs to conduct two steps.
- Close all the connections.
- Close all the go routines.
When we start the instance, the instance will start a go routing called closeManager
. It will first trigger the first step “Close all the connections”. The lsb buffer will change the pendingClose
to true
. Under that condition, the instance will:
- build no more new connection, the instance will
- not save any new in-data message, because the instance is to be closed, no need to receive
- not send any new data message to the other side.
After that, the instance will try to check if there no more unacked message, and no more message to be sent (Write()
request before Close()
). If not, wait some epoch, and the message will be acked, and will be sent. If the condition is satisfied, close the connection.
The second step of the close, end all the go routine is rather simple. Close the chan
they are using will give go routine a signal that it need to be end.