I am writing a custom RTSP server over TCP in C++. Below is my code.
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8000);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0)
{
perror("accept");
exit(EXIT_FAILURE);
}
read(new_socket, buffer, 1024);
std::string request_message(data);
std::string request_type = request_message.substr(0, request_message.find(" "));
std::string response_data;
std::string sequence_number;
std::string sub_str;
if (request_type == "OPTIONS")
{
char current_date[200];
time_t tt = time(NULL);
strftime(current_date, sizeof current_date, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
std::string current_date_str(current_date);
sub_str = request_message.substr(0, request_message.find("User-Agent:"));
sequence_number = sub_str.substr(sub_str.find("CSeq: ") + 6);
response_data = "RTSP/1.0 200 OK\r\nCSeq: " + sequence_number + "\r\nPublic: DESCRIBE, GET_PARAMETER, SETUP, SET_PARAMETER, PLAY, RECORD, PAUSE, TREADOWN\r\nServer: My Custom RSTP Server\r\nDate: " + current_date_str + "\r\n";
send(new_socket, response_data , response_data.size(), 0);
}
else if (request_type == "DESCRIBE")
{
sub_str = request_message.substr(0, request_message.find("User-Agent:"));
sequence_number = sub_str.substr(sub_str.find("CSeq: ") + 6);
std::string cnt = "v=0\r\no=- " + std::to_string(rand()) + " 1 IN IP4 my_ip_address\r\ns=\r\nt=0 0\r\nm=video 0 RTP/AVP 26\r\nc=IN IP4 0.0.0.0\r\n";
response_data = "RTSP/1.0 200 OK\r\nCSeq: " + sequence_number + "\r\nCache-control: no-cache\r\nContent-Type: application/sdp\r\nContent-Base: rtsp://my_ip_address:8000\r\nContent-length: " + std::to_string(cnt.size()) + "\r\n" + cnt;
send(new_socket, response_data , response_data.size(), 0);
}
else if (request_type == "SETUP")
{
char current_date2[200];
time_t tt2 = time(NULL);
strftime(current_date2, sizeof current_date2, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt2));
std::string current_date_str2(current_date2);
std::string transport = request_message.substr(request_message.find("Transport:"));
transport = transport.substr(0, transport.find("\r\n"));
sub_str = request_message.substr(0, request_message.find("Transport:"));
sequence_number = sub_str.substr(sub_str.find("CSeq: ") + 6);
response_data = "RTSP/1.0 200 OK\r\nCSeq: " + sequence_number + "\r\n" + transport + ";server_port=8000-8001;ssrc=4b003a1e\r\nDate: " + current_date_str2 + "\r\nSession: 1185d20035702ca\r\n";
send(new_socket, response_data , response_data.size(), 0);
}
else if (request_type == "PLAY")
{
std::string transport = request_message.substr(request_message.find("User-Agent:"));
transport = transport.substr(0, transport.find("\r\n"));
sub_str = request_message.substr(0, request_message.find("User-Agent:"));
sequence_number = sub_str.substr(sub_str.find("CSeq: ") + 6);
response_data = "RTSP/1.0 200 OK\r\nCSeq: " + sequence_number + "\r\nSession: 1185d20035702ca\r\n";
send(new_socket, response_data , response_data.size(), 0);
for(;;)
{
sendFrameData();
}
}
}
I use VLC as the RTSP client to connect to the server via rtsp://my_ip_address:8000 and use Wireshark to analyze the communication. There is no error at all. As result, they did not communicate over RTSP. They only communicated over TCP. VLC did not send requests over RTSP e.g. OPTIONS, DESCRIBE, SETUP or PLAY but it sent these requests over TCP. So the server also did not send RTSP responses.
But the server did get the message body of the VLC requests OPTIONS, DESCRIBE and SETUP.
What did I do wrong? I am not looking for a complete solution. A right direction is also thankful!
You've probably got this figured out by now, but regardless. There are a few issues here, one of which was already pointed out by someone else.
RTSP is a protocol on top of TCP/UDP. It kinda uses both, but that is out of scope for the question.
The second thing I noticed, this application should have given you a compiling error:
std::string request_message(data);the variabledatadoes not exist anywhere in your code. You are reading the contents of the socket intobuffer. So the contents of the buffer never get parsed. Did you intendstd::string request_message(buffer);?The last part I'd like to point out is that you are using a streaming socket. This is important, because it means that all data is not guaranteed to be sent in one packet, even if it is very small. In fact, it could be 1 byte at a time, although very unlikely, but best to write your code as if it could happen. You could also receive multiple packets at once in a single
read()call. With a streaming socket you must use a concept called framing to reassemble the packets using a known boundary system so you can detect the start/end of each packet you receive and reassemble them chunk by chunk.So why did your server not respond? likely because you only have one call to
read()and this socket type does not guarantee getting all the data at once. You absolutely need to implement both framing and a means to reassemble the packets when using a stream socket like TCP. I've implemented many protocols/standards like this, but i've not done RTSP yet. I did glance at it, and it seems to follow something similar to HTTP. There are many ways to implement this, but the general idea is:read()read()and check for the header ending sequence (\r\n\r\n), if you dont find it goto #1 andread()some more until you doread()and checking the buffer until you get it allI would recommend boost::asio::streambuf for your buffer, because it makes this process much easier to implement.