Hab mich jetzt weiter mit diesem Thema beschäftigt und bin auf WSAAsyncSelect() (WinSock-Funktion) gestoßen.
Die
MSDN sagt folgendes zu dieser Funktion:
Zitat:
The WSAAsyncSelect function requests Windows message-based notification of network events for a socket.
int WSAAsyncSelect(
SOCKET s,
HWND hWnd,
unsigned int wMsg,
long lEvent
);
Also habe ich das wie folgt implementiert:
Hauptprogramm:
Delphi-Quellcode:
program Project1;
uses
Windows,
WinSock,
messages,
uServer in 'uServer.pas';
var
S:TServer;
function WndProc(hWnd: HWND; Msg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
var
begin
Result := 0;
case Msg OF
WM_SOCKET: //Konstante in uServer
begin
if Assigned(S) then
if LParam=FD_CLOSE then S.Disconnect;
{...}
end;
else
Result := DefWindowProc(hWnd, Msg, wParam, lParam);
end;
end;
var
msg:TMsg;
begin
S:=TServer.Create(1223);
with S do
begin
Listen;
if not Listening then
begin
Free;
S:=nil;
end;
end;
while true do
begin
if not GetMessage(Msg,0,0,0) then Break;
DispatchMessage(Msg);
end;
end.
Zunächst einmal gibt es hier das Problem, dass WndProc von DispatchMessage nicht aufgerufen wird, da WndProc keinem
Handle zugewiesen ist. Ich will das ohne
Handle machen...
So sieht nun uServer.pas aus:
Delphi-Quellcode:
unit uServer;
interface
uses Windows,Winsock,Messages;
const
WM_SOCKET = WM_USER;
FD_SERVER= FD_READ+FD_CONNECT+FD_CLOSE+FD_ACCEPT;
type
TServer=class(TObject)
constructor Create(xPort:Word);
destructor Destroy;
override;
procedure Listen;
procedure AcceptConnection;
procedure Disconnect;
// procedure ExecuteMessage(var Msg:TMessage); message WM_SOCKET;
private
FSock:TSocket;
FClientSock:TSocket;
FPort:Word;
FConnected:Boolean;
FListening:Boolean;
public
property Sock:TSocket
Read FSock;
property ClientSock:TSocket
Read FClientSock;
property Port:Word
Read FPort;
property Connected:Boolean
Read FConnected;
property Listening:Boolean
Read FListening;
end;
implementation
constructor TServer.Create(xPort:Word);
begin
inherited Create;
FPort:=xPort;
FConnected:=False;
FListening:=False;
end;
destructor TServer.Destroy;
begin
if Connected
then Disconnect;
WSACleanUP;
inherited Destroy;
end;
procedure TServer.AcceptConnection;
begin
if Connected
then Exit;
FClientSock:=accept(Sock,
nil,
nil);
FConnected:=true;
end;
procedure TServer.Disconnect;
begin
shutdown(Sock,SD_SEND);
end;
procedure TServer.Listen;
var wsaData: TWSADATA;
SockAddr: sockaddr_in;
begin
if (WSAStartup(MAKEWORD(2,0),WSAData)) <> 0
then Exit;
FSock:=Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if FSock = INVALID_SOCKET
then Exit;
ZeroMemory(@SockAddr, sizeof(SockAddr));
SockAddr.sin_addr.S_addr := INADDR_ANY;
SockAddr.sin_family := AF_INET;
SockAddr.sin_zero := #0#0#0#0#0#0#0;
SockAddr.sin_port := htons(Port);
if (bind(Sock,SockAddr,SizeOf(SockAddr)))=SOCKET_ERROR
then Exit;
if (WinSock.listen(Sock, 1)) = SOCKET_ERROR
then Exit;
WSAAsyncSelect(Sock,0,WM_SOCKET,FD_SERVER);
FListening:=True;
end;
{procedure TServer.ExecuteMessage(var Msg:TMessage);
begin
end;}
Wie ihr vielleicht erkannt habt, habe ich ExecuteMessage hier ausgeklammert, da ich es zunächst über WndProc bewerkstelligen will.
Also TServer.Listen ruft am Ende die WSAASyncSelect() Funktion auf. Da diese Funktion ein
Handle benötigt und ich kein
Handle erzeugt habe, übergebe ich ihr die 0. WM_SOCKET ist die Message, die ankommen soll, wenn SOCK FD_SERVER, also entweder FD_READ, FD_CONNECT, FD_CLOSE oder FD_ACCEPT, zurückliefert.
Welches FD_XXX es endgültig ist, erfährt man entweder über lParam oder wParam der Message.
Nun zum Problem:
Es kommt keine Message WM_SOCKET an. Ich vermute es liegt am
Handle=0, bin mir aber nicht sicher...
Wie krieg ich es hin, dass WndProc ohne
Handle aufgerufen wird von DispatchMessage(Msg)? (eigntl. eine Win-
Api Frage)
Habt ihr irgendwelche Ratschläge?