Einzelnen Beitrag anzeigen

Astat

Registriert seit: 2. Dez 2009
Ort: München
320 Beiträge
 
Lazarus
 
#4

Re: Anfängerfrage: Non Blocking TCP Client in einem Thread

  Alt 4. Dez 2009, 18:17
Hallo alzaimar.

Du verwendest einen Asynchronen Socket, die Kommunikation wird mit dem Socket durch den Parameter (ctNonBlocking) auf WindowsMessages festgelegt.

WSAAsyncSelect(Socket, Wnd, Msg, AsyncStyle); Das heisst, es werden die Socket Events in die Application Message Queue (MSQ), zur Abarbeitung, durch den Mainthread gestellt.
Die Verwendung der TClientSocket Komponente mit dem Parameter (ctNonBlocking) in einem eigenen Thread macht keinen Sinn,
da der Motor, der den Socket am Leben hällt, die MSQ des Mainthreads ist.
Wenn du nicht die Mainthread Message Oueue verwenden willst, musst Du die Komponente umschreiben.
Ersetze das AllocateHwnd der Komponente durch folgenden Code.

Delphi-Quellcode:

function TCustomWinSocket.GetHandle: HWnd;
begin
  if FHandle = 0 then
    FHandle := AllocateHwnd(WndProc);
  Result := FHandle;
end;
Delphi-Quellcode:
function TCustomWinSocket.GetHandle: HWnd;
begin

  while _hWndMain = 0 do;

  if FHandle = 0 then
    FHandle := _hWndMain; //_hWndMain; //AllocateHwnd(WndProc);

  Result := FHandle;
end;
Delphi-Quellcode:
function MainWndProc(wnd: HWND; Msg: Integer; wp: WPARAM;
  lp: LPARAM): Integer; stdcall; forward;

var
  _Terminated : Boolean = false;
  _hWndMain : HWND = 0;
  _hThread : THandle = 0;
  _dwThreadID : dword = 0;

  TASyncMainWindowClass : TWndClass = (style: 0; lpfnWndProc: @MainWndProc;
    cbClsExtra: 0; cbWndExtra: 0; hInstance: 0; hIcon: 0; hCursor: 0;
    hbrBackground: 0; lpszMenuName: nil; lpszClassName: 'TAsyncServerMainWindowClass'
    );

function MainWndProc(wnd: HWND; Msg: Integer; wp: WPARAM; lp: LPARAM): Integer; stdcall;
var
  tMsg: TMessage;
begin
  Result := 0;
  if Msg = WM_CLOSE then DestroyWindow(wnd) else
  if _CustomWinSocket <> nil then begin
    tMsg.Msg := Msg;
    tMsg.WParam := wp;
    tMsg.LParam := lp;
    tMsg.WParamLo := LOWORD(wp);
    tMsg.WParamHi := HIWORD(wp);
    tMsg.LParamLo := LOWORD(lp);;
    tMsg.LParamHi := HIWORD(lp);;
    if not _CustomWinSocket.WndProc(tMsg) then
      Result := DefWindowProc(wnd, Msg, wp, lp)
  end else
    Result := DefWindowProc(wnd, Msg, wp, lp);
end;

procedure InitAplication;
begin
  TASyncMainWindowClass.hInstance := hInstance;

  TASyncMainWindowClass.lpszClassName := PChar(CreateClassID);

  if Windows.RegisterClass(TASyncMainWindowClass) = 0 then
    raiselastwin32error;

  _hWndMain := CreateWindowEx(WS_EX_TOOLWINDOW   , TASyncMainWindowClass.lpszClassName,
    '', WS_POPUP   , 0, 0, 0, 0, 0, 0, hInstance, nil);

  if _hWndMain = 0 then raiselastwin32error;
end;

procedure CleanupAplication;
begin
  WSACleanup;
  CloseHandle(_hThread);
  if _hWndMain <> 0 then begin
    DestroyWindow(_hWndMain);
    _hWndMain := 0;
  end;
end;

function RunAplication(p: pointer): integer;
var
  MsgRec : TMsg;
begin
  result := 0;

  SetThreadPriority(_hThread, THREAD_PRIORITY_TIME_CRITICAL);

  WSAStartup($0101, WSAData);

  InitAplication;

  while GetMessage(MsgRec, _hWndMain, 0, 0) do begin
    DispatchMessage(MsgRec)
  end;

  CleanupAplication;
  _Terminated := TRUE;
end;

....

initialization
  _hThread := beginthread(nil, 0, RunAplication, nil, 0, _dwThreadID);
end.
Also, kurz zusammengefasst:

Am einfachsten ist es, wenn du den Socket und die durch die Socket Messages getriggerten Callbacks,
im Mainthread (MainUnit) erstellst. Du verwaltest deine zusätzlich notwendigen Workerthreads von
diesem Punkt aus. Von hier aus (MainUnit) kannst du deine Threads Resumen, Starten Stoppen, und oder
mit Synchgronisationsobjekten (Events, Mutexe, Semaphoren, Critical Sections usw.) arbeiten.

Mach die SendSocket methode Threadsave, dann kann jeder Workerthread zu jeder Zeit gefahlos senden.
Erstelle deine Workerthreads, implementiere wenn notwendig Datenübergabe Prozeduren usw.

Tip:
Um eine gute Skalierung zu erreichen, den Socket alleine im Mainthread werkeln lassen.
Zusätzliche Codeteile in eigene Threads auslagern.


lg. Astat

06810110811210410503210511511603209711003210010110 9032084097103
03211611111604403209711003210010110903210010510103 2108101116122
11610103209010110510810103206711110010103210511003 2068101108112
10410503210310111509910411410510109810111003211910 5114100046
Lanthan Astat
  Mit Zitat antworten Zitat