我正在用 C++ 通过 TCP 编写自定义 RTSP 服务器。下面是我的代码。
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();
}
}
}
我使用VLC作为RTSP客户端通过
rtsp://my_ip_address:8000
连接到服务器并使用Wireshark分析通信。完全没有错误。结果,他们没有通过 RTSP 进行通信。他们只通过 TCP 进行通信。 VLC 没有通过 RTSP 发送请求,例如OPTIONS
、DESCRIBE
、SETUP
或 PLAY
但它通过 TCP 发送这些请求。所以服务器也没有发送 RTSP 响应。
但是服务器确实得到了VLC请求的消息正文
OPTIONS
,DESCRIBE
和SETUP
.
我做错了什么?我不是在寻找一个完整的解决方案。一个正确的方向也是感恩!
你现在可能已经弄明白了,但无论如何。这里有几个问题,其中一个已经被其他人指出了。
RTSP 是 TCP/UDP 之上的协议。它有点同时使用,但这超出了问题的范围。
我注意到的第二件事,这个应用程序应该给你一个编译错误:
std::string request_message(data);
变量data
在你的代码中的任何地方都不存在。您正在将套接字的内容读入buffer
。所以缓冲区的内容永远不会被解析。你是故意的std::string request_message(buffer);
吗?
我想指出的最后一部分是您使用的是流式套接字。这很重要,因为这意味着不能保证所有数据都在一个数据包中发送,即使数据很小。事实上,它可能一次是 1 个字节,虽然不太可能,但最好编写代码时就好像它可能会发生一样。您还可以在一次
read()
调用中一次接收多个数据包。使用流式套接字,您必须使用称为成帧的概念使用已知边界系统重新组装数据包,以便您可以检测收到的每个数据包的开始/结束并逐块重新组装它们。
那么为什么你的服务器没有响应?可能是因为您只有一次调用
read()
并且此套接字类型不能保证一次获取所有数据。当使用像 TCP 这样的流套接字时,您绝对需要同时实现成帧和重组数据包的方法。我已经实现了许多这样的协议/标准,但我还没有完成 RTSP。我确实看了一眼,它似乎遵循类似于 HTTP 的东西。有很多方法可以实现这一点,但总体思路是:
read()
read()
之后解析数据并检查标题结束序列(
), 如果你没有找到它转到#1 和 read()
直到你找到read()
并检查缓冲区,直到全部完成我会为您的缓冲区推荐 boost::asio::streambuf,因为它使这个过程更容易实现。