• Sockets in Delphi. Programming sockets in Delphi Components for working with delphi sockets

    Sockets (from socket (English) - connector, socket) are a software interface that ensures the exchange of information between processes.

    One of the main advantages of socket information exchange on the network is its flexibility. The main principle of working with sockets is to send a sequence of bytes to another computer, this can be a simple text message or a file.

    It is important to distinguish between two types of sockets: client sockets , And server sockets .

    To work with the “client” type of sockets in Delphi, there is a component TClientSocket, you can work with “server” sockets using the component TServerSocket.

    Installing components

    Often the TServerSocket and TClientSocket components are not included in the standard Delphi installation package, but they can be installed additionally.

    Go to the “Internet” components tab and check if the TServerSocket and TClientSocket components are present there; if not, install them. Go to the "Component/Install Packages" menu, then click the "Add" button. In the dialog box that opens, you need to find the file “dclsocketsXX.bpl” (it is located in the bin folder, which is located in the Delphi folder), where XX is the numeric version number of your Delphi. Locate the file, click Open, and then in the Install Packages window, click OK. Now, two components have appeared in the "Internet" tab - TServerSocket and TClientSocket.

    Working with client sockets (tClientSocket)

    1) Defining the Port and Host properties. For successful connection properties Port And Host The TClientSocket component needs to be assigned some values. In the Port property, you need to specify the port number for connection (1 – 65535, but it is better to take it from the range 1001 – 65535, because numbers up to 1000 may be occupied by system services).

    Host- host name or IP address of the computer you want to connect to. For example, rus.delphi.com or 192.128.0.0.

    2) Opening a socket. We will consider a socket as a queue of characters transmitted from one computer to another. You can open a socket by calling the method Open(TClientSocket component) or by assigning a value True property Active. Here it would be useful to add an exception handler in case of a failed connection.

    3) Sending/receiving data.

    4) Closing the socket. Upon completion of the data exchange, you need to close the socket by calling the method Close component TClientSocket or by assigning a value False property Active.

    Basic properties of the TClientSocket component

    Indicates whether the socket is open or closed. Open – True, closed – False. Available for recording.

    Hostname to connect to

    The IP address of the computer you want to connect to. Unlike Host, only IP can be specified here. The difference is that if the Host specifies the alphabetic name of the computer, then the IP will be requested from DNS

    Computer port number to connect to (1-65535)

    ClientType

    Contains data transfer type:

    ctBlocking- synchronous transmission ( OnRead And OnWrite don't work). The synchronous connection type is suitable for streaming data exchange;

    ctNonBlocking- asynchronous transmission (sending/receiving data can be done using events OnRead And OnWrite)

    Basic methods of the TClientSocket component

    Opens a socket (setting the Active property to True)

    Closes the socket (setting the Active property to False)

    Main events of the TClientSocket component

    OnConnect

    Occurs when a connection is established. In the handler you can now begin authorization or sending/receiving data

    OnConnecting

    Also occurs when connecting. Differs from OnConnect in that the connection has not yet been established. Most often used, for example, to update status

    OnDisconnect

    The event occurs when the socket is closed by your program, the remote computer, or due to a failure

    The event occurs when there is an error. During a socket opening, this event will not help catch an error. To avoid an error message from Windows, it is better to take care of internal exception handling by placing open statements in a block " try..except »

    OnLookup

    Event occurs when trying to obtain an IP address from DNS

    The event occurs when a remote computer sends you any data. When calling OnRead, it is possible to process received data

    The event occurs when your program is allowed to write data to the socket

    Introduction

    This article is devoted to creating client/server architecture applications in Borland Delphi based on sockets (“sockets”). And I wrote this article for a reason, but because recently this question has become of interest to many people. For now, we will only touch on the creation of the client part of the socket application.
    I first became acquainted with sockets, if I'm not mistaken, a year or a year and a half ago. Then the task was to develop an application protocol that would transmit a request to a server machine (running on the Unix/Linux OS) and receive a response via a socket channel. It should be noted that unlike any other protocols (FTP, POP, SMTP, HTTP, etc.), sockets are the basis for these protocols. Thus, using sockets, you can create (simulate) FTP, POP, and any other protocol yourself, and not necessarily an already created one, but even your own!

    So, let's start with the theory. If you are a convinced practitioner (and cannot see any algorithms with your eyes), then you should skip this section.

    Algorithm for working with socket protocols

    So what do sockets allow us to do?... Yes, anything! And this is one of the main advantages of this method of data exchange on the network. The fact is that when working with a socket, you simply send a sequence of characters to another computer. So with this method you can send both simple messages and entire files! Moreover, you do not need to control the correctness of the transfer (as was the case when working with COM ports)!
    Below is an example diagram of working with sockets in Delphi applications:

    Defining Host and Port properties >>> Starting a Socket (ClientSocket1.Open) >>> Authorization >>> Sending/receiving data >>> Closing a Socket

    Let's look at the diagram in more detail:
    Defining the Host and Port properties - to successfully establish a connection, you need to assign the required values ​​to the Host and Port properties of the TClientSocket component. Host is the host name (for example: nitro.borland.com) or IP address (for example: 192.168.0.88) of the computer you want to connect to. Port - port number (from 1 to 65535) to establish a connection. Typically, port numbers are taken starting from 1001 - because numbers less than 1000 may be occupied by system services (for example, POP - 110). For more details about the practical part, see below;
    Opening a socket - after you have assigned the appropriate values ​​to the Host and Port properties, you can proceed directly to opening the socket (a socket is considered here as a queue that contains characters transmitted from one computer to another). To do this, you can call the Open method of the TClientSocket component, or set the Active property to True. Here it is useful to install an exception handler in case the connection fails. You can read more about this below, in the practical part;
    Authorization - this item can be skipped if the server does not require entering any logins and/or passwords. At this stage, you send the server your login (username) and password. But the authorization mechanism depends on the specific server;
    Sending/receiving data is, in fact, what the socket connection was opened for. The communication protocol also depends on the server;
    Closing a socket - after all operations have been completed, you must close the socket using the Close method of the TClientSocket component (or set the Active property to False).

    Description of properties and methods of the TClientSocket component

    Here we will get acquainted with the main properties, methods and events of the TClientSocket component.

    Properties
    Active - shows whether the socket is open or not. Type: Boolean. Accordingly, True is open, and False is closed. This property is writable;
    Host - a string (Type: string) indicating the host name of the computer to connect to;
    Address - a string (Type: string) indicating the IP address of the computer to which you want to connect. Unlike Host, this can only contain IP. The difference is that if you specify a symbolic computer name in Host, then the IP address corresponding to this name will be requested from DNS;
    Port - port number (Type: Integer (Word)) to connect to. Valid values ​​are from 1 to 65535;
    Service - a string (Type: string) defining the service (ftp, http, pop, etc.) to which port the connection will occur. This is a kind of directory of port numbers corresponding to various standard protocols;
    ClientType - connection type. ctNonBlocking - asynchronous data transfer, i.e. You can send and receive data over a socket simultaneously using OnRead and OnWrite. ctBlocking - synchronous data transfer. The OnRead and OnWrite events do not work. This type of connection is useful for organizing data exchange using streams (that is, working with a socket as a file);

    Methods
    Open - opening a socket (similar to assigning the value True to the Active property);
    Close - closing the socket (similar to assigning the value False to the Active property);

    This is where all the methods of the TClientSocket component are exhausted. And you ask: “But how to work with a socket? How then to send data?” You will learn about this a little further.

    Practice and examples

    The easiest (and most useful) way to learn any programming method is to practice it. Therefore, the following are examples with some comments:

    Example 1. The simplest socket program

    (You need to place a TButton button and two TEdits into the form. When you click on the button, the OnClick event handler - Button1Click is called. Before that, you need to enter the host name in the first of the TEdits, and the port of the remote computer in the second. DO NOT FORGET TO PLACE THE COMPONENT IN THE FORM TClientSocket !}


    begin
    (Assign the Host and Port properties the required values)
    ClientSocket1.Host:= Edit1.Text;
    ClientSocket1.Port:= StrToInt(Edit2.Text);
    (We are trying to open the socket and establish a connection)
    ClientSocket1.Open;
    end;


    begin
    (As soon as the connection has occurred, close the socket and interrupt the connection)
    ClientSocket1.Close;
    end;

    If you think that this example program is completely useless and cannot bring any benefit, then you are deeply mistaken. The given code is the simplest example of a port scanner (PortScanner). The essence of such a utility is to check whether the specified port is enabled and whether it is ready to receive/transmit data. PortScanner from the NetTools Pro program is based on this principle.

    Example 2. Sending/receiving text messages over sockets

    (You need to place two TButtons and three TEdits in the form. When you click on the first button, the OnClick event handler - Button1Click is called. Before that, you need to enter the host name in the first of the TEdits, and the port of the remote computer in the second. After the connection is established, you can send text messages by entering text in the third TEdit and pressing the second TButton. To disconnect, you need to press the first TButton again. You also need to add a TListBox, into which we will place the received and sent messages. DO NOT FORGET TO PLACE THE TClientSocket COMPONENT IN THE FORM. !}

    procedure Button1Click(Sender: TObject);
    begin
    (If the connection has already been established, interrupt it.)
    if ClientSocket1.Active then begin
    ClientSocket1.Close;
    Exit; (...and exit the handler)
    end;
    (Assign the Host and Port properties the required values)
    ClientSocket1.Host:= Edit1.Text;
    ClientSocket1.Port:= StrToInt(Edit2.Text);
    (We are trying to open the socket and establish a connection)
    ClientSocket1.Open;
    end;


    begin
    (As soon as the connection occurs, we send a greeting)
    Socket.SendText(′Hello!′);
    ListBox1.Items.Add(′< Hello!′);
    end;

    procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
    begin
    (If a message has arrived, add it to the ListBox)
    ListBox1.Items.Add(′> ′+Socket.ReceiveText);
    end;

    procedure Button2Click(Sender: TObject);
    begin
    (Button pressed - send text from third TEdit)
    ClientSocket1.Socket.SendText(Edit3.Text);
    ListBox1.Items.Add(′< ′+Edit3.Text);
    end;

    NOTE: In some cases (depending on the server) it is necessary to send a newline after each message:
    ClientSocket1.Socket.SendText(Edit3.Text+#10);

    Working with a socket stream

    “How else can you work with a socket?” you ask. Naturally, the above method is not the best solution. There are a lot of methods for organizing work with sockets. I will give just one more additional one - working through a stream. Surely, many of you already have experience working, if not with streams, then with files, for sure. For those who don't know, a stream is a channel for exchanging data, working with it is similar to working with a regular file. The following example shows how to organize a thread to work with a socket:

    Example 3. Thread for working with a socket

    procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
    var c: Char;
    MySocket: TWinSocketStream;
    begin
    (As soon as the connection has occurred, we create a thread and associate it with the socket (60000 - timeout in ms))
    MySocket:= TWinSocketStream.Create(Socket,60000);
    (The WaitForData operator waits for data from the stream for the specified time in ms (100 in this example) and returns True if at least one byte of data has been received, False if there is no data from the stream.)
    while not MySocket.WaitForData(100) do
    Application.ProcessMessages;
    (Application.ProcessMessages allows Windows to redraw the necessary elements of the window and gives time to other programs. If this statement were not there and the data did not arrive for quite a long time, the system would freeze slightly.)
    MySocket.Read(c,1);
    (The Read operator reads a specified number of bytes from a stream (in this example, 1) into a specified variable of a specific type (in the example, into a Char variable c). Note that Read, unlike ReadBuffer, does not impose strict restrictions on the amount of information received. That is, Read reads at most n bytes from the stream (where n is the specified number. This function returns the number of data bytes received.)
    MySocket.Write(c,1);
    (The Write statement is similar to the Read statement, except that Write writes data to a stream.)
    MySocket.Free;
    (Don't forget to free the memory allocated for the thread)
    end;

    NOTE: To use a stream, be sure to set the ClientType property to ctBlocking.

    Sending/receiving complex data

    Sometimes it is necessary to send not only simple text messages over the network, but also complex structures (the record type in Pascal), or even files. And then you need to use special operators. Some of them are listed below:

    Methods TClientSocket.Socket (TCustomWinSocket, TClientWinSocket):
    SendBuf(var Buf; Count: Integer) - Sending a buffer through the socket. The buffer can be any type, be it a structure (record) or a simple Integer. The buffer is indicated by the Buf parameter, the second parameter you must specify the size of the transferred data in bytes (Count);
    SendText(const S: string) - Send a text string via a socket. This method was discussed in example 2 (see above);
    SendStream(AStream: TStream) - Sends the contents of the specified stream via a socket. The forwarded stream must be open. The stream can be of any type - file, from RAM, etc. A description of working directly with threads is beyond the scope of this article;
    All of the listed methods correspond to the Receive... Their description can be found in the Delphi help file (VCL help).

    Finally, I would like to give a simple example of how you can implement authorization (login to the server). In this example, the password is sent in clear text, so if you want a truly secure login mechanism, you will have to make some changes to the source code of this example. The example is implemented as working with a socket stream.

    (In this example, you need to add two more TEdits to the form - Edit3 and Edit4 for entering your login and password)

    procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
    var c: Char;
    MySocket: TWinSocketStream;
    login,password: string;
    begin
    MySocket:= TWinSocketStream.Create(Socket,60000);
    (We add a newline character to the login and password so that the server can separate the login and password.)
    login:= Edit3.Text+#10;
    password:= Edit4.Text+#10;
    MySocket.Write(login,Length(Edit3.Text)+1);
    MySocket.Write(password,Length(Edit4.Text)+1);
    while not MySocket.WaitForData(100) do
    Application.ProcessMessages;
    MySocket.Read(c,1);
    (Here the server sends us one byte, the value of which 1 corresponds to confirmation of successful authorization, and 0 - an error (this is just an example). Next, we perform the necessary actions (receiving/sending data) and close the stream.)
    MySocket.Free;
    end

    Beginner programmers (and I myself, when I started learning Delphi) ask the question: how can I transfer a file via sockets, if in addition to this file, a lot of information is transferred via the socket!? It seems that the problem is not so complicated, but still not an easy one... After a long search on the Internet, I still haven’t found a single useful article on this topic. So I decided to correct this shortcoming, and in this article I will try to help solve this problem...

    Let's write a program that can transfer files via sockets (client and server), and in addition other commands, for example some kind of message! The client will receive files or commands, and the server will send. If the client writes everything into the buffer, then in addition to the file, it will also contain commands, and we need to make sure that files and commands do not merge under any circumstances! You also need to take into account that if the file is large, then when transferred, it will be cut into several packets, that is, the file will be sent not in one packet, but in several, and the OnClientRead event will be called several times... This is the main problem of transfer!

    To be able to separate commands from the file, we first send the client something like this line: “file#file.txt#16”, that is: command + separator + file name + separator + file size.
    When receiving this command, the client will switch to file receiving mode and write everything to the buffer until the file size is equal to the size of the received data. This way the client will separate the commands from the file!

    So let's start writing code:
    Let's start with the server (it will send the file):

    Place the following components on the form: TServerSocket, TButton, TEdit, TProgressBar and TStatiusBar. Place them as shown in the picture.
    Set the TServerSocket component to port: 1001.
    Set the TStatusBar component and the SimplePanel variable to true.
    In the line, the name of the file to be transferred is entered, the TButton button is used to transfer the file.

    First, let's add a buffer for the file to the global variables:

    Var Form1: TForm1; MS: TMemoryStream; // Buffer for file

    Now let’s make sure that when creating a form, a socket opens:

    Procedure TForm1.FormCreate(Sender: TObject); begin ServerSocket1.Open; // Open the socket end;

    When the application terminates, you must remember to close the socket:

    Procedure TForm1.FormDestroy(Sender: TObject); begin ServerSocket1.Close; // Close the socket end;

    When you click the button, we send the file:

    Procedure TForm1.Button1Click(Sender: TObject); // Transfer the file var Size: integer; P: ^Byte; begin MS:= TMemoryStream.Create; // Create a buffer for the file MS.LoadFromFile(Edit1.Text); // Load the file into the buffer // Send information about the file (command # name # size) ServerSocket1.Socket.Connections.SendText("file#"+Edit1.Text+"#"+IntToStr(MS.Size)+"#") ; MS.Position:= 0; // Move the carriage to the beginning of the file P:= MS.Memory; // Load the file Size into the variable "P":= ServerSocket1.Socket.Connections.SendBuf(P^, MS.Size); // Send the file // Display progress ProgressBar1.Position:= Size*100 div MS.Size; StatusBar1.SimpleText:= "Sent "+IntToStr(Size)+" of "+IntToStr(MS.Size)+" bytes"; end;

    On the OnClientRead event of the TServerSocket component, enter the following code:

    Procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); begin if Socket.ReceiveText = "end" then // If the client has received the file, then... begin StatusBar1.SimpleText:= "The client has received the file"; MS.Free; // Kill the buffer end; end;

    This is necessary so that the server kills the buffer only after the client accepts the file. If you kill the buffer immediately after transferring the file, the client will not have time to receive the entire file! As soon as the client accepts the file, it will send the server the "end" command, which means the file has been accepted, and the server will kill the buffer.

    Now let’s make our server display some information about the connection:
    On the OnClientConnect event of the TServerSocket component, enter the following code:

    Procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Connection established"; end;

    And on the OnClientDisconnect event enter:

    Procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Connection not established"; end;

    Now the server is ready! Now let's move on to the client (it accepts the file), there will be more fuss with it:

    Place the components on the forum: TClientSocket, two TLabels, TProgressBar and TStatusBar.
    Set the TClientSocket component to port: 1001 (like the server), and the address variable: 127.0.0.1 (your IP).
    Don't forget to set the TStatusBar component and the SimplePanel variable to true so that our text is visible.
    In one TLabel the file name is displayed, in another the file size.
    You should end up with something similar to this:

    We declare variables and one procedure. Write the variables exactly in private, otherwise nothing will work:

    Procedure Writing(Text: string); // Procedure for writing data to the buffer private ( Private declarations ) Name: string; // File name Size: integer; // File size Receive: boolean; // MS client mode: TMemoryStream; // Buffer for file

    At the form creation event, we connect to the server and wait for the file to be transferred:

    Procedure TForm1.FormCreate(Sender: TObject); begin ClientSocket1.Open; // Open the socket Receive:= false; // Client mode - receiving commands end;

    When the application terminates, close the socket:

    Procedure TForm1.FormDestroy(Sender: TObject); begin ClientSocket1.Close; // Close the socket end;

    In the same way as with the server, we will make the client provide information about the connection:

    Procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Connection established"; end; procedure TForm1.ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Connection not established"; end;

    Now we need to enter the code into the Writing procedure. This procedure is needed in order to write received data to a file. Procedure code:

    Procedure TForm1.Writing(Text: string); begin if MS.Size< Size then // Если принято байт меньше размера файла, то... MS.Write(Text, Length(Text)); // Записываем в буфер // Выводим прогресс закачки файла ProgressBar1.Position:= MS.Size*100 div Size; StatusBar1.SimpleText:= "Принято "+IntToStr(MS.Size)+" из "+IntToStr(Size); if MS.Size = Size then // Если файл принят, то... begin Receive:= false; // Переводим клиента в нормальный режим MS.Position:= 0; // Переводим каретку в начало буфера MS.SaveToFile(Name); // Сохраняем файл ClientSocket1.Socket.SendText("end"); // Посылаем команду "end", то есть файл принят MS.Free; // Убиваем буфер StatusBar1.SimpleText:= "Файл принят"; end; end;

    Now, on the OnClientRead event of the TClientSocket component, enter the following code:

    Procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); var Rtext: string; // Received text begin Rtext:= Socket.ReceiveText; if Receive then // If the client is in file receiving mode, then... Writing(RText) // Write data to the buffer else // If the client is not in file receiving mode, then... begin if Copy(Rtext, 0, Pos ("#", Rtext) -1) = "file" then // If this is a file, then... begin MS:= TMemoryStream.Create; // Create a buffer for the file Delete(Rtext, 1, Pos("#", Rtext)); // Determine the file name Name:= Copy(Rtext, 0, Pos("#", Rtext) -1); // Determine the file name Delete(Rtext, 1, Pos("#", Rtext)); // Determine the file size Size:= StrToInt(Copy(Rtext, 0, Pos("#", Rtext) -1)); // Determine the file size Delete(Rtext, 1, Pos("#", Rtext)); // Remove the last separator Label1.Caption:= "File size: "+IntToStr(Size)+" bytes"; // Display the file size Label2.Caption:= "File name: "+Name; // Print the file name Receive:= true; // Switch the server to file receiving mode Writing(RText); // Write data to the buffer end; end; end;

    Thus, if the file is large, and the OnClientRead event will be called not once, but several times, then if the client is in file receiving mode, it will write data to the buffer, but if not, then the client will determine the received command, and if it is a file, then will switch to file receiving mode. If you don’t understand something, then read the program code, it’s not for nothing that I uncommented everything there :-)

    Well that's all...
    Client and server are ready! First, start the server, and then the client, and try to transfer files several megabytes in size :-) I sent files of 10-12 MB in size over the network without any problems.

    Have fun with your programming!

    This article will discuss the basic properties and functions of the Delphi components: TClientSocket and TServerSocket - used to work with the network using the TCP\IP protocol.

    Attention! If you are using a version of Delphi higher than 6.0, then you first need to install Sockets Components; in all versions of Delphi this is done as follows:

    • Go to the Install Packages... dialog: (Main menu) Component -> Install Packages;
    • Click on the Add… button, after which we find the Bin folder of your Delphi (for example: C:\Program Files\Borland\Delphi 7\bin, or C:\Program Files\Embarcadero\RAD Studio\7.0\Bin);
    • In the found Bin folder we are already looking for the file dclsockets [here are the numbers].bpl, click OK;
    • We are happy because now we have two wonderful components TServerSocket and TClientSocket on the Internet tab of the component panel.

    When developing any network applications, usually development always starts with the server (of course, if you have a team, interface designers can start working on the client). To implement a server, surprisingly, you need to use TServerSocket.

    Main properties:

    • Active– a boolean type field, when set to true – the server starts, you can use it either by assigning specific values ​​or by calling the functions ServerSocket1.Open (... Active:=true;) or ServerSocket1.Close (... Active:=false).
    • Port– the port on which the server will listen (receive clients), any value within the range not occupied by other servers in the system integer.

    Main events:

    • OnListen– called when the server is set to listening mode, can be used when we need to determine the time of the real start of the server.
    • OnClientRead– called when data is received from the client.
    • OnClientError
    • OnClientConnect– Called when a new client joins the server.
    • OnClientDisconnect– reverse event to event, OnClientConnect

    every event function has an attribute Socket: TCustomWinSocket, a pointer to the socket object with which we are currently working is passed, if we need to respond or do something with the client that caused the event, we need to use this particular object, in all other cases we use ServerSocket1.Socket, the situation is similar with the client component.

    Readonly properties and functions:

    • – returns the number of active connections.
    • ServerSocket1.Socket.Connections– an array of objects of the TCustomWinSocket type, an array of all objects associated with clients, index counting starts from 0, the length of the array is ServerSocket1.Socket.ActiveConnections.
    • Functions and properties that apply to the elements of the ServerSocket1.Socket.Connections array and the Socket attribute passed to the server event function:
    • Socket.LocalHost
    • Socket.LocalAddress– returns the server IP.
    • Socket.RemoteHost
    • Socket.RemoteAddress– returns the client IP.
    • Socket.ReceiveText– returns a text message received from the client, after which it clears the buffer, can be used only 1 time, per 1 reception.
    • Socket.SendText(Text)– sends a text message of type Text to the client string.

    For the TClientSocket component, everything is practically the same, only in reverse + the main visual difference between the server and the client is that the server in the system can be launched 1 by 1 port value, the number of clients is limited only by RAM.

    Main properties:

    • Active– a boolean field, when set to true – the client is trying to connect to the server, can be used either by assigning specific values ​​or by calling the functions ClientSocket1.Open (... Active:=true;) or ClientSocket1.Close (... Active:=false) .
    • Port– port through which the client can connect to the server, any value within the range integer.
    • Address– IPv4 address of server type string according to the pattern 255.255.255.255, with which the client will connect.

    Main events:

    • OnRead– called when data is received from the north.
    • OnError– called when an error occurs in data transmission.
    • OnConnecting– Called when a client joins the server.
    • OnDisconnect– reverse event to event, OnConnecting, called when a client disconnects from the server.

    Readonly properties and functions:

    • ClientSocket1.Socket.SendText() string
    • Socket.LocalHost– returns the client's online name.
    • Socket.LocalAddress– returns the client IP.
    • Socket.RemoteHost– returns the name of the server on the network.
    • Socket.RemoteAddress– returns the server IP.
    • Socket.ReceiveText– returns a text message received from the server, after which it clears the buffer, can be used only 1 time, at a time.
    • Socket.SendText(Text)– sends a text message of type Text to the server string.

    The information provided is quite enough to implement a small server chat that meets the technical specifications: internet_sockets.doc (Word Doc 97-2003, 26.5 Kb).

    This article was written on Sunday, October 10th, 2010 at 1:24 am in the section. You can subscribe to updates on comments to the article -. You can


    Introduction


    This article is devoted to creating client/server architecture applications in Borland Delphi based on sockets (“sockets” - nests). Unlike the previous article on the topic of sockets, here we will look at creating server applications.

    It should be immediately noted that for the coexistence of separate client and server applications, it is not necessary to have several computers. It is enough to have only one on which you can simultaneously run both the server and the client. In this case, you need to use the host name as the name of the computer to which you want to connect localhost or IP address - 127.0.0.1 .

    So, let's start with the theory. If you are a convinced practitioner (and cannot see any algorithms with your eyes), then you should skip this section.

    Socket server operation algorithm


    What does a socket server allow you to do?.. On what principle does it work?.. A server based on the socket protocol allows you to serve many clients at once. Moreover, you can specify the limit on their number yourself (or remove this limit altogether, as is done by default). For each connected client, the server opens a separate socket through which you can exchange data with the client. Another great solution is to create a separate process (Thread) for each connection.

    Below is an example diagram of how a socket server works in Delphi applications:

    Let's look at the diagram in more detail:

    • Definition of Port and ServerType properties - in order for clients to be able to connect to the server normally, it is necessary that the port used by the server exactly matches the port used by the client (and vice versa). The ServerType property determines the connection type (see below for more details);
    • Opening a socket - opening the socket and the specified port. This is where we automatically start waiting for clients to connect ( Listen);
    • Connecting a client and exchanging data with it - here the client connects and exchanges data with it. You can find out more about this stage below in this article and in the article about sockets (client part);
    • Disabling a client - Here the client disconnects and its socket connection with the server is closed;
    • Closing the server and socket - At the administrator’s command, the server shuts down, closing all open socket channels and stopping waiting for client connections.

    It should be noted that points 3-4 are repeated many times, i.e. These steps are performed for each new client connection.

    Note : There is very little documentation on sockets in Delphi at the moment, so if you want to study this topic as deeply as possible, I advise you to look through the literature and electronic documentation on Unix/Linux systems - there Very The theory of working with sockets is well described. In addition, there are many examples of socket applications for these OSes (though mostly in C/C++ and Perl).

    Brief description of the TServerSocket component


    Here we will get acquainted with main properties, methods and events of a component TServerSocket.

    Properties

    Socket - the TServerWinSocket class, through which you have access to open socket channels. Next we will consider this property in more detail, because it is, in fact, one of the main ones. Type: TServerWinSocket ;
    ServerType - server type. Can take one of two values: stNonBlocking- synchronous work with client sockets. With this type of server you can work with clients through events OnClientRead And OnClientWrite. stThreadBlocking- asynchronous type. A separate process (Thread) is created for each client socket channel. Type: TServerType ;
    ThreadCacheSize - the number of client processes (Thread) that will be cached by the server. Here you need to select the average value depending on the load on your server. Caching occurs in order not to create a separate process each time and not to kill a closed socket, but to leave them for later use. Type: Integer ;
    Active - an indicator of whether the server is active at a given moment or not. That is, in fact, the value True indicates that the server is running and ready to receive clients, and False- the server is turned off. To start the server, you simply need to set this property to True. Type: Boolean ;
    Port - port number for establishing connections with clients. The server and client ports must be the same. Values ​​from 1025 to 65535 are recommended, because from 1 to 1024 - can be occupied by the system. Type: Integer ;
    Service - a string defining the service ( ftp, http, pop, etc.) whose port will be used. This is a kind of directory of port numbers corresponding to various standard protocols. Type: string ;

    Open - Starts the server. Essentially this command is identical to assigning a value True property Active;
    Close - Stops the server. Essentially this command is identical to assigning a value False property Active.

    OnClientConnect - occurs when the client has established a socket connection and is waiting for a response from the server ( OnAccept);
    OnClientDisconnect - Occurs when the client has disconnected from the socket channel;
    OnClientError - occurs when the current operation fails, i.e. an error occurred;
    OnClientRead - occurs when the client has sent some data to the server. This data can be accessed through a passed parameter Socket: TCustomWinSocket;
    OnClientWrite - occurs when the server can send data to the client over a socket;
    OnGetSocket - in the handler of this event you can edit the parameter ClientSocket;
    OnGetThread - in the handler of this event you can define a unique process (Thread) for each individual client channel by assigning the parameter SocketThread the desired subtask TServerClientThread;
    OnThreadStart , OnThreadEnd - occurs when a subtask (process, Thread) is started or stopped, respectively;
    OnAccept - occurs when the server accepts the client or refuses him a connection;
    OnListen - occurs when the server goes into waiting mode for clients to connect.


    TServerSocket.Socket(TSServerWinSocket)


    So how can the server send data to the client? What about receiving data? Mainly if you are working through events OnClientRead And OnClientWrite, then you can communicate with the client through the ClientSocket (TCustomWinSocket) parameter. You can read about working with this class in the article about client sockets, because sending/sending data through this class is similar - methods (Send/Receive)(Text,Buffer,Stream). The same applies when working with TServerSocket.Socket. However, because Here we are considering a server, we should highlight some useful properties and methods:

    • ActiveConnections (Integer) - number of connected clients;
    • ActiveThreads (Integer) - number of running processes;
    • Connections (array) - an array consisting of separate TClientWinSocket classes for each connected client. For example, this command:
      ServerSocket1.Socket.Connections.SendText("Hello!");
      sends a "Hello!" message to the first connected client. Commands for working with elements of this array - also (Send/Receive)(Text,Buffer, Stream);
    • IdleThreads (Integer) - the number of free processes. Such processes are cached by the server (see ThreadCacheSize);
    • LocalAddress, LocalHost, LocalPort- respectively - local IP address, host name, port;
    • RemoteAddress, RemoteHost, RemotePort- respectively - remote IP address, host name, port;
    • Methods Lock And Unlock- respectively, blocking and unblocking the socket.

    Practice and examples


    Now let's look at the above using a specific example. You can download ready-made sources by clicking.

    So, let's look at a very good example of working with TServerSocket (this example is the most visual aid for studying this component). The sources below demonstrate logging of all important server events, plus the ability to receive and send text messages:

    Example 1. Logging and studying the operation of the server, sending/receiving messages via sockets

    (...Here goes the file header and the definition of the form TForm1 and its instance Form1)
    (See full source)
    procedure TForm1.Button1Click(Sender: TObject); begin (Determine the port and start the server) ServerSocket1.Port:= 1025; (The Insert method inserts a string into the array at the specified position) Memo2.Lines.Insert(0,"Server starting"); ServerSocket1.Open; end; procedure TForm1.Button2Click(Sender: TObject); begin (Stop the server) ServerSocket1.Active:= False; Memo2.Lines.Insert(0,"Server stopped"); end; procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket); begin (Here the server is "listening" on the socket for clients) Memo2.Lines.Insert(0,"Listening on port "+IntToStr(ServerSocket1.Port)); end; procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket); begin (Here the server accepts the client) Memo2.Lines.Insert(0,"Client connection accepted"); end; procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin (Here the client connects) Memo2.Lines.Insert(0,"Client connected"); end; procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin (Here the client disconnects) Memo2.Lines.Insert(0,"Client disconnected"); end; procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin (An error occurred - display its code) Memo2.Lines.Insert(0,"Client error. Code = "+IntToStr(ErrorCode)); end; procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); begin (A message was received from the client - display it in Memo1) Memo2.Lines.Insert(0,"Message received from client"); Memo1.Lines.Insert(0,"> "+Socket.ReceiveText); end; procedure TForm1.ServerSocket1ClientWrite(Sender: TObject; Socket: TCustomWinSocket); begin (Now you can send data to the socket) Memo2.Lines.Insert(0,"Now can write to socket"); end; procedure TForm1.ServerSocket1GetSocket(Sender: TObject; Socket: Integer; var ClientSocket: TServerClientWinSocket); begin Memo2.Lines.Insert(0,"Get socket"); end; procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread); begin Memo2.Lines.Insert(0,"Get Thread"); end; procedure TForm1.ServerSocket1ThreadEnd(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Thread end"); end; procedure TForm1.ServerSocket1ThreadStart(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Thread start"); end; procedure TForm1.Button3Click(Sender: TObject); var i: Integer; begin (Send a message to ALL clients from Edit1) for i:= 0 to ServerSocket1.Socket.ActiveConnections-1 do begin ServerSocket1.Socket.Connections[i].SendText(Edit1.Text); end; Memo1.Lines.Insert(0,"

    Techniques for working with TServerSocket (and simply with sockets)


    Storing unique data for each client.


    Surely, if your server will serve many clients, then you will need to store some information for each client (name, etc.), and bind this information to the socket of this client. In some cases, doing all this manually (binding to a socket handle, client arrays, etc.) is not very convenient. Therefore, for each socket there is a special property - Data. In fact, Data is just a pointer. Therefore, when writing client data to this property, be careful and follow the rules of working with pointers (memory allocation, type definition, etc.)!

    Sending files via socket.


    Here we will look at sending files via a socket (at the request of JINX) :-). So how do you send a file over a socket? Very simple! All you have to do is open this file as a file stream (TFileStream) and send it via a socket (SendStream)! Let's look at this with an example:

    It should be noted that the method SendStream used not only by the server, but also by the client ( ClientSocket1.Socket.SendStream(srcfile))

    Why can several blocks be combined into one during transmission?


    This is also at JINX's request :-). Many thanks to him for this! So, firstly, it should be noted that data sent through a socket can not only be combined into one block, but also separated over several blocks. The fact is that a socket is a regular stream, but unlike, say, a file stream (TFileStream), it transfers data more slowly (you understand - network, limited traffic, etc.). That's why two commands:
    ServerSocket1.Socket.Connections.SendText("Hello, ");
    ServerSocket1.Socket.Connections.SendText("world!");
    completely identical to one command:
    ServerSocket1.Socket.Connections.SendText("Hello, world!");

    And that is why, if you send a file of, say, 100 KB through a socket, then the person to whom you sent this block will receive several blocks with sizes that depend on the traffic and line congestion. Moreover, the sizes will not necessarily be the same. It follows that in order to accept a file or any other large data, you should accept blocks of data and then combine them into one whole (and save, for example, to a file). An excellent solution to this problem is the same file stream - TFileStream (or a stream in memory - TMemoryStream). You can receive pieces of data from a socket through the OnRead (OnClientRead) event using the universal method ReceiveBuf. You can determine the size of the resulting block using the method ReceiveLength. You can also use a socket stream (see the article about TClientSocket). And here is a small example (approximate):

    How to monitor a socket


    This issue is complex and requires long consideration. For now, I’ll just note that you can always monitor the socket created by your program :-). Sockets (like most objects in Windows) have their own handle, written in the Handle property. So, once you recognize this descriptor, you will be able to freely manage any socket (even one created by someone else’s program)! However, most likely, to monitor someone else's socket, you will have to use exclusively the WinAPI Sockets functions.


    This article shows the basic techniques for working with the TServerSocket component in Delphi and several general techniques for exchanging data over sockets. If you have questions, send them to me by email: [email protected], and even better - write in the conference of this site (Delphi. General questions) so that other users can see your question and try to answer it!

    Karikh Nikolay ( Nitro). Moscow region, Zhukovsky