Delphi Socket Architecture - Felix John COLIBRI.
  • abstract : The architecture of the ScktComp socket CLASSes
  • key words : Sockets, WinSock, Windows Socket, ScktComp, socket UML class diagram, socket file transfer, tClientSockettServerSocket, HTTP server
  • software used : Windows XP, Delphi 6
  • hardware used : Pentium 1.400Mhz, 256 M memory, 140 G hard disc
  • scope : Delphi 1 to 8 for Windows
  • level : Delphi developer
  • plan :

1 - Socket Programming in Delphi

Sockets were first introduced under Unix. Windows later, much later, followed. Since Windows has no easy multitasking library, a consortium defined a WindowsSocket specification which could use the Windows messages for socket scheduling. Third parties offered libraries (Trumpet was one of those). Later Windowsincluded its own WinSock version. And later Windows offered threading, which allows blocking sockets without message notifications.

Therefore it is a small wonder that Delphi had to change its VCL components several times to allow networking. To make a long story short:

  • NetManage components were first offered. This component set

    • allows the whole TCP/IP gamut: basic client and server, but also HTTP client, FTP client and server, POP3 reader etc
    • was licensed by Borland but the source never provided
    • many bugs were reported
  • at some time, we heard about John KASTER writing a book about socket programming. I never saw this book (and I believe nobody else did). I seem to remember that he then was hired by Borland, and there were rumors about rewriting the Socket component suite. John KASTER then managed the very useful Borland Community site, and we could watch him proudly grinning at us every time we visited this site. Anyway, he sure had plenty of web programming and administration on his hands. A pitty that the Delphi programs used for this site were not published. But John KASTER also published nice papers available on the community site.
  • in any case, NetManage remained on the Palette, but Delphi added tClientSocket and tServerSocket components, with source code this time
  • thereafter, NetManage was quietly put aside, and the Indy component suite placed on the component palette

This paper will present the client and server socket component organization, as well as a small file exchange application.

2 - The ScktComp unit

2.1 - Required Classes

Communication between a Server and a Client needs:

  • on the Client side, a socket for connecting, reading and writing
  • on the Server side
    • a socket for welcoming the Clients
    • a new socket for each Client which has established a connection

To review what is really needed, have a look at the Socket Programming article.

In any case, a component library will naturally include:

  • Client class, with all the connection, read and write functionality
  • Server class, with the welcoming socket and a list of sockets handling the connected clients

However there are several aspects:

  • the windows message mechanism is the same for the Server and the Client. So it seems natural to place the whole event processing in a base class
  • the library users should be able to choose between several possibilities:
    • use windows messaging asynchronous mode, or blocking mode
    • in blocking mode, threading should be offered
    • reception and emission could be encapsulated in Streams

2.2 - The ScktComp Unit

The tClientSocket and tServerSocket have been place in a single 90 K unit, which contains 12 classes. Splitting those into 6 units helped us better understand the relationships between those classes. Basically there are two layers:

  • the first layer is concerned with the Winsock encapsulation:

    • start the wsa library (Windows Sockets Asynchronous= Winsock.Dll)
    • call the functions (SocketBindSend etc)
    • handle the notifications
  • the second layer handles the user point of view:
    • store the parameters (IP address, blocking or asynchronous mode ...)
    • call the main functions (ConnectSendRecv)
    • receive the events

2.3 - The WinSock encapsulation

  • tCustomWinsocket (40 K)is the base CLASS. All functions, whether Client or Server are included. Some functions are only included to update internal states but the calls to the WinSock library is performed in the derived Client or Server CLASS. This is the case, for instance, for Accept, where:

    • this CLASS contains only an empty method
    • the derived tServerWinSocket contains the real Winsock.Accept call

    This CLASS also added DNS Lookup features. If we only know the symbolic URL (www.Borland.com), the Lookup will find the numeric address (80.15.235.71). In most Socket programming, this lookup is done in blocking mode: there is not much the user can do if the numeric IP required by theConnect function is unknown. However, Delphi included an asynchronous lookup, which is woven in the same WinProc as the other asynchronous socket Windows notification messages.

    Those Windows message are handled by a Window created by the tCustomWinsocket using the Delphi AllocateHwnd call. This simply calls theRegisterClassCreateWindow routines, which any reader of Charles PETZOLD Windows 3 programming book certainly remembers (THE book that really started Windows).

  • tClientWinSocket (2 K) adds the Client specific part (is the Client blocking or not, call Connect)
  • tServerWinSocket (13 K) add the Server specific part: ListenAccept.

    The tServerWinSocket also includes a tList of tServerClientWinSocket (which is very small), and all ServerClientEvents are delegated to thetServerWinSocket. This allows the final user to only deal with the tServerWinsocket, using events with the specific tServerClientWinsocket parameter.

    Each tServerClientWinSocket has a back link to the tServerWinSocket, which allows to manage the tList addition and deletion from thetServerClientWinSocket side

To add threading capabilities, the following CLASSES (17 K) have been added:

  • tServerAcceptThread: this thread is used to isolate the tServerWinSocket in a thread. This is normally used only if the Server is in blocking (not Windows Asynchronous) mode
  • tServerClientThread: each incoming Client can be handled in its own thread. Each tServerClientWinSocket has a link to its thread. And thetServerWinSocket contains a tList of all those threads

As usual, threading add its share of complexity: there is a thread cache, one has to wait for thread termination etc.

And to read and write socket as streams, the following tStream descendent is included:

  • tWinSocketStream (4 K)is a tStream descendent. It contains a tCustomWinSocket member, and the Read and Write stream methods. Read and Write use the Windows Overlapped mode. This CLASS is not used anywhere else in ScktComp, but used in the SvrHTTP HTTP Web server unit.

    Also not that the base tCustomWinSocket has Stream possibilities (the tCustomWinSocket.SendStream function) but the stream is just used to hand over the bytes to send (not to perform read write synchronization like the tWinSocketStream)

In addition, when we use threads, there are some access protection to take care of:

  • the tCustomWinSocket contains an FSocketLock critical section which is used

    • when we access socket properties (local or remote AddressHostPort
    • when we Send or Recv
    • when we Disconnect
  • the tServerWinSocket protects the tServerClientWinSocket list with a FListLock critical section

2.4 - The User classes

The user is mainly concerned with

  • initializing the connection parameters,
  • connect, read and write data,
  • and get events if the asynchronous mode.

For this purpose, we have the following CLASSes:

  • tAbstractSocket and its associated tCustomSocket (10 K), essentially containing the parameters, and Open and Close methods
  • tClientSocket (3 K) essentially promotes the visibility of the client tCustomSocket properties, methods and events
  • tCustomServer and tServerSocket (7 K) also promote the server tCustomSocket fields

2.5 - The UML class diagram

Base on the previous analysis, we can draw the following diagram:

Note that

  • the blue classes are Client specific
  • the green classes are Server specific
  • we framed in red the classes placed on the Delphi Palette

2.6 - ScktComp usage

The ScktComp is used in the following VCL units:

  • ScktMain which is some kind of socket monitoring application
  • sConnect used by the n-tier database architecture (Midas)
  • SvrHTTP which is an HTTP server
  • SvrLog which journals the HTTP activities

There is a Chat demo application in the DEMO directory of Delphi. This is a very elegant sample, since all the people taking part in a chat use the same application. The user either start with "Listen" or "Connect" and the tForm1 contains an IsServer Boolean which tells whether the application should use the tServerSocket or thetClientSocket. And all is contained in 7 K. I would be reluctant though to use this application as a Socket programming first example, since most Socket application are disymmetric by nature: there is a Server application handling server tasks, and a separate Client application used by all the clients dedicated to client business. The server receives an HTML page requests and uses CGI, ISAPI, ASP etc to build the page and send it over to the Client. On the other side, the Client receives the page and renders it, analyzing Style Sheets parameters and monkeying around with Visual Basic Script, Java Script or ActiveX components. Quite different jobs.

The tClientSocket and tServerSocket are also published on the Palette to let us build Socket application which:

  • implement any standard TCP/IP protocol (HTTP, SMTP, POP3, FTP etc)
  • implement our own protocols (sending and receiving files etc) or protocols which are not implemented by Indy, or special protocols (CVS, Spidering, Peer to Peer etc)

We will now build a simple file transfer application as an example.


3 - Socket File Transfer

3.1 - The Goal

The Client will send a file name, and the Server will send the file back. To avoid typing errors, the Client selects the file names in a tFileListBox (so in effect he has already access to the files, but let's pretend that he cannot grab them directly).

The Server loads the file and uses Send to transfer the file.

3.2 - The Client

The Client application uses a tClientSocket. We have the following possibilities:

  • "connect" starts the connection
  • "disconnect" stops it
  • clicking on a filename in the tListBox will send the file name to the Server, using SendText

The tClientSocket.OnRead will be notified of data reception. The Client first reads the file size (4 bytes) and then reads the packets in a loop, until the reception buffer is empty. If the file was not received, the loop is exited, and the next OnRead will fetch the next packets.

3.3 - The Server

The "listen" and "disconnect" buttons allow to start or stop the Server.

The requested file will be read using a tFileStream and sent over to the client with tServerSocket.SendStream or tServerSocket.SendBuff. Since several Clientstransfer might overlap, we have to use a separate tFileStream for each incoming Client. We chose to save those tFileStreams as pointers in the tListBox.Objects. The key we chose to identify each Client is the ServerClientSocket handle.

Here is the tServerSocket.OnAccept handler:

function f_socket_key(p_c_server_client_socketTCustomWinSocket): String;
  begin
    Result:= IntToStr(p_c_server_client_socket.SocketHandle)
  end; // f_socket_key

procedure Tserver_form.ServerSocketAccept(SenderTObject
    SocketTCustomWinSocket);
  begin
    ListBox1.Items.Add(f_socket_key(Socket));
    ListBox1.ItemIndex:= ListBox1.Items.Count- 1;
  end; // ServerSocketAccept

And when the fd_read notification arrives, we use the following tServerSocket.OnRead handler:

procedure Tserver_form.ServerSocketClientRead(SenderTObject;
    SocketTCustomWinSocket);
  const k_buffer_size= 4096;
  var l_textString;
      l_file_nameString;
      l_listbox_indexInteger;
      l_c_file_streamtFileStream;
      l_sizel_start_positionInteger;
      l_sleepInteger;

l_bufferarray[0..k_buffer_size- 1] of byte;
      l_to_sendl_sent_countInteger;
  begin
    l_text:= Socket.ReceiveText;

// -- now locate the file
    l_file_name:= k_source_pathl_text;

// -- find or create the stream
    l_listbox_index:= ListBox1.Items.IndexOf(f_socket_key(Socket));

if l_listbox_index< 0
      then display_bug_halt('  *** no_listbox_ix '+ IntToStr(Socket.SocketHandle))
      else
        if ListBox1.Items.Objects[l_listbox_index]= Nil
          then begin
              l_c_file_stream:= tFileStream.Create(l_file_namefmOpenRead or fmShareDenyNone);
              // -- attach the stream to the tListBox.Objects
              ListBox1.Items.Objects[l_listbox_index]:= l_c_file_stream;
            end
          else begin
              display_bug_stop('  *** no_2_overlapping_read_from_same_socket');
              Exit;
            end;

with l_c_file_stream do
    begin
      // -- send the size back to the client
      if Position= 0
        then begin
            l_size:= Size;
            Socket.SendBuf(l_size, 4);
          end;

// -- send the bytes
      l_sleep:= StrToInt(sleep_edit_.Text);

if l_sleep= 0
        then begin
            // -- send all
            if Socket.SendStream(l_c_file_stream)
              then begin
                  // -- ScktCmp frees the stream, and NIL its local pointer
                  // -- so we also NIL our pointer
                  ListBox1.Items.Objects[l_listbox_index]:= NIL;
                end;
          end
        else begin
            while l_c_file_stream.Positionl_c_file_stream.Size do
            begin
              l_start_position:= l_c_file_stream.Position;

l_to_send:= l_c_file_stream.Read(l_bufferk_buffer_size);
              if l_to_send> 0
                then
                  l_sent_count:= Socket.SendBuf(l_bufferl_to_send);
                  // -- should check status

Application.ProcessMessages;
              SysUtils.Sleep(l_sleep);
            end; // while not end_of_stream
          end; // send by packets with some delay
    end; // with g_c_file_stream
  end; // ServerSocketClientRead

This procedure is unnecessarily complex, because we wanted to use packet transfers with delay in order to display several Clients in action. If we use a zero delay, only SendStream is called. Notice however that Delphi frees the tFileStream when the transfer is over. We personally prefer to let the unit which calls Create also call Free, but this is not the case here.

3.4 - Transfer example

Here is a snapshot of 2 clients downloading files from the server:

In this example

  • we started the Server, with a 100 ms delay to be able to visualize the packet transfers
  • we started the first client
  • while the transfer was still in progress, we started a second client

In this case, since the server uses an infinite loop until all the file is sent, we will observe that:

  • the second request triggers a recursive OnRead call
  • the second transfer request will send all its packets
  • the first transfer will then send the remaining packets

If we wanted to evenly spread the sending between clients, we should

  • either use threads
  • or use some kind of round robin sending, which would be effective as long as there are more than one alive client requests

4 - Download the Sources

You can download the Client and Server projects (not very useful, but anyway...):

Those .ZIP files contain:

  • the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
  • any .TXT for parameters
  • all units (.PAS) for units

Those .ZIP

  • are self-contained: you will not need any other product (unless expressly mentioned).
  • can be used from any folder (the pathes are RELATIVE)
  • will not modify your PC in any way beyond the path where you placed the .ZIP (no registry changes, no path creation etc).

To use the .ZIP:

  • create or select any folder of your choice
  • unzip the downloaded file
  • using Delphi, compile and execute

To remove the .ZIP simply delete the folder.

As usual:

  • please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken links or had some problem downloading the file. Resulting corrections will be helpful for other readers
  • we welcome any comment, criticism, enhancement, other sources or reference suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
  • or more simply, enter your (anonymous or with your e-mail if you want an answer) comments below and clic the "send" button
    Name :
    E-mail :
    Comments * :
     
  • and if you liked this article, talk about this site to your fellow developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate: the more traffic and Google references we get, the more articles we will write.

5 - The author

Felix John COLIBRI works at the Pascal Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly active in the area of custom software developmentDelphi Consulting and Delph training, and is a frequent speaker at Borland Developer Conferences. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, Interbase, Asp.Net, Ado.Net and OOP  /  UML training sessions.

Created: nov-04. Last updated: oct-10 - 61 articles, 121.ZIP sources, 954 figures
Copyright © Felix J. Colibri   http://www.felix-colibri.com 2004 - 2010. All rigths reservedBack:    Home  Papers  Training  Delphi developments  Links  Download

[转] Delphi Socket Architecture的更多相关文章

  1. Delphi Socket Demo

    Delphi Socket Demo 转自  http://www.anqn.com/dev/delphi/2010-01-07/a09122531-1.shtml   自己对中间有点修改,下面是代码 ...

  2. Delphi Socket通信及多线程编程总结

    http://cxhblog.blog.sohu.com/41930676.html 一.Socket通信: Delphi在ScktComp单元中对WinSock进行了封装,该单元提供了TAbstra ...

  3. 初涉Delphi Socket编程

    不是第一次接触socket编程了,但以前都是看别人的依葫芦画瓢,也不知道具体的原理. 新的项目,有了新的开始,同时也需要有新的认识. Delphi 中带有两套TCP Socket组件: Indy So ...

  4. Delphi Socket 阻塞线程下为什么不触发OnRead和OnWrite事件

    //**********************************************************************************//说明: 阻塞线程下为什么不触 ...

  5. Delphi socket() 函数的应用

    socket()系统调用,带有三个参数: 1.参数domain指明通信域,如PF_UNIX(unix域),PF_INET(IPv4), PF_INET6(IPv6)等 2.type指明通信类型,最常用 ...

  6. delphi socket 编程 使用多线程

    http://blog.csdn.net/lailai186/article/details/8788710?utm_source=tuicool TClientSocket和TServerSocke ...

  7. Delphi Socket的最好项目——FastMsg IM(还有一些IM控件),RTC,RO,Sparkle等等,FileZilla Client/Server,wireshark,NSClient

    https://www.nsclient.org/nsclient/ 好好学习,天天向上

  8. DELPHI下的SOCK编程

     DELPHI下的SOCK编程(转自http://www.cnblogs.com/devcjq/articles/2325600.html) 本文是写给公司新来的程序员的,算是一点培训的教材.本文不会 ...

  9. Android InputStream转Bitmap

    android socket服务端 接收Delphi socket客户端发来的图片,保存到bitmap中,代码如下: public static Bitmap readInputStreamToBit ...

随机推荐

  1. html5--移动端视频video的android兼容,去除播放控件、全屏等

    html5 中的video 在手机浏览器中的总结所有页面播放时, 如果选择全屏播放, 播放画面将浮动到屏幕的最上层 IOS 手机   自动播放 播放界面浮动文字 播放时是否自动全屏 能否嵌入在页面中播 ...

  2. Java精选笔记_集合概述(Collection接口、Collections工具类、Arrays工具类)

    集合概述 集合有时又称为容器,简单地说,它是一个对象,能将具有相同性质的多个元素汇聚成一个整体.集合被用于存储.获取.操纵和传输聚合的数据. 使用集合的技巧 看到Array就是数组结构,有角标,查询速 ...

  3. NGUI在5.3打包失败问题

    一.NGUI版本 NGUI是很好用的Unity UI插件. 当前使用版本NGUI Next-Gen UI v3.9.7 (Feb 10, 2016)和NGUI Next-Gen UI 3.9.0两个版 ...

  4. laravel框架容器管理的一些要点

    本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...

  5. Runtime应用(二)使用对象关联为分类增加属性(每个对象的属性互不干扰)

    一.对象的关联方法有 1. void objc_setAssociatedObject(id object, const void *key, id value,objc_AssociationPol ...

  6. 【转载】为ASP.NET MVC及WebApi添加路由优先级

    路由方面的: 转载地址:http://www.jb51.net/article/73417.htm Author:lijiao 这是一个对Asp.Net Mvc的一个很小的功能拓展,小项目可能不太需要 ...

  7. 如何偷懒地用 PHP 搭建一个班级网站

    版权声明:本文由李宜东原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/116 来源:腾云阁 https://www.qclo ...

  8. 学习坤哥的replaceTpl方法

    学习坤哥的方法之后自己写的replaceTpl function replaceTpl(tpl, data){///////////////没有传入可让用户自己定义的方式进行替换,不够灵活       ...

  9. hihocoder [Offer收割]编程练习赛14 投掷硬币

    题目2 : 投掷硬币 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi有一枚神奇的硬币.已知第i次投掷这枚硬币时,正面向上的概率是Pi. 现在小Hi想知道如果总共投 ...

  10. windows本地环境如何用wamp配置多域名绑定访问

    https://jingyan.baidu.com/article/acf728fd5fcdadf8e510a3e5.html