delphi TTcpClient TTcpServer分析

http://blog.csdn.net/andrew57/article/details/8767308

只描述windows socket部分。

sockets.pas中各个类得继承关系:

TBaseSocket

|

------------------------

|

TIpSocket

|

--------------------------------------------------------------------------------------

|                                                   |                                |

TCustomIPClient              TRawSocket            TCustomTcpServer

|                                                                                    |

-------------------------------------                                  --------------------

|                               |                                                    |

TTcpClient       TUdpSocket                                 TTcpServer

TBaseSocket:

这个是socket基类,此类中封装了winsocket的基本操作,如建立什么类型的socket,关闭socket,数据的发送、接收、检测是否可以读写及异常。并提供了几个控制反转调用。具有socket使用的地址族、协议、类型、socket句柄等成员变量。

ErrorCheck socket函数执行结果检测。

DoHandleError socket函数执行失败后的错误处理。

Open 建立一个socket用户对象(windows内部处理)。

Close关闭建立的socket。

PeekBuf探测接收缓冲区数据长度。

SendBuf写入发送缓冲区。

ReceiveBuf从接收缓冲区读数据。

SendStream发送一个流对象中的数据。

WaitForData判断接收缓冲区是否有数据可读(对select调用)。

TIpSocket

这个类在TBaseSocket基础上增加了绑定地址、接收指定指定地址数据,发送数据到指定地址及主机名和地址转换等方法, 增加了本地地址、端口,远程地址、端口成员变量。

默认建立的是af_inet地址族下的流式socket。

Bind绑定本地地址和端口到socket句柄。

ReceiveFrom接收指定地址的数据,一般udp使用。

SendTo发送数据到指定的地址端口,一般udp使用。

到目前为止此类可以建立一个基本流式socket,但他没有连接,断开,监听等操作。

TCustomIpClient

客户端socket类,顾名思义,此类是作为socket客户端使用的(也许发起连接的就可以称为客户端吧!,连接后两边其实就是对等的了),所以此类增加了Connect, Disconnet

方法,同时改写了TBaseSocket的Open,Close方法。并提供了连接及断开的控制反转。

Open建立socket并连接到远程地址端口。

Close关闭socket的收发同时关闭socket(closesocket)。

Connect对Open的调用。

Disconnect对Close的调用。

GetThreadObject此方法在客户端无用,返回nil。由于服务端等会为每个请求的客户端建立TCustomIpClient来服务,同时每个连接成功的客户端用一条线程专门服务,这个函数便是取那个线程对象用的。线程的引用地址会保存在thread-local变量 ThreadObject中。

TRawSocket

继承自TIpSocket,默认建立的是支持比较底层协议的socket,未尝试不做解释。

TUdpSocket

继承自TIpSocket,建立一个支持udp协议的socket客户端,主要是改变了建立socket的类型和协议,可以参考windows socket()函数建立不同类型的socket参数说明。

TTcpClient

继承自TCustomIpClient无自己的成员函数和变量。

TClientSocketThread

此线程类主要为服务端使用,当一个连接请求到来时server会取一个此线程类得对象来处理socket的连接,

此线程类得对象主要是生成一个TCustomIpClient来接收sockets accept的返回参数。但注意到execute中在调用了服务端的Accept()后立即释放了TCustomIPClient,这样意味着立即和客户端断开了,是否无法做数据处理,只是从连接队列中取出一个连接socket而已!!!我们在接下来的 TCustomTcpServer.Accept(var clientSocket: TCustomIpClient)中发现socket函数accept成功后会有DoAccept调用,那么在TTcpServer.OnAccept中加死循环处理通讯知道断开是一种方法,如下:

procedure DoAccept(Sender: TObject; ClientSocket: TCustomIpClient);
begin
    // 到这里时客户端建的连接已经被成功处理,ClientSocket便是和对方通讯的Socket的封装
    while not ClientSocket.GetThreadObject.Finished and ClientSocket.Connected do  // GetThreadObject就是那个获取那个为客户端服务的线程,上面已介绍
   begin
       // bRead, bExcept: Boolean
       if ClientSocket.Select(@bRead, nil, @bExcept, 100) then  // 判断此socket是否可读且没有异常,在客户端断开时也返回true同时读到得数据长度0,证明已经断开
       begin
          if bRead and not bExcept then
          begin
              if ClientSocket.ReceiveBuf(buf, 512) = 0 then  // 已经断开 buff: array[0..511] of byte
                 ClientSocket.Disconnect   // 断开本地socket改变Connected值
              else begin
                 // 处理接收的数据
              end;
          end;
       end;
   end;
   //  到这里已经断开或者线程中发生异常退出
end;

ExecuteSyncProc同步到主线程中执行方法(让主线程调用)。

ClientSocket此线程所建立的TCustomIPClient。

ServerSocketThread建立此线程的服务端线程。

TServerSocketThread

此线程是服务端用于来处理socket请求的线程,他内部有TClientSocketThread缓冲池,默认缓冲10个线程对象,以下介绍他几个方法。

AddClientSocketThread增加一个客户端socket处理线程并加入线程池,如果线程池中缓冲已满那就返回nil,意味着这个socket连接请求就没办法处理 。

这个处理线程可以是TClientSocketThread以及他的派生类对象,如果你没有做派生那默认是TClientSocketThread。

CreateThread建立一个TClientSocketThread线程类对象。

FetchClientSocketThread从缓冲池获取一个已经挂起的对象。

RemoveClientSocketThread这个是供TClientSocketThread调用的,他在释放的时候需要处理。

ClearThreadPool释放缓冲池中对象。

Execute主要是等待客户端的连接,当有连接来时取一条线程来服务客户端,如果缓冲池已满此连接请求将无法处理。

OnGetThread此属性可以调用到派生的TClientSocketThread类对象。

TCustomTcpServer

socket服务端,主要是负责处理客户端的请求,继承自TIpSocket,此服务端类默认是线程阻塞模式(其实socket就只有阻塞于非阻塞,这里的线程阻塞是每个线程服务一个客户端,建立的通讯socket是阻塞的,这样代码好写),如果是阻塞或者非阻塞方式,那自己要处理的事多些,当然自由度应该大些。以下介绍此类得几个方法:

GetThread 获取一个TClientSocketThread类对象,如果是线程阻塞模式此函数供TServerSocketThread调用。

DoAccept 处理请求成功后的控制反转。

Listen 在socket上启动监听。

Open 改写父类方法,调用TBaseSocket.Open建立socket并绑定本地地址端口,启动监听,唤醒服务线程。

Close 改写父类方法,终止并释放服务线程TServerSocketThread,关闭监听socket。

Accept建立TCustomIpClient接收连接请求。

Accept如上面的Accept,成功后改变TCustomIpClient对象的属性,并调用DoAccept。

WaitForConnection 等待客户端连接,有连接才返回,否则阻塞导致执行线程挂起。

TTcpServer

继承自TCustomTcpServer,无自己的方法。

以上时sockets.pas各个类得介绍。

线程阻塞模式下服务启动后,TServerSocketThread会调用WaitForConnection等待连接请求,有连接请求时会被唤醒,然后获取一个线程TclientSocketThread(可能生成)来处理这条连接,当缓冲池已满且获取不到空闲挂起的线程时此连接请求将无法处理,会一直存在连接队列,无法处理,所以使用TClientSocketThread服务时可以将ThreadCacheSize设置大些。而 TClientSocketThread从连接队列获取到socket后会立即关闭,所以必须在DoAccept方法中处理此socket直到关闭,在 TCustomTcpServer.OnAccept有公布。上面有具体代码说明。

另一种方法是不用TServerSocketThread中建立的TClientSocketThread线程来处理数据读写,他们只处理连接请求,而且也不需要10条。这样就必须从TClientSocketThread

派生从一个类来改写TClientSocketThread的一些逻辑(TClientSocketThread.Execute如果不在 OnAccept中死循环socket将会被立即释放),同时在TCustomTcpServer.OnGetThread中提供派生类得对象供 TServerSocketThread使用。

TMyClientSocketThread = class(TClientSocketThread)
protected
    procedure SyncProc; override;  // 要同步到主线程的代码
    procedure Execute; override;
end;

Execute如下:
var
  Client: TCustomIpClient;
begin
  // inherited;
  Client := TCustomIpClient.Create(nil);
  if Assigned(Client) then
  begin
   if ServerSocketThread.ServerSocket.Accept(Client) then   // 为Client赋值
    begin
        // 对建立成功的Client处理,比如加入TList,然后由其他线程处理,这里应该快速做完,这样这个线程就可以等待继续被调度,否则其他连接长时间无线程处理
        // 依然在连接队列中
    end else begin  // 失败就释放此socket
      Client.Free;
    end;
    if not Terminated then  // 接收客户端socket成功,挂起线程
      Suspend;
  end;
end;

  兴致来所写,如果错误还望指正,可以随意转载。

时间: 2024-11-23 06:13:33

delphi TTcpClient TTcpServer分析的相关文章

delphi程序病毒分析

文件: C__Users_SETH_AppData_Local_Temp_Scanned Copy[1].exe大小: 776704 bytes修改时间: 2018年1月29日, 13:37:20MD5: 9B2130F13784B915088BB83E272B8C4DSHA1: A3284EE13269B7B051DAFA46EABFCD3281951651CRC32: 19F7C8F0文件基本信息:区段信息: 对于delphi程序使用DeDeDark或DelphiDecompiler看看 参

delphi 内存泄露 分析

由于类的属性又可以是类,那么类在初始化的时候,属性类的初始化怎么办,我猜想是 重写 类的 create和destroy方法,在create和destroy中做好属性类的初始化和释放工作 结果今天遇到一个内存泄露,原因是我忘记释放TAqApi了,所以造成上面这么多的内存泄露,原因是TAqApi包含了很多类属性,那么如果它没有释放,那么它包含的属性类也就 没有释放,所以才造成了这么多的内存泄露. 通过上面的例子 可以看出,如果出现了内存泄露,就看最下面的那么  就知道是 哪个类的对象 没有释放了.

delphi-TTcpServer与TTcpClient

最简单的TTcpServer与TTcpClient通信实例-Delphi_海盗船长_新浪博客http://blog.sina.com.cn/s/blog_5383794d0100nt9u.html delphi TTcpClient TTcpServer分析 - 沧海一粟 - 博客频道 - CSDN.NEThttp://blog.csdn.net/andrew57/article/details/8767308 用TTcpClient和TTcpServer进行文件的传输 - 好记性不如烂笔头 -

吐血整理 Delphi系列书籍 118本(全)

Delphi 教程 系列书籍 网友(老帅)整理 001_<Delhpi6数据库设计思想与实践> 002_<Delphi6应用开发指南> 003_<Delphi6开发人员指南> 004_<Delphi6数据库开发典型实例> 005_<Delphi6组件大全> 006_<深入Delphi6 网络编程> 007_<Delphi6数据库深入编程技术> 008_<Delphi6程序设计教程> 009_<Delphi

Delphi之萝莉调教篇

本文纯属技术交流.如果各位看官想与小生一起探讨萝莉的问题的话...PM我吧 关于Delphi的萝莉调教技术,很久以前就有大牛做过了...其实技术早掌握了只是觉得太无聊~估计大家也都会于是就没有写~既然群里有人提出~就留下一份记录以前我很傻很天真.主要原因是也因为很懒.正值新春之际全当写出来给各位献礼了.给大家拜个晚年由于本文作者水平问题,有说的不对或者不明确的地方请大家海涵.菜鸟之作高手跳过... Q:为啥不用Delphi?A:体积太大Q:为啥用Delphi?A:很方便 体积问题一直都是Delp

最简单的TTcpServer与TTcpClient通信实例-Delphi

unit TcpSCDemo;//最简单的TTcpServer与TTcpClient通信实例-Delphi //Borland推出TTcpServer与TTcpClient作为主要的网络通信控件,意味着这两个控件有很大的优越性,//但是有关这两个控件的资料非常少见,很多人都在问一个相同的问题:TcpServer怎么样向TcpClient发送数据?//网上的回答千奇百怪,归纳起来有三种://1.参考delphi自带的netchat.例子中TcpServer只收不发,答题得分0.//2.用indy或

用TTcpClient和TTcpServer进行文件的传输

发送数据时有Sendln,SendBuf,SendStream.接收数据时有Receiveln,ReceiveBuf,当时我很奇怪为什么没有ReceiveStream.因为很自然的想到是对应关系的.但当时我不知道,发数据时是一小段一小段地发的 看看SendStream代码: [delphi] view plain copy function TBaseSocket.SendStream(AStream: TStream): Integer; var BufLen: Integer; Buffer

Delphi中Android运行和JNI交互分析

Androidapi.JNIBridge负责和JNI交互.,既然要交互,那么首先就是需要获得JNI的运行环境,Android本身内置的就有一个Java(Dalvik)虚拟机.所以这个第一步就肯定是要这个虚拟机和我们当前的运行线程环境关联.这时候Androidapi.JNIBridge中的TJNIResolver就出场了.GetJNIEnv这个函数就是, [delphi] view plaincopy class function TJNIResolver.GetJNIEnv: PJNIEnv;

关于Delphi中的字符串的详细分析

关于Delphi中的字符串的详细分析 只是浅浅的解析下,让大家可以快速的理解字符串. 其中的所有代码均在Delphi7下测试通过. Delphi 4,5,6,7中有字符串类型包括了: 短字符串(Short String) 长字符串(Long String) 宽字符串(Wide String) 零结尾字符串(Null-Terminated String).PChar和字符数组 1.短字符串(Short String) 固 定长度,最大字符数个数为255,短字符串也成为长度字节(Length-byt