无法让TClientSocket接收缓冲区值

时间:2014-11-24 10:09:59

标签: sockets delphi buffer delphi-7

在服务器端,文本输入到memobox中。然后使用以下代码将此文本发送到服务器端:

var
 ftmpstr     :String;
 buf   :array[0..255] of char;
 msize, nyites :dword;
 i             :Integer;

..
Command := Socket.ReceiveText;
if split(Command,'|', 0) = 'IBATCH' then
  begin
  ftmpstr := IBat.Memo1.Text;
  nyites := 1;
  msize := length(ftmpstr);
  Server.Socket.Connections[ListView1.Selected.Index].SendText(IntToStr(msize));
  while msize>255 do
  begin
  for i := 0 to 255 do
  buf[i] := ftmpstr[nyites+i];
  Server.Socket.Connections[Form1.ListView1.Selected.Index].SendBuf(buf,256);
  dec(msize,256);
  inc(nyites,256);
  end;
  if msize>0 then
  begin
  for i := 0 to msize-1 do
  buf[i] := ftmpstr[nyites+i];
  Server.Socket.Connections[Form1.ListView1.Selected.Index].SendBuf(buf,msize);
  end;
  end;

服务器端的代码:

Socket.SendText('IBATCH');
  ftmpstr:='';
  mbytesleft := strtoint(Socket.ReceiveText);
   SetLength(ftmpstr,mbytesleft);
   nyites:=1;
  while mbytesleft>255 do
  begin
  Socket.ReceiveBuf(buf,256);
  for I:=0 to 255 do
  ftmpstr[nyites+i]:=buf[i];
  dec(mbytesleft,256);
  inc(nyites,256);
  end;

  if mbytesleft>0 then begin
  Socket.ReceiveBuf(buf,mbytesleft);
  for I:=0 to mbytesleft-1 do
  ftmpstr[nyites+i]:=buf[i];
  end;
  nfile:=TempDir+IntToStr(GetTickCount)+'.cmd';
  AssignFile(devf,nfile);
  Rewrite(devf);
  Writeln(devf,ftmpstr);
  closefile(devf);
  Sleep(50);
  ShellExecute(0,'Open',pchar(nfile),nil,nil,SW_SHOWNORMAL);
  end;  

应接收文本,然后将其写入文件并执行。 然而,我确实在线找到了代码并修改它以使用TServerSocket和TClientSocket组件。我在客户端和服务器之间建立了成功的连接,但上面的代码并不想工作。也许拥有更多专业知识的人可以帮助我实现这一目标。 任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

您的代码没有结构化协议。 TCP是一个原始字节流,你将所有内容作为字符串发送(并没有做得很好 - 没有错误处理,没有部分发送/接收处理等)。您需要将字段/消息彼此分隔开。然后接收器可以查找那些分隔符。您必须从套接字读取所有内容到中间缓冲区,检查缓冲区是否有消息终止符,然后只提取已完成的消息并根据需要处理它们。

例如:

普通的:

type
  TSocketBuffers = class
  private
    fSocket: TCustomWinSocket;
    fInput: TMemoryStream;
    fOutput: TMemoryStream;

    procedure Compact(Stream: TMemoryStream);

  public
    constructor Create(ASocket: TCustomWinSocket);
    destructor Destroy; override;

    procedure AppendToInput: Boolean;
    function ReadInput(var Msg: string): Boolean;

    function SendOutput(const Msg: string): Boolean;
    function FlushOutput: Boolean;
  end;

constructor TSocketBuffers.Create(ASocket: TCustomWinSocket);
begin
  inherited Create;
  fSocket := ASocket;
  fInput := TMemoryStream.Create;
  fOutput := TMemoryStream.Create;
end;

destructor TSocketBuffers.Destroy;
begin
  fInput.Free;
  fOutput.Free;
  inherited;
end;

procedure TSocketBuffers.Compact(Stream: TMemoryStream);
begin
  if Stream.Position < Stream.Size then
  begin
    Move(Pointer(Longint(Stream.Memory) + Stream.Position)^, Stream.Memory^, Stream.Size - Stream.Position);
    Stream.Size := Stream.Position;
    Stream.Position := 0;
  end else begin
    Stream.Clear;
  end;
end;

function TSocketBuffers.AppendToInput: Boolean;
var
  buf: array[0..255] of Byte;
  nBuf: Integer;
begin
  nBuf := fSocket.ReceiveBuf(buf[0], sizeof(buf));
  if nBuf > 0 then
  begin
    fInput.Seek(0, soEnd);
    fInput.WriteBuffer(buf[0], nBuf);
    Result := True;
  end else begin
    Result := False;
  end;
end;

function TSocketBuffers.ReadInput(var Msg: string): Boolean;
var
  b: Byte;
  tmp: string;
  needed: Integer;
begin
  Result := False;
  Msg := '';
  fInput.Position := 0;
  while fInput.Position < fInput.Size do
  begin
    fInput.ReadBuffer(b, 1);
    if b = Ord('|') then
    begin
      SetString(tmp, PAnsiChar(fInput.Memory), fInput.Position-1);
      needed := StrToInt(tmp);
      if needed > 0 then
      begin
        if (fInput.Size - fInput.Position) < Int64(needed) then
          Exit;
        SetLength(Msg, needed);
        fInput.ReadBuffer(PAnsiChar(Msg)^, needed);
      end;
      Compact(fInput);
      Result := True;
      Exit;
    end;
  end;
end;

function TSocketBuffers.SendOutput(const Msg: string): Boolean;
var
  tmp: AnsiString;
  nSent: Integer;
begin
  Result := True;
  tmp := IntToStr(Length(Msg)) + '|' + Msg;
  if fOutput.Size = 0 then
  begin
    repeat
      nSent := fSocket.SendBuf(PAnsiChar(tmp)^, Length(tmp));
      if nSent < 0 then
      begin
        if WSAGetLastError() <> WSAEWOULDBLOCK then
        begin
          Result := True;
          Exit;
        end;
        Break;
      end;
      Delete(tmp, 1, nSent);
    until tmp = '';
  end;
  if tmp <> '' then
  begin
    fOutput.Seek(0, soEnd);
    fOutput.WriteBuffer(PAnsiChar(tmp)^, Length(tmp));
  end;
end;

function TSocketBuffers.FlushOutput: Boolean;
var
  buf: array[0..255] of Byte;
  nBuf, nSent: Integer;
begin
  Result := True;

  fOutput.Position := 0;
  while fOutput.Position < fOutput.Size do
  begin
    nBuf := fOutput.Read(buf[0], sizeof(buf));
    nSent := fSocket.SendBuf(buf[0], nBuf);
    if nSent < 0 then
    begin
      if WSAGetLastError() <> WSAEWOULDBLOCK then
      begin
        fOutput.Seek(-nBuf, soCurrent);
        Result := False;
      end;
      Break;
    end;
  end;

  if fOutput.Position > 0 then
    Compact(fOutput);
end;

服务器:

procedure TForm1.ServerSocketConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  Socket.Data := TSocketBuffers.Create(Socket);
end;

procedure TForm1.ServerSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  TSocketBuffers(Socket.Data).Free;
end;

procedure TForm1.ServerSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
  bufs: TSocketBuffers;
  Command: string;
begin
  bufs := TSocketBuffers(Socket.Data);

  if not bufs.AppendToInput then Exit;

  while bufs.ReadInput(Command) do
  begin
    if split(Command, '|', 0) = 'IBATCH' then
      bufs.SendOutput(IBat.Memo1.Text);
  end;
end;

procedure TForm1.ServerSocketWrite(Sender: TObject; Socket: TCustomWinSocket);
begin
  TSocketBuffers(Socket.Data).FlushOutput;
end;

客户端:

bufs := TSocketBuffers.Create(Client.Socket);

...

// this is assuming TClientSocekt is set to blocking mode
// otherwise you have to use the OnRead and OnWrite events...

if bufs.SendOutput('IBATCH') then
begin
  while bufs.AppendToInput do
  begin
    if bufs.ReadInput(ftmpstr) then
    begin
      nfile := TempDir+IntToStr(GetTickCount) + '.cmd';
      AssignFile(devf, nfile);
      Rewrite(devf);
      Writeln(devf, ftmpstr);
      closefile(devf);
      Sleep(50);
      ShellExecute(0, nil, PChar(nfile), nil, nil, SW_SHOWNORMAL);
    end;
    Break;
  end;
end;

就个人而言,我建议你切换到Indy并让它的TCP组件为你处理这些细节:

服务器:

type
  TIBatSync = class(TIdSync)
  protected
    fText: string;
    procedure DoSynchronize; override;
  public
    class function GetText: string;
  end;

procedure TIBatSync.DoSynchronize;
begin
  fText := Form1.IBat.Memo1.Text;
end;

class function TIBatSync.GetText: string;
begin
  with Create do
  try
    Synchronize;
    Result := fText;
  finally
    Free;
  end;
end;

procedure TForm1.IdTCPServerExecue(AContext: TIdContext);
var
  Command, tmp: string;
begin
  tmp := AContext.Connection.IOHandler.ReadLn('|');
  Command := AContext.Connection.IOHandler.ReadString(StrToInt(tmp));
  if split(Command, '|', 0) = 'IBATCH' then
  begin
    tmp := TIBatSync.GetText;
    AContext.Connection.IOHandler.Write(Length(tmp) + '|' + tmp);
  end;
end;

客户端:

Client.IOHandler.Write('6|IBATCH');

ftmpstr := Client.IOHandler.ReadLn('|');
ftmpstr := Client.IOHandler.ReadString(StrToInt(ftmpstr));

nfile := TempDir+IntToStr(GetTickCount) + '.cmd';
AssignFile(devf, nfile);
Rewrite(devf);
Writeln(devf, ftmpstr);
closefile(devf);
Sleep(50);
ShellExecute(0, nil, PChar(nfile), nil, nil, SW_SHOWNORMAL);