{( )) MEOW.dpr - Mini EO? WOW! (( )) Created by Sordie Solomon-Smith out of bordem (( http://sordie.com/ )} program MEOW; {$APPTYPE CONSOLE} uses Windows, WinSock; type procedureref = reference to procedure; CriticalSectionHelper = record helper for TRTLCriticalSection procedure Create; inline; procedure Free; inline; procedure Enter; inline; procedure Leave; inline; procedure Section(Code: procedureref); inline; end;{CriticalSectionHelper} Server = class abstract class var CriticalSection: TRTLCriticalSection; type TSession = class var Socket: TSocket; var IPStr: AnsiString; var IPInt: Integer; var Thread: THandle; var ID: Cardinal; constructor Create(ASocket: TSocket); destructor Destroy; override; function Execute: Boolean; end;{Session} type TArray<T: class> = class var Items: array of T; constructor Create; destructor Destroy; override; function Find (Item: T): Integer; function Add (Item: T): Integer; function Remove(Item: T): Integer; procedure Clear; end;{TArray<T>} class var Sessions: TArray<TSession>; class var Socket: TSocket; class constructor Create; class destructor Destroy; class procedure Reference; class procedure Main; class function GetSessionByID(ID: Cardinal): TSession; class procedure Log (Params: array of const); class procedure Error(Params: array of const); end;{Server} procedure CriticalSectionHelper.Create; begin InitializeCriticalSection(Self); end;{CriticalSectionHelper.Create} procedure CriticalSectionHelper.Free; begin DeleteCriticalSection(Self); end;{CriticalSectionHelper.Free} procedure CriticalSectionHelper.Enter; begin EnterCriticalSection(Self); end;{CriticalSectionHelper.Enter} procedure CriticalSectionHelper.Leave; begin LeaveCriticalSection(Self); end;{CriticalSectionHelper.Leave} procedure CriticalSectionHelper.Section(Code: procedureref); begin Enter; try Code; finally Leave; end;{try...finally} end;{CriticalSectionHelper.Secion} class constructor Server.Create; var WSAData: TWSAData; AddrIn: TSockAddrIn; begin CriticalSection.Create; Log(['Server.Create']); Sessions := TArray<TSession>.Create; WSAStartup(MakeLong(2, 2), WSAData); Socket := WinSock.socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if Socket = 0 then Error(['Failed to create socket']); FillChar(AddrIn, sizeof(AddrIn), 0); with AddrIn do begin sin_family := AF_INET; sin_addr.S_addr := inet_addr('0.0.0.0'); sin_port := htons(8078); end;{with AddrIn} if bind(Socket, AddrIn, sizeof(AddrIn)) <> 0 then Error(['Failed to bind socket']); if listen(Socket, 0) <> 0 then Error(['Cannot listen on socket']); try Main; except Log(['Server Exception']); end;{try...except} end;{class)Server.Create} class destructor Server.Destroy; begin if Socket <> 0 then begin closesocket(Socket); Socket := 0; end;{if Socket <> 0} Sessions.Free; Log(['Server.Destroy']); CriticalSection.Free; Readln; end;{class)Server.Destroy} class procedure Server.Reference; begin ClassName end;{class)Server.Reference} class procedure Server.Main; var FDSet: TFDSet; SockSize: Integer; SockAddr: TSockAddr; begin repeat Sleep(1); FDSet.fd_count := 1; FDSet.fd_array[0] := Socket; if select(0, @FDSet, nil, nil, nil) = 1 then begin SockSize := sizeof(SockAddr); TSession.Create(accept(Socket, @SockAddr, @SockSize)); end;{if select} until Socket = 0; end;{class)Server.Main} class function Server.GetSessionByID(ID: Cardinal): TSession; var Session: TSession; begin CriticalSection.Enter; try for Session in Sessions.Items do if Session.ID = ID then exit(Session); Result := nil; finally CriticalSection.Leave; end;{try...finally} end;{class)Server.GetSessionByID} class procedure Server.Log(Params: array of const); var i: Integer; begin CriticalSection.Enter; try for i := 0 to high(Params) do with TVarRec(Params[i]) do case VType of vtInteger: Write(VInteger); vtBoolean: Write(VBoolean); vtChar: Write(VChar); vtWideChar: Write(VWideChar); vtExtended: Write(VExtended^); vtString: Write(AnsiString(VString)); vtPointer: Write(Cardinal(VPointer)); vtPChar: Write(AnsiString(VPChar)); vtObject: Write(VObject.ClassName); vtClass: Write(VClass.ClassName); vtPWideChar: Write(WideString(VPWideChar)); vtWideString: Write(WideString(VWideString)); vtInt64: Write(VInt64^); vtUnicodeString: Write(String(VUnicodeString)); else Write('?(', VType, ')'); end;{case VType} finally Writeln; CriticalSection.Leave; end;{try...finally} end;{class)Server.Log} class procedure Server.Error(Params: array of const); begin Log(Params); halt(1); end;{class)Server.Error} constructor Server.TArray<T>.Create; begin inherited Create; Clear; end;{Server.TArray<T>.Create} destructor Server.TArray<T>.Destroy; begin Clear; inherited; end;{Server.TArray<T>.Destroy} function Server.TArray<T>.Find(Item: T): Integer; var i: Integer; begin for i := 0 to high(Items) do if Items[i] = Item then exit(i); Result := -1; end;{Server.TArray<T>.Add} function Server.TArray<T>.Add(Item: T): Integer; begin Result := Find(Item); if Result = -1 then begin SetLength(Items, length(Items) + 1); Result := high(Items); Items[Result] := Item; end;{if Result = -1} end;{Server.TArray<T>.Add} function Server.TArray<T>.Remove(Item: T): Integer; begin Result := Find(Item); if Result = -1 then exit; if Result < high(Items) then move(Items[Result + 1], Items[Result], sizeof(T) * (length(Items) - 1)); SetLength(Items, length(Items) - 1); end;{Server.Tarray<T>.Remove} procedure Server.TArray<T>.Clear; begin SetLength(Items, 0); end;{Server.TArray<T>.Clear} function SessionThread(Session: Server.TSession): Integer; begin Result := 0; try try while Session.Execute do Sleep(1); except Server.Log(['Session Exception']); end;{try...except} finally try Session.Free; except {} end;{try...except} EndThread(Result); end;{try...finally} end;{SessionThread} constructor Server.TSession.Create(ASocket: TSocket); var i: Integer; Addr: TSockAddr; begin inherited Create; Socket := ASocket; if Socket <> 0 then begin FillChar(Addr, sizeof(Addr), 0); i := sizeof(Addr); getpeername(Socket, Addr, i); IPStr := AnsiString(inet_ntoa(Addr.sin_addr)); IPInt := Addr.sin_addr.S_addr; i := 1; ioctlsocket(Socket, FIONBIO, i); end;{if Socket} Server.CriticalSection.Section(procedure begin ID := 100; while Server.GetSessionByID(ID) <> nil do inc(ID); Server.Sessions.Add(Self); end);{Server.CriticalSection.Section} BeginThread(nil, 0, @SessionThread, Pointer(Self), 0, Thread); Server.Log(['Session[', ID, '] ', IPStr, ' created']); end;{Server.TSession.Create} destructor Server.TSession.Destroy; begin if Socket <> 0 then begin closesocket(Socket); Socket := 0; end;{if Socket <> 0} Server.CriticalSection.Section(procedure begin Server.Sessions.Remove(Self); end);{Server.CriticalSection.Section} Server.Log(['Session[', ID, '] ', IPStr, ' destroyed']); inherited; end;{Server.TSession.Destroy} function Server.TSession.Execute: Boolean; begin if (Socket = 0) or (recv(Socket, nil^, 0, MSG_OOB) = 0) then exit(False); Result := True; end;{Server.TSession.Execute} begin Server.Reference; end.