In Socket applications programmed by TCPServer/Client components, usually we active server side, then connect client to server, and when we need to get or send data from one side to other, first we send a command from client to server and a communication will starts.
But the problem is that always we need to start conversation from client side!
I want to ask is any idea for start conversation randomly from server side without client side request?
I need this functionality for notify client(s) from server side. for example, when a registered user (client-side) connected to server, other connected users (on other client-sides), a notification must send from server to all users (like Yahoo Messenger).
I'm using TIdCmdTCPServer and TIdTCPClient components
You are using TIdCmdTCPServer. By definition, it sends responses to client-issued commands. For what you are asking, you should use TIdTCPServer instead, then you can do whatever you want in the TIdTCPServer.OnExecute event.
What you ask for is doable, but its implementation depends on your particular needs for your protocol.
If you just want to send unsolicited server-to-client messages, and never responses to client-to-server commands, then the implementation is fairly straight forward. Use TIdContext.Connection.IOHandler when needed. You can loop through existing clients in the TIdTCPServer.Contexts list, such as inside the TIdTCPServer.OnConnect and TIdTCPServer.OnDisconnect events. On the client side, you need a timer or thread to check for server messages periodically. Look at TIdCmdTCPClient and TIdTelnet for examples of that.
But if you need to mix both client-to-server commands and unsolicited server-to-client messages on the same connection, you have to design your protocol to work asynchronously, which makes the implementation more complex. Unsolicited server messages can appear at anytime, even before the response to a client command. Commands need to include a value that is echoed in the response so clients can match up responses, and the packets need to be able to differentiate between a response and an unsolicited message. You also have to give each client its own outbound queue on the server side. You can use the TIdContext.Data property for that. You can then add server messages to the queue when needed, and have the OnExecute event send the queue periodically when it is not doing anything else. You still need a timer/thread on the client side, and it needs to handle both responses to client commands and unsolicited server messages, so you can't use TIdConnection.SendCmd() or related methods, as it won't know what it will end up reading.
I have posted examples of both approaches in the Embarcadero and Indy forums many times before.
Clients initiate communication. That is the definition of a client–the actor that initiates the communication. Once the connection is established though, both sides can send data. So, the clients connect to the server. The server maintains a list of all connected clients. When the server wants to send out communications it just sends the data to all connected clients.
Since clients initiate communication, it follows that, in the event of broken communication, it is the client's job to re-establish connection.
If you want to see working code examples where server sends data, check out Indy IdTelnet: the telnet client uses a thread to listen to server messages. There is only one socket, created by the client, but the server uses the same socket for its messages to the client, at any time.
The client starts the connection, but does not have to start a conversation by saying 'HELLO' or something like that.
Technically, the client only needs to open the socket connection, without sending any additional data. The client can remain quiet as long as he wants, even until the end of the connection.
The server has a socket connection to the client as soon as the client has connected. And over this socket, the server can send data to the client.
Of course, the client has to read from the connection socket to see the server data. This can be done in a loop in a background thread, or even in the main thread (not in a VCL application of course as it would block).
Finally, this is the code that I used to solve my problem:
// Thread at client-side
procedure FNotifRecieverThread.Execute;
var
str: string;
MID: Integer;
TCP1: TIdTCPClient;
begin
if frmRecieverMain.IdTCPClient1.Connected then
begin
TCP1 := TIdTCPClient.Create(nil);
TCP1.Host := frmRecieverMain.IdTCPClient1.Host;
TCP1.Port := frmRecieverMain.IdTCPClient1.Port;
TCP1.ConnectTimeout := 20000;
while True do
begin
try
TCP1.Connect;
while True do
begin
try
str := '';
TCP1.SendCmd('checkmynotif');
TCP1.Socket.WriteLn(IntToStr(frmRecieverMain.UserID));
str := TCP1.Socket.ReadLn;
if Pos('showmessage_', str) = 1 then
begin
MID := StrToInt(Copy(str, Pos('_', str) + 1, 5));
frmRecieverMain.NotifyMessage(MID);
end
else
if str = 'updateusers' then
begin
LoadUsers;
frmRecieverMain.sgMsgInbox.Invalidate;
frmRecieverMain.sgMsgSent.Invalidate;
frmRecieverMain.cbReceipent.Invalidate;
end
else
if str = 'updatemessages' then
begin
LoadMessages;
frmRecieverMain.DisplayMessages;
end;
except
// be quite and try next time :D
end;
Sleep(2000);
end;
finally
TCP1.Disconnect;
TCP1.Free;
end;
Sleep(5000);
end;
end;
end;
// And command handlers at server-side
procedure TfrmServer.cmhCheckMyNotifCommand(ASender: TIdCommand);
var
UserID, i: Integer;
str: string;
begin
str := 'notifnotfound';
UserID := StrToIntDef(ASender.Context.Connection.Socket.ReadLn, -1);
for i := 0 to NotificationStack.Count - 1 do
if NotificationStack.Notifs[i].Active and
(NotificationStack.Notifs[i].UserID = UserID)
then
begin
NotificationStack.Notifs[i].Active := False;
str := NotificationStack.Notifs[i].NotiffText;
Break;
end;
ASender.Context.Connection.Socket.WriteLn(str);
end;
// And when i want to some client notificated from server, I use some methodes like this:
procedure TfrmServer.cmhSetUserOnlineCommand(ASender: TIdCommand);
var
UserID, i: Integer;
begin
UserID := StrToIntDef(ASender.Context.Connection.Socket.ReadLn, -1);
if UserID <> -1 then
begin
for i := 0 to OnLineUsersCount - 1 do // search for duplication...
if OnLineUsers[i].Active and (OnLineUsers[i].UserID = UserID) then
Exit; // duplication rejected!
Inc(OnLineUsersCount);
SetLength(OnLineUsers, OnLineUsersCount);
OnLineUsers[OnLineUsersCount - 1].UserID := UserID;
OnLineUsers[OnLineUsersCount - 1].Context := ASender.Context;
OnLineUsers[OnLineUsersCount - 1].Active := True;
for i := 0 to OnLineUsersCount - 1 do // notify all other users for refresh users list
if OnLineUsers[i].Active and (OnLineUsers[i].UserID <> UserID) then
begin
Inc(NotificationStack.Count);
SetLength(NotificationStack.Notifs, NotificationStack.Count);
NotificationStack.Notifs[NotificationStack.Count - 1].UserID := OnLineUsers[i].UserID;
NotificationStack.Notifs[NotificationStack.Count - 1].NotiffText := 'updateusers';
NotificationStack.Notifs[NotificationStack.Count - 1].Active := True;
end;
end;
end;
Related
I'm trying to use Indy's IdFTP to send and receive some files via FTP.
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
var
TimeoutFTP: integer;
begin
Result := false;
with TIdFTP.Create(Fm) do
try
try
TimeoutFTP := 2000;
Host := Servidor;
Port := 21;
UserName := Usuario;
PassWord := Contra;
Passive := Pasivo;
Connect(true, TimeoutFTP);
if not Connected then
begin
Error := true;
end
else
begin
TransferType := ftASCII;
if Binario then
TransferType := ftBinary;
OnWorkEnd := FinDeTransmision;
if Descargar then
Get(Remoto , Local, True)
else
Put(InterpretarRutaEspecial(Local), Remoto, True);
if Descargar and Borrar then
Delete(Remoto);
Disconnect;
Result := true;
Fm.Hide;
end;
Except on E: Exception do
Mensaje := E.Message;
end;
finally
Free;
end;
if not Result then
ErrorTransmision;
end;
Whenever I try to do a PUT/GET on Active mode I get the following error: EIdProtocolReplyError: 'Failed to establish connection". It works fine on Passive mode.
The thing is that I want to use Indy (used elsewhere in the project) but the previous version of the code, using OverbyteIcsFtpCli works fine both in Active and Passive mode.
This is the code using OverbyteIcsFtpCli:
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
begin
with TFtpClient.Create(Fm) do
try
HostName := Servidor;
Port := '21';
UserName := Usuario;
PassWord := Contra;
HostDirName := '';
HostFileName := Origen;
LocalFileName := InterpretarRutaEspecial(Destino);
Binary := Binario;
Passive := Pasivo;
OnRequestDone := FinDeTransmision;
if Descargar then
Result := Receive
else
Result := Transmit;
OnRequestDone := nil;
if Descargar and Borrar then
Delete;
Result := Result and not Error;
Fm.Hide;
if not Result then
ErrorTransmision;
finally
Free;
end;
end;
So I took a look under the hood using wireshark and I found that Indy's FTP is not answering some messages from the server.
This is the file-transmission handshake with OverBytes' FTP:
I've highlighted in yellow the two packets sent between server and client that start the data transmission.
Now let's see what happens with Indy's FTP:
The server is sending the packet to start the file transmission but IdFTP is not answering.
I've seen this question but this two tests where ran in the same computer, same network connection, same firewall, etc. Also this one, but I want the FTP to work both in active and passive modes.
What's happening?
In an Active mode transfer, an FTP server creates an outgoing TCP connection to the receiver.
Your Wireshark captures clearly show that the FTP server in question is creating that transfer connection BEFORE sending a response to the RETR command to let your client know that the connection is proceeding. TFtpClient is accepting that connection before receiving the RETR response. But TIdFTP waits for the RETR response before it will then accept the transfer connection (this also applies to TIdFTP's handling of STOR/STOU/APPE commands, too).
LPortSv.BeginListen; // <-- opens a listening port for transfer
...
SendPort(LPortSv.Binding); // <-- sends the PORT command
...
SendCmd(ACommand, [125, 150, 154]); // <-- sends the RETR command and waits for a response!
...
LPortSv.Listen(ListenTimeout); // <-- accepts the transfer connection
...
Re-reading RFC 959, it says the following:
The passive data transfer process (this may be a user-DTP or a second server-DTP) shall "listen" on the data port prior to sending a transfer request command. The FTP request command determines the direction of the data transfer. The server, upon receiving the transfer request, will initiate the data connection to the port. When the connection is established, the data transfer begins between DTP's, and the server-PI sends a confirming reply to the user-PI.
ICS is asynchronous, so this situation is not a big deal for it to handle. But Indy uses blocking sockets, so TIdFTP will need to be updated to account for this situation, likely by monitoring both command and transfer ports simultaneously so it can act accordingly regardless of the order in which the transfer connection and the command response arrive in.
I have opened a ticket in Indy's issue tracker for this:
#300: TIdFTP fails on Active mode transfer connection with vsFTPd
UPDATE: the fix has been merged into the main code now.
I deploy my application in environments heavily stricken with firewalls. Frequently I find myself using Telnet to check if a port is open and accessible in the network.
Now I would like to implement an equivalent functionality of the command, Telnet [domainname or ip] [port], in Delphi.
Is it adequate that I just attempt to open and close a TCP/IP socket without sending or receiving any data?
Is there any risk that I might crash the arbitrary application/service listening on the other end?
Here's my code:
function IsPortActive(AHost : string; APort : Word):boolean;
var IdTCPClient : TIdTCPClient;
begin
IdTCPClient := TIdTCPClient.Create(nil);
try
try
IdTCPClient.Host := AHost;
IdTCPClient.Port := APort;
IdTCPClient.Connect;
except
//Igonre exceptions
end;
finally
result := IdTCPClient.Connected;
IdTCPClient.Disconnect;
FreeAndNil(IdTCPClient);
end;
end;
If you just want to check whether the port is open, then you can use this:
function IsPortActive(AHost : string; APort : Word): boolean;
var
IdTCPClient : TIdTCPClient;
begin
Result := False;
try
IdTCPClient := TIdTCPClient.Create(nil);
try
IdTCPClient.Host := AHost;
IdTCPClient.Port := APort;
IdTCPClient.Connect;
Result := True;
finally
IdTCPClient.Free;
end;
except
//Ignore exceptions
end;
end;
But that only tells you if any server app has opened the port. If you want to make sure that YOUR server app opened the port, then you will have to actually communicate with the server and make sure its responses are what you are expecting. For this reason, many common server protocols provide an initial greeting so clients can identify the type of server they are connected to. You might consider adding a similar greeting to your server, if you are at liberty to make changes to your communication protocol.
Simply opening a connection to the server does not impose any risk of crashing the server, all it does is momentarily occupy a slot in the server's client list. However, if you actually send data to the server, and the server app you are connected to is not your app, then you do run a small risk if the server cannot handle arbitrary data that does not conform it its expected protocol. But that is pretty rare. Sending a small command is not uncommon and usually pretty safe, you will either get back a reply (which may be in a format that does not conform to your protocol, so just assume failure), or you may not get any reply at all (like if the server is waiting for more data, or simply is not designed to return a reply) in which case you can simply time out the reading and assume failure.
I have three questions:
is it possible to destroy IdTCPServer by to many connection?
I tried to test my application and when I have several connections - it works very good (even several days) but when sometimes number of connection increases application gives acess violation. I wrote application similates 50 clients sending data constantly (with only sleep(200)). And in this situation IdTCPServer gives exceptions?
My application reseives information from clients by onExecute event and modyfies databases table using
TidNotify and TIdSync classes. I believe it protects crosses connections threads?
Sending information to clients is doing by TTimer (it is only now, I'll change it to other thread).
Have I use in this situation special protection or something like that is enough:
type
PClient = ^TClient;
TClient = record
Activity_time:TDateTime;
AContext: TIdContext;
end;
...
list := server.Contexts.LockList;
try
for i := 0 to list.Count - 1 do
with TIdContext(list[i]) do
begin
if SecondsBetween(now(), PClient(data)^.activity_time) > 6 then
begin
Connection.IOHandler.Close;
Continue;
end;
try
Connection.IOHandler.writeln('E:');
Except
Connection.IOHandler.Close;
end;
end;
finally
server.Contexts.UnlockList;
end;
2.Is a simple way to refuse connection when server is to busy (I think my database isn't complicated (100 rows, only one row is modyfied by one connection) but maybe here is a way to keep stability of server?
3.I know that this question was repeating many times but I didn't find satisfying answer: how to protect application to avoid message exception: "Connection closed gracefully" and "Connection reset by peer"?
Thank You for all advices
is it possible to destroy IdTCPServer by to many connection?
You are asking the wrong question, because you are not actually destroying TIdTCPServer itself, you are simply closing idle connections from an outside thread. That kind of logic can be (and should be) handled inside of the OnExecute event instead, where it is safest to access the connection, eg:
type
PClient = ^TClient;
TClient = record
Activity_time: TDateTime;
Heartbeat_time: TDateTime;
AContext: TIdContext;
end;
procedure TForm1.serverConnect(AContext: TIdContext);
var
Client: PClient;
begin
New(Client);
Client^.Activity_time := Now();
Client^.Heartbeat_time := Client^.Activity_time;
AContext.Data := TObject(Client);
end;
procedure TForm1.serverDisconnect(AContext: TIdContext);
var
Client: PClient;
begin
Client := PClient(AContext.Data);
AContext.Data := nil;
if Client <> nil then Dispose(Client);
end;
procedure TForm1.serverExecute(AContext: TIdContext);
var
Client: PClient;
dtNow: TDateTime;
begin
Client := PClient(AContext.Data);
dtNow := Now();
if SecondsBetween(dtNow, Client^.Activity_time) > 6 then
begin
AContext.Connection.Disconnect;
Exit;
end;
if SecondsBetween(dtNow, Client^.Heartbeat_time) > 2 then
begin
AContext.Connection.IOHandler.WriteLn('E:');
Client^.Heartbeat_time := dtNow;
end;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
if not AContext.Connection.IOHandler.CheckForDataOnSource(100) then
Exit;
end;
// process incoming data as needed ...
Client^.Activity_time := Now();
end;
Is a simple way to refuse connection when server is to busy (I think my database isn't complicated (100 rows, only one row is modyfied by one connection) but maybe here is a way to keep stability of server?
The current architecture does not allow for refusing connections from being accepted. You can let the server accept connections normally and then close accepted connections when needed. You can do that in the OnConnect event, or you can set the server's MaxConnection property to a low non-zero number to allow the server to auto-disconnect new connections for you without wasting resources creating new TIdContext objects and threads for them.
Another option is to call the server's StopListening() method when the server is busy, so that new connections cannot reach the server anymore, and then call the server's StartListening() method when you are ready to accept new clients again. Existing clients who are already connected should not be affected, though I have not actually tried that myself yet.
I know that this question was repeating many times but I didn't find satisfying answer: how to protect application to avoid message exception: "Connection closed gracefully" and "Connection reset by peer"?
You should not avoid them. Let them happen, they are normal errors. If they are happening inside of the server's events, just let the server handle them normally for you. That is how TIdTCServer is designed to be used. If they are happening outside of the server's events, such as in your timer, then just wrap the socket operation(s) in a try/except block and move on.
I'm trying to use the TUdpSocket in Delphi. What I want to do is connect to a UDP server, send some data and wait for answer. Data is sent correctly, but the control does not receive anything. I don't know why. I've been struggling with this problem for many hours now and I'm going to give up :-(.
I tried to use TIdUDPClient, but the situation is the same. Data is sent properly, but none is received.
Only does TIdUDPServer work more or less properly for it both sends and receives data. Unfortunately data reception is handled by a separate thread (main or other, depending on the ThreadedEvent property) which forces me to use synchronization and complicate the whole code. I would like to handle UDP connection in my own thread. Just send some data and call WaitForData() to wait for an answer and then handle it in the same thread.
And if opossible, I don't want to use any third party controls, but if it is the only solution, I accept it.
Thank you very, very much for your help in advance.
---- Examples ---
i) TUDPSocket:
var
lR, lW, lE: boolean;
begin
UdpSocket1.LocalPort := '1600';
UdpSocket1.RemotePort := '1600';
UdpSocket1.RemoteHost := '127.0.0.1';
UdpSocket1.Connect;
UdpSocket1.Sendln('test');
UdpSocket1.Select(#lR, #lW, #lE, 2000);
if lR then
ShowMessage(UdpSocket1.Receiveln());
end;
As you can see, the control should receive the data it transmits. And apparently it does, for the lR evaluates to true after the Select() method is called. But Receiveln() returns an empty string, as ReceiveBuf() does. When i start a UDP server and send some data to it, it is received properly, so I'm certain that the data is really sent.
You should need nothing more than this:
function SayHi(Host: String; Port: Integer): String;
var
Client: TIdUDPClient;
begin
Client := TIdUDPClient.Create(nil);
try
Client.Host := Host;
Client.Port := Port;
Client.Send('Hello');
Result := Client.ReceiveString(1000);
finally
Client.Free;
end;
end;
which will, in the same thread, send a UDP packet and either receive a UDP packet or raise an exception after a timeout.
If the above doesn't work, check (using something like Wireshark that the client's actually sending the data. Then check on the server that it's actually receiving the packet.
Send() ultimately (on Windows) calls WinSock's sendto(), and ReceiveString() uses select() and recvfrom(). You could sort've simulate a TIdUDPServer by
while not Terminated do begin
S := Client.ReceiveString(1000);
DoSomething(S);
end;
Another possibility would be to use Synapse for your communication. It is a simple communications library which performs blocking calls, so works well in a single worker thread where you want to stop and wait for data rather than rely on an event to fire. The latest versions in SVN have support for the latest versions of Delphi.
So I'm having this application that verifies weather a user can log-in or not.
It consists of multiple Clients (up to 200) and a server that processes the login querys (containing Username, PW and IP). The Server checks weather the user exists and sends an answer back.
TLoginQuery is a Record.
procedure TLogin_Form.btnLoginClick(Sender: TObject);
var LoginQuery1: TLoginQuery;
begin
if not LoginSocket.Active then
begin
LoginSocket.Open;
end;
//Paketchen schnüren.
LoginQuery1.Name := ledtName.Text;
LoginQuery1.Passwort := ledtPasswort.Text;
LoginQuery1.IP := LoginSocket.Socket.LocalAddress;
//LoginQuery ín den Socket legen.
LoginSocket.Socket.SendBuf(LoginQuery1, SizeOf(LoginQuery1));
end;
The Server currently reads:
procedure TServer_Form.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var LoginQuery: TLoginQuery;
uservalid: boolean;
begin
uservalid := false;
Socket.ReceiveBuf(LoginQuery, SizeOf(LoginQuery));
if CheckIfUserValid(LoginQuery) then
begin
uservalid := true;
ServerSocket.Socket.SendBuf(uservalid, SizeOf(uservalid));
end;
end;
The question now is:
Does the server (as it should generally be) create a different socket connection per client?
My assumption:
ClientA sends his login data and recieves the uservalid boolean (code above) from the server. As the uservalid boolean is written into the socket connection the following happens: Just before ClientA can get the uservalid boolean (as it should be) ClientB, who is already logged in, reads from the socket and gets (as it should NOT be) the uservalid boolean.
This could be intervented using one socket per client. Right?
TServerSocket.OnClientRead's Socket: TCustomWinSocket parameter represents just that one connection between one of your clients and the server. Thus, when client Foo sends the login record, and TServer_Form.ServerSocketClientRead is called, just say
if CheckIfUserValid(LoginQuery) then
begin
uservalid := true;
Socket.SendBuf(uservalid, SizeOf(uservalid));
end;
and you'll send the data to the right client.
Typically, your server socket is not going to be broadcasting its outgoing messages to all connected clients. Instead, it will be choosing one specific connected client to send the response. Think of all those connections from different clients as unique. Sure, they may have some common settings, but they are unique connections. (In database-speak, the primary key for a connection is a combination of all of the server IP, server port, client IP, client port)
I've not used TServerSocket, but the IPWorks library makes this explicit by using a connection ID that is specified both on the receiving side and the sending side. This way you know that the data you are reading/writing will be using a specific connection and the data is from/to the expected client.