Delphi本地网络应用程序

3

我正在尝试学习如何制作服务器客户端应用程序等相关技术。我正在尝试在所有客户端中绘制圆形(鼠标单击时),这就是我尝试做的事情。但是它没有起作用——没有错误,但表格是空的。我需要修复什么?

客户端代码:

unit Client;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, {Figure, Ball,} IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, ScktComp;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Button1: TButton;
    ClientSocket: TClientSocket;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);



  private

  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  f:boolean;
  p:MyPoint;
  s:MyPoint;
  z:TCanvas;
  obj: MyFigure;
  pX, pY:Integer;
  myBuf: array[1..32] of Integer;
  dataBuf: array[1..32] of Integer;
implementation

{$R *.dfm}



procedure TForm1.FormCreate(Sender: TObject);
begin

  Timer1.Enabled:=false;
  Timer1.Interval:=5;
  z:=Form1.Canvas;//TCanvas.Create;

  Button1.Caption:='Пуск';


  f:=false;

  ClientSocket.Port:=1234;
  ClientSocket.Active:= False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if not f then
 begin
  Timer1.Enabled:=true;
  Button1.Caption:='Стоп';
  f:=not f;
 end
else
 begin
  Timer1.Enabled:=false;
  Button1.Caption:='Пуск';
  ClientSocket.Active:= True;
  f:=not f;
 end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
 //z.Lock;

  //z.Brush.Color:=ClWhite;
  //z.FillRect(Canvas.ClipRect);

  //obj.Draw(z);
  if ClientSocket.Active  then
     ClientSocket.Socket.ReceiveBuf(dataBuf, 32);

  z.Brush.Color:=ClRed; 
  z.Ellipse(dataBuf[1] + 10, dataBuf[2] + 10,dataBuf[1] - 10, dataBuf[2] - 10);

//z.Unlock;
end;



procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
      ClientSocket.Active := false;
end;


procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin

  myBuf[1]:=X;
  myBuf[2]:=Y;
  if ClientSocket.Active  then
     ClientSocket.Socket.SendBuf(myBuf, 32);

end;

end.

服务器

unit ServerProject;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ScktComp;

type
  TForm1 = class(TForm)
    ServerSocket1: TServerSocket;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ServerSocket1ClientWrite(Sender: TObject;
      Socket: TCustomWinSocket);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  sBufer : array [1..32] of Integer;
implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ServerSocket1.Port:=1234;
  ServerSocket1.Active := True;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ServerSocket1.Active := false;
end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  i:integer;

begin
  for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do
  begin
    with ServerSocket1.Socket.Connections[i] do
    begin
      ReceiveBuf(sBufer, 32);
    end;
  end;
end;

procedure TForm1.ServerSocket1ClientWrite(Sender: TObject;
  Socket: TCustomWinSocket);
var
  i:integer;

begin
  for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do
  begin
    with ServerSocket1.Socket.Connections[i] do
    begin
      SendBuf(sBufer, 32);
    end;
  end;

end;

end.
1个回答

0

你的绘画代码放错了位置,而且绘制的对象也不对。在Windows程序中,你应该在响应WM_PAINT消息时进行绘制。但你没有这样做。此外,你必须在由调用BeginPaint提供的设备上下文中进行绘制。

VCL为你包装了所有这些细节,但你仍然需要遵循规则。在你的情况下,我建议你向表单添加一个TPaintBox组件。然后为绘画框实现一个OnPaint事件处理程序。最后,每当你想要重新绘制绘画框时,例如在计时器上,调用绘画框的Invalidate方法。

我猜你想让每个新的椭圆形在之前绘制的椭圆形上添加。这种情况下,最好先将它们绘制到屏幕外的位图上,然后当您开始在绘画框中绘制时,再将该位图绘制到绘画框中。重点是一个窗口需要能够完全重新绘制自己。当您绘制到屏幕设备时,下次该窗口需要绘制时,您所绘制的内容会丢失。因此,如果被要求,应用程序负责能够在任何时候绘制其全部。

更普遍地说,我敦促你停止使用全局变量。它们将给你带来无尽的麻烦。尽可能使用局部变量。如果您需要状态在不同的方法调用之间保持不变,请使用成员变量。指导原则是使用尽可能狭窄的范围。

您当前的设计使用定时器轮询新数据。这是非常糟糕的方法。最有效和最有效的方法是使用同步阻塞通信。Indy采用这种方法。Windows套接字组件通常以异步模式使用。无论这两种方法的相对优劣如何,您都不应该使用计时器轮询。如果您确实使用异步通信,那么请通过处理事件来响应新数据,而不是轮询。

你的程序目前正在尝试混合使用GDI绘图和网络通信。我建议你先逐个掌握这些概念。学会在没有通信干扰的情况下进行绘图。然后,当你掌握了绘图技巧后,再尝试引入通信方面。


1
但是如果我只写类似于z.Ellipse(X + 10, Y + 10, X- 10, Y - 10);procedure TForm1.FormMouseDown中,它会正确地绘制椭圆。 - DanilGholtsman
好的,当我说你需要在 WM_PAINT 响应中进行绘制时,你可能不相信我。 - David Heffernan
1
不,我相信你(但实际上我并没有完全理解所有的事情,我的英语不是很好),但是据我所理解,我在这里做的就是从一些客户端数组中向服务器发送数据,然后再将这个数组从服务器发送给所有客户端。如果像 z.Ellipse(X + 10, Y + 10, X- 10, Y - 10); 这样的东西可以工作(我已经检查过了),为什么 z.Ellipse(dataBuf[1] + 10, dataBuf[2] + 10,dataBuf[1] - 10, dataBuf[2] - 10); 就不能工作呢? - DanilGholtsman
我猜测 dataBuf 不包含你认为它包含的内容。在我看来,你的代码太混乱了,以至于你很难取得实质性的进展。你需要尝试一次只学习一件事情。目前你的代码有太多不良设计的方面,这些不同方面的问题会混淆你学习和理解的尝试。首先,我会让绘画代码正确无误。 - David Heffernan
1
好的。所以通信部分不起作用。下一步是隔离程序的那一部分,并找出原因。集中精力逐个方面解决问题。您已经构建了一个庞大的程序,其中很多部分都失败了。您不知道该去哪里寻找。逐个构建每个部分,只有在每个部分单独运行良好时才继续前进。 - David Heffernan
显示剩余9条评论

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