TCP客户端无法从RTSP服务器接收响应

6
在Delphi XE2中,我正在使用TTCPClient组件与RTSP服务器通信。在尝试多次未能从服务器获得响应后,我将项目切换为通过端口80发送HTTP请求(而不是554用于RTSP),并尝试向网站(特别是www.google.com)发送请求。但我仍然没有收到任何响应。
在主窗体(Form1)上有一个名为ClientTTCPClient组件,一个名为LogTMemo控件,一个名为txtHostTEdit控件和一个TBitBtn控件。以下是相关代码的部分内容: 连接到服务器
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  if Client.Active then Client.Disconnect;
  Client.RemoteHost:= txtHost.Text;
  Client.RemotePort:= '80'; // '554';
  Client.Connect;
end;

OnConnect Event Handler (HTTP)

procedure TForm1.ClientConnect(Sender: TObject);
var
  S: String;
begin
  Client.Sendln('GET / HTTP/1.0');
  Client.SendLn('');
end;

OnConnect事件处理程序(RTSP)

procedure TForm1.ClientConnect(Sender: TObject);
var
  S: String;
begin
  Client.SendLn('OPTIONS * RTSP/1.0');
  Client.SendLn('CSeq:0');
  Client.SendLn('');
end;

OnReceive Event Handler

procedure TForm1.ClientReceive(Sender: TObject; Buf: PAnsiChar;
  var DataLen: Integer);
var
  S, R: String;
begin
  S:= Client.Receiveln;
  while S <> '' do begin
    R:= R+ S;
    S:= Client.Receiveln;
  end;
  Log.Lines.Append('> RECEIVED ' + R);
end;

OnError事件处理程序

procedure TForm1.ClientError(Sender: TObject; SocketError: Integer);
begin
  Log.Lines.Append('> ERROR '+IntToStr(SocketError));
end;
OnReceive事件从未被调用,我连接到的任何服务器都没有返回任何内容。我做错了什么?参考资料如下:
我正在使用的相机是Grandstream GXV3601LL更新:我得出结论,问题出在RTSP服务器上,并在Grandstream网站的论坛上提出了一个问题。该代码与其他服务器连接正常工作。

实际上,它适用于HTTP,但尚未适用于RTSP... - Jerry Dodge
1
我可以问一下为什么你在使用老旧的TTCPClient吗?Indy更容易使用... - whosrdaddy
我只是随便做了一个样例,以确保在开始真正的项目之前,我已经掌握了一般的通信概念。 - Jerry Dodge
1
看起来我必须把这个问题转给相机的供应商,因为它可以正常地使用 RTSP 源,例如 Youtube。 - Jerry Dodge
2个回答

6

这对我有效,取决于您是否处于阻塞模式:

unit Unit11;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Sockets, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls;

type
  TForm1 = class(TForm)
    IdTCPClient1: TIdTCPClient;
    TcpClient1: TTcpClient;
    Memo1: TMemo;
    procedure TcpClient1Connect(Sender: TObject);
    procedure TcpClient1Receive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 TcpClient1.BlockMode := bmBlocking;
 TcpClient1.RemoteHost := 'www.google.com';
 TcpClient1.RemotePort := '80';
 TcpClient1.Connect;
end;

procedure TForm1.TcpClient1Connect(Sender: TObject);

var s : string;

begin
 memo1.Lines.Add('connected');
 TcpClient1.Sendln('GET /');
 s := TcpClient1.Receiveln;
 memo1.Lines.Add(S);
end;

end.

编辑

这里有一个关于RTSP服务器(以Youtube为例)的实际案例,我使用了Indy IdTcpClient。

unit Unit11;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Sockets, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Client: TIdTCPClient;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);

var s : string;

begin
 Client.Host := 'v5.cache6.c.youtube.com';
 Client.Port := 554;
 Client.Connect;
 Client.IOHandler.Writeln('OPTIONS * RTSP/1.0');
 Client.IOHandler.Writeln('CSeq: 1');
 Client.IOHandler.Writeln('');

 s := Client.IOHandler.ReadLn;
 Memo1.Lines.Add(s);
 s := Client.IOHandler.ReadLn;
 Memo1.Lines.Add(s);
end;

end.

嗯,使用你的示例对我有效,但是当我输入RTSP相机的IP地址时,它会出现套接字错误10053。 - Jerry Dodge
错误10053是WSAECONNABORTED,因此似乎相机不允许连接。我们在谈论什么品牌的相机? - whosrdaddy
我还验证了所有相机的设置,包括允许匿名未经身份验证的查看的设置。 - Jerry Dodge
这是可行的,你只需要包含一个授权元素。请参考此页面获取指针:http://forum.videolan.org/viewtopic.php?f=13&t=44780 - whosrdaddy
我想我必须接受你的答案,因为它确实回答了问题,但我仍然有这些特定摄像头不响应的问题。我已经在Grandstream的论坛上发布了一个问题。 - Jerry Dodge

2
OnReceive事件没有被调用的原因是因为TTCPClient不是一个异步组件,就像你试图对待它那样。 OnReceive事件与旧的TClientSocket.OnRead事件的工作方式不同OnReceive事件只在ReceiveBuf()方法内部被调用(ReceiveLn()在内部调用ReceiveBuf())。 传递到OnReceive事件的数据是ReceiveBuf()方法返回的相同数据。你遇到了进退两难的情况 - 在调用ReceiveLn()之前等待OnReceive事件触发,但是在先调用ReceiveLn()之前OnReceive事件不会被触发。如果你想以异步方式使用TTCPClient,则必须定期调用其ReceiveLn()方法,可以在计时器或工作线程中调用,而不是OnReceive事件中调用。 TTCPClient组件是Kylix的旧CLX框架的一部分。它不是VCL甚至不是FireMonkey的一部分,不应再使用。要么使用旧的TClientSocket组件(虽然已弃用但仍可用),要么更改为其他组件库,例如Indy。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接