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.
4

1 に答える 1

0

Your painting code is in the wrong place and is painting to the wrong thing. In Windows programs you are meant to paint in response to a WM_PAINT message. You are not doing so. What's more, you have to paint on a device context that is provided by a call to BeginPaint.

The VCL wraps all those details up for you, but you still need to follow the rules. In your case I recommend that you add a TPaintBox component to your form. Then implement an OnPaint event handler for the paint box. Finally, whenever you wish to repaint the paint box, for example on a timer, call the Invalidate method of the paint box.

I suspect that you want your each new ellipse to be drawn in addition to the earlier drawn ellipses. In which case you are probably best served by drawing them to an off-screen bitmap first and then, when you come to paint to the paint box, draw that bitmap on the paint box. The point is that a window needs to be able to re-paint itself in its entirety. When you paint to a screen device, what you painted is lost the next time that window needs to be painted. So it's the responsibility of the application to be able to paint its entire self at any point, if it is asked.

More generally I urge you to stop using global variables. They will cause you no end of trouble. Prefer local variables wherever possible. If you need state to persist between different method calls, use member variables. The guiding principle is to use the narrowest scope possible.

Your current design uses a timer to poll for new data. That's a very poor approach. The most efficient and effective approach is to use synchronous blocking communication. Indy takes that approach. Windows sockets components instead tend to be used in an asynchronous mode. Irrespective of the relative merits of these two approaches, you should not be polling on a timer. If you do use asynchronous communication, then respond to new data by handling an event rather than polling.

Your program is currently trying to mix together GDI painting, and network communication. I suggest that you attempt to get on top of these concepts one at a time. Learn how to paint without the distraction of communication. Then when you have cracked painting, try to bring in the communication aspect.

于 2012-10-28T12:15:33.643 に答える