0

我正在尝试通过网络将 mp3 文件发送到由媒体播放器播放的服务器。我到目前为止的代码适用于字符串,但不适用于文件。有人可以告诉我我做错了什么吗?

这是源代码

客户:

procedure TForm1.Button1Click(Sender: TObject);
var
  MyStream : TFileStream;
  MP3 : TFileName;
  Len : Int64;
begin
  MP3 := GetCurrentDir + '\Clocks.mp3';
  MyStream := TFilestream.Create(MP3, fmOpenRead);
  try
    Len := Length(MP3);
    MyStream.WriteBuffer(Len, SizeOf(Len));
    MyStream.WriteBuffer(Pointer(MP3)^, Len * SizeOf(MP3));

    MyStream.Position := 0;

    TcpClient1.Active := True;
    TcpClient1.SendStream(MyStream);
    TcpClient1.Active := False;
  finally
    MyStream.Free
  end;
end;

服务器:

procedure TForm1.TcpServer1Accept(Sender: TObject;
  ClientSocket: TCustomIpClient);
var
  MP3 : TFileName;
  Len: Int64;
begin
  //Receives the message from the client
  ClientSocket.ReceiveBuf(Len, SizeOf(Len),0);
  SetLength(MP3, Len);
  ClientSocket.ReceiveBuf(Pointer(MP3)^,Len * SizeOf(MP3), 0);

  MediaPlayer1.FileName := MP3;
  MediaPLayer1.Open;
  MediaPlayer1.Play;
end;

当程序尝试写入流时问题就开始了,所以我什至不确定接收端是否有效

附言。我是一个初级程序员

4

2 回答 2

2

首先,您要发送宽字符串,这意味着您必须使用:

Len :=Length(mp3) * SizeOf(Char);

其次,你不需要演员表。这应该很好:

myStream.Write(mp3[1],Len);

第三,如果 tcpSocket 是非阻塞的,那么你不能在发送流之后直接禁用它,这会在连接开始之前切断连接。使用事件了解数据何时发送。

为避免这一切,请在您的流上使用 TReader 和 TWriter。这样你就不必关心两边的字符串数据大小,只关心流的大小。

提示:始终首先发送一个魔法值(一个常量值),这样您就可以在另一端检查它是否是一个有效的数据包。

这是您可以做到的一种方法:

Procedure TForm1.WriteMP3File(aFilename:string);
var
  mData:  TMemoryStream;
  mFile:  TFilestream;
  mWriter:  TWriter;
Begin
  mData:=TMemoryStream.Create;
  try
    mFile:=TFileStream.Create(aFilename,fmOpenRead);
    try
      mWriter:=TWriter.Create(mData);
      try
        // Write header code, any number will do
        mWriter.WriteInteger($BABE);

        // Write the filename
        mWriter.WriteString(ExtractFileName(aFilename));

        //Write size of MP3 file
        mWriter.WriteVariant(mFile.Size);

        //Push all data into the mData stream
        mWriter.FlushBuffer;

        //Append MP3 file
        mData.CopyFrom(mFile,mFile.Size);
      finally
        mWriter.Free;
      end;

      mData.Position:=0;
      socket.sendstream(mData);

    finally
      mFile.Free;
    end;
  finally
    mData.Free;
  end;
end;

并在服务器上反向读取它:

Procedure TForm1.ReadMP3File(aMp3PlayPath:String;aStream:TStream);
var
  mReader:  TReader;
  mFileData:  TFileStream;
  mName:    String;
  mSize:    Int64;
Begin
  try

    mReader:=TReader.Create(aStream);
    try
      if mReader.ReadInteger=$BABE then
      Begin
        mName:=mReader.ReadString;
        mSize:=mReader.ReadVariant;
      end else
      raise exception.Create('Invalid datapackage error');
    finally
      mReader.Free;
    end;

    if mSize>0 then
    Begin
      mFileData:=TFileStream.Create(aMp3PlayPath + mName,fmCreate);
      try
        mFiledata.CopyFrom(aStream,mSize);
      finally
        mFiledata.Free;
      end;
    end;

    //Now you can play the file


  finally
    //Release stream or do it elsewhere
    aStream.Free;
  end;
end;
于 2013-11-07T09:07:50.373 回答
0

首先这段代码:

MyStream.WriteBuffer(Len, SizeOf(Len));
MyStream.WriteBuffer(Pointer(MP3)^, Len * SizeOf(MP3));

在此您试图更改 MP3 文件的内容,它会损坏您的 mp3 文件。幸运的是,您打开了文件,fmOpenRead这就是MyStream.WriteBuffer失败的原因。

如果要将文件名发送到服务器,则必须单独发送(例如,使用TTcpClient.SendBuf.

在服务器端,您目前只读取文件名并忽略文件内容本身。

ClientSocket.ReceiveBuf(Len, SizeOf(Len),0); // this reads length of the filename
SetLength(MP3, Len);
ClientSocket.ReceiveBuf(Pointer(MP3)^,Len * SizeOf(MP3), 0); // this reads filename itself
// Now you should receive file contents itself and store it on the server 

将文件存储在服务器上后,您可以使用 MediaPlayer1 播放它。如果您不将文件存储在服务器中,则只能在服务器和客户端位于同一台计算机上时播放文件(除非媒体播放器使用 url 或网络路径作为文件名)。

于 2013-11-07T09:12:19.623 回答