如何在 C++ 中通过 TCP 响应 RTSP 客户端

问题描述 投票:0回答:1

我正在用 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
.

我做错了什么?我不是在寻找一个完整的解决方案。一个正确的方向也是感恩!

c++ c rtsp rtsp-server
1个回答
0
投票

你现在可能已经弄明白了,但无论如何。这里有几个问题,其中一个已经被其他人指出了。

RTSP 是 TCP/UDP 之上的协议。它有点同时使用,但这超出了问题的范围。

我注意到的第二件事,这个应用程序应该给你一个编译错误:

std::string request_message(data);
变量
data
在你的代码中的任何地方都不存在。您正在将套接字的内容读入
buffer
。所以缓冲区的内容永远不会被解析。你是故意的
std::string request_message(buffer);
吗?

我想指出的最后一部分是您使用的是流式套接字。这很重要,因为这意味着不能保证所有数据都在一个数据包中发送,即使数据很小。事实上,它可能一次是 1 个字节,虽然不太可能,但最好编写代码时就好像它可能会发生一样。您还可以在一次

read()
调用中一次接收多个数据包。使用流式套接字,您必须使用称为成帧的概念使用已知边界系统重新组装数据包,以便您可以检测收到的每个数据包的开始/结束并逐块重新组装它们。

那么为什么你的服务器没有响应?可能是因为您只有一次调用

read()
并且此套接字类型不能保证一次获取所有数据。当使用像 TCP 这样的流套接字时,您绝对需要同时实现成帧和重组数据包的方法。我已经实现了许多这样的协议/标准,但我还没有完成 RTSP。我确实看了一眼,它似乎遵循类似于 HTTP 的东西。有很多方法可以实现这一点,但总体思路是:

  1. 继续打电话
    read()
  2. 在每个
    read()
    之后解析数据并检查标题结束序列( ), 如果你没有找到它转到#1 和
    read()
    直到你找到
  3. 找到结尾序列后,提取标题属性并查找“Content-Length: 126”
  4. 提取内容长度大小。如果它不存在,或者是 0,那么你就读完了 goto #6。否则转到#5
  5. 如果 Content-Length 不为 0,则必须继续读取缓冲区和套接字,直到获得剩余字节。消息正文中没有结束分隔符,它完全由 Content-Length 值决定。继续调用
    read()
    并检查缓冲区,直到全部完成
  6. 处理您组装的数据包
  7. 如果你的缓冲区中还有数据,你必须回到#2 处理,因为它是另一个数据包的开始。否则,如果您的缓冲区在处理后为空,则转到 #1 并等待更多数据

我会为您的缓冲区推荐 boost::asio::streambuf,因为它使这个过程更容易实现。

© www.soinside.com 2019 - 2024. All rights reserved.