Can't get TClientSocket to receive buffer values

杀马特。学长 韩版系。学妹 提交于 2019-12-13 08:23:16

问题


On the server side, text is entered into a memobox. This text is then sent to the Server side using this code:

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;

Code on the Server side:

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;  

The text should be received, then written to a file, and be executed. I did however find the code online and modify it to work with TServerSocket and TClientSocket components. I created a successful connection between the client and server, but the above code just doesn't want to work. Maybe someone with more expertise could help me get this working. Any help would be greatly appreciated.


回答1:


Your code has no structured protocol to it. TCP is a stream of raw bytes, and you are sending everything as strings (and not doing a very good job of it - no error handling, no partial send/receive handling, etc). You need to delimit your fields/messages from one another. Then the receiver can look for those delimiters. You would have to read everything from the socket into an intermediate buffer, checking the buffer for a message terminator, and then extract only completed messages and process them as needed.

For example:

Common:

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;

Server:

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;

Client:

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;

Personally, I suggest you switch to Indy and let its TCP components handle these kind of details for you:

Server:

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:

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);


来源:https://stackoverflow.com/questions/27102283/cant-get-tclientsocket-to-receive-buffer-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!