Jump to content

Remy Lebeau

Members
  • Content Count

    3091
  • Joined

  • Last visited

  • Days Won

    139

Everything posted by Remy Lebeau

  1. Try something like this (based on your earlier example): type TOnUserConnect = procedure(AConn: TIdContext) of object; TOnUserDisconnect = procedure(AConn: TIdContext) of object; TOnUserDataIn = procedure(AConn: TIdContext; const AData: String) of object; TOnUserDataOut = procedure(AConn: TIdContext; const AData: String) of object; type // server listener TMyServer = class(TIdCustomTCPServer) protected procedure InitComponent; override; private FOnUserConnect: TOnUserConnect; FOnUserDisconnect: TOnUserDisconnect; FOnUserDataIn: TOnUserDataIn; FOnUserDataOut: TOnUserDataOut; procedure DoConnect(AContext: TIdContext); override; // note: thread safety procedure DoDisconnect(AContext: TIdContext); override; // note: thread safety function DoExecute(AContext: TIdContext): Boolean; override; // note: thread safety procedure DoParseProtocol(AContext: TIdContext; const AData: String); public LWaitFor: String; // command separator property OnUserConnect: TOnUserConnect read FOnUserConnect write FOnUserConnect; property OnUserDisconnect: TOnUserDisconnect read FOnUserDisconnect write FOnUserDisconnect; property OnUserDataIn: TOnUserDataIn read FOnUserDataIn write FOnUserDataIn; property OnUserDataOut: TOnUserDataOut read FOnUserDataOut write FOnUserDataOut; end; procedure TMyServer.InitComponent; begin inherited InitComponent; ContextClass := TMyClient; end; procedure TMyServer.DoConnect(AContext: TIdContext); begin if Assigned(FOnUserConnect) then TThread.Queue(nil, procedure begin if Assigned(FOnUserConnect) then FOnUserConnect(AContext); end ); end; procedure TMyServer.DoDisconnect(AContext: TIdContext); begin if Assigned(FOnUserDisconnect) then TThread.Queue(nil, procedure begin if Assigned(FOnUserDisconnect) then FOnUserDisconnect(AContext); end ); end; function TMyServer.DoExecute(AContext: TIdContext): Boolean; var AData: String; begin (AContext as TMyClient).SendQueue; AData := AContext.Connection.IOHandler.WaitFor(LWaitFor, True, False, nil, 5000); if AData <> '' then begin TThread.Queue(nil, procedure begin if Assigned(FOnUserDataIn) then FOnUserDataIn(AConn, AData); DoParseProtocol(AConn, AData); end ); end; Result := AContext.Connection.Connected; end; procedure TMyServer.DoParseProtocol(AConn: TIdContext; const AData: String); var AUser: TMyClient; begin AUser := AConn as TMyClient; if AData.Equals('Validate') then begin AUser.FNick := '<parsed data>'; AUser.QueueData('Hello'); end else if AData.Equals('<bad command>') then begin AConn.Binding.CloseSocket; end; // this is the actual protocol parser, from here user connection will be // verified, protocol parsed, responses written, other helper classes // will check for bans, shared memory access will be performed, file // contents will be read from hard drive and sent back to users, // thread-unsafe objects will be used, users will get disconnected, // basically all the main load will be performed here end; ... uses ..., IdThreadSafe; type // user connection TMyClient = class(TIdServerContext) protected // FQueue: TIdThreadSafeStringList; ... procedure SendData(const AData: String); procedure SendQueue; public constructor Create(AConn: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override; destructor Destroy; override; procedure QueueData(const AData: String); end; constructor TMyClient.Create(AConn: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); begin inherited Create(AConn, AYarn, AList); ... FQueue := TIdThreadSafeStringList.Create; end; destructor TMyClient.Destroy; begin ... FQueue.Free; inherited Destroy; end; procedure TMyClient.QueueData(const AData: String); begin FQueue.Add(AData); end; procedure TMyClient.SendData(const AData: String); begin Connection.IOHandler.Write(AData); if Assigned((Server as TMyServer).FOnUserDataOut) then TThread.Queue(nil, procedure begin if Assigned((Server as TMyServer).FOnUserDataOut) then (Server as TMyServer).FOnUserDataOut(Self, AData); end ); end; procedure TMyClient.SendQueue; var QueueList: TStringList; SendList: TStringList; I: Integer; begin SendList := nil; try QueueList := FQueue.Lock; try if QueueList.Count = 0 then Exit; SendList := TStringList.Create; SendList.Assign(QueueList); QueueList.Clear; finally FQueue.Unlock; end; for I := 0 to SendList.Count-1 do SendData(SendList[I]); finally SendList.Free; end; end; ... Why? You don't need the thread handle for this task. But, that being said, it is technically possible if you do need it for some reason - you can type-cast the TIdContext.Yarn property to TIdYarnOfThread, and then use the TIdYarnOfThread.Thread.Handle property. handle.
  2. Remy Lebeau

    IDE cant find address in dot proj file

    No. What you are looking at is just a namespace within the XML document. XML namespaces are formatted as URIs, but they are not required to refer to actual live sites. They just need to be unique within the XML document.
  3. You get that for free with TIdTCPServer, as each client runs in its own thread. Sending the answer from the main thread itself is not a good idea. If the send blocks, not only will you block all other requests and answers, but also block the UI, too. This goes back to my earlier suggestion that you should use a per-client queue for outgoing messages. The main thread can put the answer in the client's queue, and then the server's OnExecute event can send the client's queue when it is safe to do so, ie between reads of incoming messages. By default, no. Blocking sockets wait forever for operations to finish. But, if desired, you can use the TIdContext.Binding.SetSockOpt() method to set a timeout for the SO_SNDTIMEO option on the underlying socket. If a send times out, a failure will be reported back to you. At which point, you don't know the state of the socket, or how many bytes it may have sent before timing out, so the only sane thing you can do is close that socket and let that client reconnect to your server. That may or may not be a good design choice. For example, if Client1 and Client2 want to send a message to Client3 at the same time, then you would need to synchronize those messages. But, if Client1 wants to send a message to Client2 while Client3 wants to send a message to Client4 at the same time, then there is no need to synchronize those messages. So, unless the requests are accessing UI resources, there is not a good reason for the main UI thread to be processing the client requests. TIdTCPServer already disconnects active client sockets during its shutdown. But, if you must do it manually, then using Connection.Binding.CloseSocket() would be safer than Connection.Disconnect() especially in a multi-threaded environment. That said, make sure your server's event handlers are exiting properly during server shutdown. This is another reason NOT to use the main UI thread to process requests. If the main thread is blocked shutting down the server then it can't handle subsequent synchronization requests, thus causing a deadlock (client threads are blocked waiting on the main thread, and the main thread is blocked waiting on the client threads). So, do not perform synchronizations with the main thread during server shutdown, or else perform the shutdown in a separate thread leaving the main thread open to handle synchronizations until the server is fully shutdown.
  4. Remy Lebeau

    Understanding TJumpList

    Or, you could just have the main EXE do that same work in its startup code to send the message to an existing instance of itself. This has the added benefit that if there is no existing instance running, the new instance can continue to load itself normally, and then take the requested action when ready.
  5. Indy has some wrappers for setsockopt() and recv(), so your code can be cleaned up a little bit. Try something more like this (based on https://stackoverflow.com/a/78873272/65863): uses ..., IdStack, IdStackBSDBase, IdSSL; type TIdStackBSDBaseAccess = class(TIdStackBSDBase) end; function SslTlsHandshakeDetected(ASocket: TIdStackSocketHandle): Boolean; var Buffer: array[0..5] of Byte; NumBytes, Len: Integer; MsgType: Byte; begin Result := False; NumBytes := GStack.CheckForSocketError( TIdStackBSDBaseAccess(GBSDStack).WSRecv(ASocket, Buffer, SizeOf(Buffer), MSG_PEEK) ); if NumBytes < 3 then Exit; // SSL v3 or TLS v1.x ? if (Buffer[0] = $16) and // type (22 = handshake) (Buffer[1] = $3) then // protocol major version (3.0 = SSL v3, 3.x = TLS v1.x) begin Result := True; Exit; end; // SSL v2 ? if (Buffer[0] and $80) <> 0 then begin // no padding, 2-byte header Len := (Integer(buffer[0] and $7F) shl 8) + buffer[1]; MsgType := buffer[2]; end else begin // padding, 3-byte header Len := (Integer(buffer[0] and $3F) shl 8) + buffer[1]; MsgType := Buffer[3]; end; Result := (Len > 9) and (MsgType = $1); // msg type (1 = client hello) end; procedure SetRecvTimeout(AContext: TIdContext; const ASecs: Integer); {$IFDEF POSIX} type TTimeVal = record tv_sec, tv_usec: LongInt; end; var LTime: TTimeVal; {$ENDIF} begin {$IFDEF POSIX} LTime.tv_sec := ASecs; LTime.tv_usec := 0; GBSDStack.SetSocketOption(AContext.Binding.Handle, Id_SOL_SOCKET, Id_SO_RCVTIMEO, LTime, SizeOf(LTime)); {$ELSE} AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_RCVTIMEO, ASecs * 1000); {$ENDIF} end; ... begin SetRecvTimeout(AContext, 1); TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := not SslTlsHandshakeDetected(AContext.Binding.Handle); SetRecvTimeout(AContext, 0); end;
  6. Remy Lebeau

    win32 exe compiled in Delphi11 does not run on W2000

    One of the new features in Delphi 13 is support for UI Automation in the VCL. The UIA framework was introduced in Windows 7. Embarcadero dropped suppprt for XP awhile ago, and Win2K is even older than XP.
  7. Remy Lebeau

    New Hint in D13

    Well, then the hint is valid since the value -1 will never be used.
  8. Remy Lebeau

    New Hint in D13

    Did they update Abort() to mark it with the new 'noreturn' attribute?
  9. Remy Lebeau

    Ways to change the text size in a Popup menu?

    Have you tried changing the Size property of the VCL's global TScreen.MenuFont?
  10. Remy Lebeau

    RAD Studio 13 is available

    This is explained in the DocWiki documentation: https://docwiki.embarcadero.com/RADStudio/Florence/en/What's_New#Unified_internal_version_numbers_to_37
  11. Remy Lebeau

    New Delphi features in Delphi 13

    Yes. Major version releases can coexist with each other. It's minor version releases that can't.
  12. The Community edition does not support offline installation or offline registration.. You need a Paid edition for that.
  13. Forcing use of client/server components on specific ports does not provide security. Nor are your "secret" ports actually very secret, they are easily discoverable. Also, the server has no way to know if the client is even listening on the "secret" port before replying, so there's no security in favoring one port over another. If you want true security, use a proper security protocol, like DTLS. Also, using an asynchronous server component to receive 1 response is not making the client faster and simpler, it's actually making the client slower and complex (multi-thread handling, breaking up code flow, etc), and it goes against one of Indy's core designs - linear writes and reads in command/response models. But whatever. It's your business design, do what makes sense to you. What do I know? I'm just the guy who maintains the components you are basing your work on. Good luck.
  14. I have some issues with your implementation: IPv6 networks are not supported. The client request is needlessly wordy. The client broadcasts to 255.255.255.255 instead of to the subnet's actual broadcast IP. Some routers block traffic to 255.255.255.255 by default. In case the client is run on a machine with multiple networks installed (VPN, etc), there is no option to let the user decide which network to broadcast to. The client is using separate UDP sockets to send the request and read replies. You are forcing the client to receive on a specific listening port. The server is not sending its reply back to the real port that actually sent the request. Most routers will block this traffic if port forwarding is used. On the server side, always reply to the sending port. On the client side, either 1) let TIdUDPClient read replies on its sending port, or 2) let TIdUDPServer send the request on its listening binding. Use one or the other, not both. Doesn't use Indy's TIdStack.GetLocalAddressList() method to discover local networks (at least on Windows, as it may have issues on Android). Since your goal is to allow a TCP client to discover a server, I would opt to get rid of the TIdUDPServer on the client side, and just use TIdUDPClient by itself. Send a request, read a response, done. You don't need to make that process asynchronous with TIdUDPServer in most use cases. Using TIdUDPServer on the client side makes more sense if you want to discover multiple servers and then let the user choose between them, or to leave it open so servers can just broadcast their presence over time.
  15. Remy Lebeau

    Global in RTL for unit communication?

    Why? You said you are creating IDE design-time packages. So they run in the IDE process, and the IDE is not itself a cross-platform application, it is a Windows-only VCL application. What am I missing? Can you provide more exact details of what you are actually trying to accomplish?
  16. Remy Lebeau

    Global in RTL for unit communication?

    System.Classes.RegisterClass()? It is not clear what you are actually looking for. Please clarify. Can you provide an example of what you want to accomplish? Maybe using custom window messages, or System.Messaging.TMessageManager ? Are they separate packages in the same process, or are they in separate processes? It makes a big difference depending on whether you have to cross the process boundary or not. There is nothing like that framework in the common RTL. And it wouldn't make sense anyway if your two units can't share a common unit that defines what they need to share.
  17. Remy Lebeau

    rease ... at ReturnAddress

    If I'm reading your codes correctly, you are both assuming that ReturnAddress() itself doesn't have a stack frame, and it is returning the address stored in its caller's stack frame. Which means it won't work correctly if its caller has no stack frame, or if it is called from inside of an inline function.
  18. Never heard of CleanTalk. Are you putting your code snippets in code blocks (the `</>` button on the editor toolbar)? I'm guessing you did not read the ICS documentation. It says: https://wiki.overbyte.eu/wiki/index.php/TWSocketServer On a side note: `while PeekMessage() = False do WaitMessage();` is the exact same as calling `GetMessage()` without a loop. Makes sense, if you are creating a TCP server socket expecting it to receive UDP traffic. You need a UDP server socket.
  19. Can you provide the actual code you are having trouble with? Did you set the component's MultiThreaded property to true? Does your thread have a message loop?
  20. "The only way I currently see would be to attach to that program after it has been executed. But that's rather cumbersome."
  21. Remy Lebeau

    Start Menu items missing for RAD Studio 12.3

    The only useful tidbits I get from that page are that: the Start Menu doesn't display two shortcuts pointing to the same target the Start Menu tries to block showing Uninstallers. Good to know. Seems to be on track with what I'm seeing.
  22. I just installed RAD Studio 12.3 on Windows 10. On my Start Menu, underneath "Embarcadero RAD Studio 12", I see the following shortcuts: C++Builder 12 C++Builder 12 (DPI Unaware) Delphi 12 Delphi 12 (DPI Unaware) Migration Tool RAD Studio 12 (64-bit Initial Release) RAD Studio 12 (64-bit Initial Release) (DPI Unaware) RAD Studio 12 RAD Studio 12 (DPI Unaware) RAD Studio Command Prompt RAD Studio Command Prompt (x64) Uninstall Note the two highlighted items are missing. However, in the "%ProgramData%\Microsoft\Windows\Start Menu\Programs\Embarcadero RAD Studio 12" folder, I see the following shortcuts: C++Builder 12 C++Builder 12 (DPI Unaware) Delphi 12 Delphi 12 (DPI Unaware) Migration Tool RAD Studio 12 (64-bit Initial Release) RAD Studio 12 (64-bit Initial Release) (DPI Unaware) RAD Studio 12 RAD Studio 12 (DPI Unaware) RAD Studio Command Prompt RAD Studio Command Prompt (x64) Uninstall So, why are these two items not appearing on my Start Menu? Anyone else ever see this happen?
  23. Remy Lebeau

    Start Menu items missing for RAD Studio 12.3

    It turns out that I had a separate "RAD Studio 12" shortcut in the "%APPDATA%\Microsoft\Windows\Start Menu\Programs" folder from an earlier installation. So the Start Menu was displaying "RAD Studio 12" underneath "R" instead of underneath "Embarcadero RAD Studio 12". When I deleted the rogue shortcut, "RAD Studio 12" now appears under "Embarcadero RAD Studio 12" as expected. But, the "Uninstall" shortcut is still missing. And I have several other programs with "Uninstall" shortcuts that are also missing. Apparently, the Start Menu doesn't like displaying multiple shortcuts with duplicate names, even though they are in different folders. Nice one, Microsoft.
  24. Remy Lebeau

    Bug VCL I'm losing my TControls course ?

    Or TThread.ForceQueue(), eg: procedure TMyForm.Edit1Exit(Sender: TObject); begin TThread.ForceQueue(nil, procedure begin ShowDialog('It works!'); end ); end;
  25. Remy Lebeau

    Rad Studio 13?

    v13 has not been released yet, and no information about its pricing is available at this time.
×