Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi ServerSocket只接收一次_Delphi_Record_Serversocket - Fatal编程技术网

Delphi ServerSocket只接收一次

Delphi ServerSocket只接收一次,delphi,record,serversocket,Delphi,Record,Serversocket,我正在尝试通过clientsocket发送记录,并在serversocket上接收,一切正常,但只有在第一次发送后,我需要断开clientsocket,再次连接以再次发送 如果有人能帮我 以下是我如何接收信息的服务器端代码: procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); var LBuffer: TBytes; LMessageBuffer: TBytes; L

我正在尝试通过clientsocket发送记录,并在serversocket上接收,一切正常,但只有在第一次发送后,我需要断开clientsocket,再次连接以再次发送

如果有人能帮我

以下是我如何接收信息的服务器端代码:

procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  LBuffer: TBytes;
  LMessageBuffer: TBytes;
  LDataSize: Integer;
  LProtocol: TProtocol;
begin
  LDataSize := Socket.ReceiveLength;

if LDataSize >= szProtocol then begin
    try
      Socket.ReceiveBuf(LBuffer, SizeOf(LBuffer));
      LProtocol := BytesToProtocol(LBuffer);

  // check client command and act accordingly
  case LProtocol.Command of
    cmdConnect: begin
      Memo1.Lines.Add(Format('[%s][%s][%s]', ['Connect', LProtocol.Sender.UserName, TimeToStr(LProtocol.Sender.ID)]));
    end; // cmdConnect: begin
    cmdDisconnect: begin
      Memo1.Lines.Add(Format('[%s][%s][%s]', ['Disconnect', LProtocol.Sender.UserName, TimeToStr(LProtocol.Sender.ID)]));
    end; // cmdDisconnect: begin
  end;
finally
  ClearBuffer(LBuffer);
end;
  end;
end;
这里是客户端:

var
  LBuffer: TBytes;
  LProtocol: TProtocol;
  x : Integer;
begin
  InitProtocol(LProtocol);
  LProtocol.Command := cmdConnect;
  ClientData.UserName := Edit1.Text;
  ClientData.ID := Now;
  LProtocol.Sender := ClientData;
  LBuffer := ProtocolToBytes(LProtocol);
  try
    ClientSocket1.Socket.SendBuf(LBuffer, Length(LBuffer));
  finally
    ClearBuffer(LBuffer);
  end;
记录声明:

type
  TCommand = (
    cmdConnect,
    cmdDisconnect,
    cmdMessageBroadcast,
    cmdMessagePrivate,
    cmdScreenShotGet,
    cmdScreenShotData);

// client information structure, you can extend this based on your needs
type
  TClient = record
    UserName: string[50];
    ID: TDateTime;
  end; // TClient = record

// size of the client information structure
const
  szClient = SizeOf(TClient);

谢谢:)

t字节是一个动态数组,但您将其视为一个静态数组

在客户端代码中,您没有正确发送
LBuffer
。作为一个动态数组,
LBuffer
只是指向存储在内存中其他位置的数据的指针。因此,您需要取消引用
LBuffer
,将正确的内存地址传递到
SendBuf()

在服务器代码中,在读取字节之前,您甚至没有为
LBuffer
分配任何内存。而且,与客户端一样,在将其传递到
ReceiveBuf()时,需要取消对
LBuffer
的引用。告诉
ReceiveBuf()
要读取多少字节时,还需要使用正确的字节大小(
SizeOf(TBytes)
使用的值错误)

最后,您需要注意
SendBuf()
ReceiveBuf()
的返回值,因为它们返回的字节数可能比处理请求的字节数少!因此,您应该在循环中调用
SendBuf()
ReceiveBuf()

试试这个:

var
  LBuffer: TBytes;
  LProtocol: TProtocol;
  LBufferPtr: PByte;
  LBufferLen: Integer;
  LNumSent: Integer;
begin
  InitProtocol(LProtocol);
  LProtocol.Command := cmdConnect;
  ClientData.UserName := Edit1.Text;
  ClientData.ID := Now;
  LProtocol.Sender := ClientData;
  LBuffer := ProtocolToBytes(LProtocol);
  LBufferPtr := PByte(LBuffer);
  LBufferLen := Length(LBuffer);
  repeat
    LNumSent := ClientSocket1.Socket.SendBuf(LBufferPtr^, LBufferLen);
    if LNumSent = -1 then
    begin
      // if ClientSocket1.ClientType is set to ctNonBlocking,
      // uncomment this check ...
      {
      if WSAGetLastError() = WSAEWOULDBLOCK then
      begin
        // optionally call the Winsock select() function to wait
        // until the socket can accept more data before calling
        // SendBuf() again...
        Continue;
      end;
      }
      // ERROR!
      ClientSocket1.Close;
      Break;
    end; 
    Inc(LBufferPtr, LNumSent);
    Dec(LBufferLen, LNumSent);
  until LBufferLen = 0;
end;


您没有显示什么是
TProtocol
,如何定义
szProtocol
,或者如何将
TProtocol
序列化到字节数组或从字节数组序列化。字节数组的长度始终相同,还是长度可变?这对通过套接字发送它的方式有很大的不同。在粘贴时,我遇到了连接卡住的TIdTCPServer问题,因此我不再使用它。你有一些例子来管理和他的关系吗?我指的是一个记录,用于保存连接的客户端并在以后管理它们的客户端。在indy示例中,不需要检查ReadBytes是否小于协议,以避免ppl连接并发送奇怪的信息?@P–MBolsoni如果您在
TIdTCPServer
中管理客户端时遇到问题,请发布有关该问题的新问题,并显示您的代码。但是请注意,
TIdTCPServer
为您跟踪连接的客户端(在
TIdTCPServer.Contexts
属性中),您不需要手动跟踪它们。至于
ReadBytes()
,它会阻塞直到收到
szProtocol
字节数,这与您的
Socket类似。ReceiveLength
检查您的
TServerSocket
代码。您有责任验证接收到的字节是否符合预期。是的,我刚刚要求使用TIdTCPServer,在SocketServer上,我检查长度是否小于协议,并执行心跳以确定连接是否正确。后来我再次尝试使用TIdTCPServer,而不是现在。我已经解决了这个问题后,张贴这个话题,但我会分析你的答案。谢谢。Remy Lebeau用你的例子说明我如何向客户发送文本协议?
procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  LBuffer: TBytes;
  LDataSize: Integer;
  LProtocol: TProtocol;
  LBufferPtr: PByte;
  LBufferLen: Integer;
  LNumRecvd: Integer;
begin
  LDataSize := Socket.ReceiveLength;
  if LDataSize < szProtocol then Exit;

  SetLength(LBuffer, szProtocol);

  repeat
    // since you are validating ReceiveLength beforehand, ReceiveBuf()
    // *should not* return fewer bytes than requested, but it doesn't
    // hurt to be careful...
    LBufferPtr := PByte(LBuffer);
    LBufferLen := szProtocol;
    repeat
      LNumRecvd := Socket.ReceiveBuf(LBufferPtr^, LBufferLen);
      if LNumRecvd <= 0 then Exit;
      Inc(LBufferPtr, LNumRecvd);
      Dec(LBufferLen, LNumRecvd);
      Dec(LDataSize, LNumRecvd);
    until LBufferLen = 0;

    LProtocol := BytesToProtocol(LBuffer);

    // check client command and act accordingly
    case LProtocol.Command of
      cmdConnect: begin
        Memo1.Lines.Add(Format('[%s][%s][%s]', ['Connect', LProtocol.Sender.UserName, TimeToStr(LProtocol.Sender.ID)]));
      end;
      cmdDisconnect: begin
        Memo1.Lines.Add(Format('[%s][%s][%s]', ['Disconnect', LProtocol.Sender.UserName, TimeToStr(LProtocol.Sender.ID)]));
      end;
    end;
  until LDataSize < szProtocol;
end;
type
  PTIdBytes = ^TIdBytes;
var
  LBuffer: TBytes;
  LProtocol: TProtocol;
begin
  InitProtocol(LProtocol);
  LProtocol.Command := cmdConnect;
  ClientData.UserName := Edit1.Text;
  ClientData.ID := Now;
  LProtocol.Sender := ClientData;
  LBuffer := ProtocolToBytes(LProtocol);
  // TBytes and TIdBytes are technically the same thing under the hood,
  // but they are still distinct types and not assignment-compatible,
  // so using a dirty hack to pass a TBytes as a TIdBytes without having
  // to make a copy of the bytes...
  IdTCPClient1.IOHandler.Write(PTIdBytes(@LBuffer)^);
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
type
  PTBytes = ^TBytes;
var
  LBuffer: TIdBytes;
  LProtocol: TProtocol;

  // note: TIdTCPServer is a multi-threaded component, so you must
  // sync with the main thread when accessing the UI...
  procedure AddToMemo(const AStr: string);
  begin
    TThread.Synchronize(nil,
      procedure
      begin
        Memo1.Lines.Add(AStr);
      end
    );
  end;

begin
  // ReadBytes() can allocate the buffer for you...
  AContext.Connection.IOHandler.ReadBytes(LBuffer, szProtocol);

  // using a similar dirty hack to pass a TIdBytes as a TBytes
  // without making a copy of the bytes ...
  LProtocol := BytesToProtocol(PTBytes(@LBuffer)^);

  // check client command and act accordingly
  case LProtocol.Command of
    cmdConnect: begin
      AddToMemo(Format('[%s][%s][%s]', ['Connect', LProtocol.Sender.UserName, TimeToStr(LProtocol.Sender.ID)]));
    end;
    cmdDisconnect: begin
      AddToMemo(Format('[%s][%s][%s]', ['Disconnect', LProtocol.Sender.UserName, TimeToStr(LProtocol.Sender.ID)]));
    end;
  end;
end;