delphi TTcpClient TTcpServer分析(转)
delphi TTcpClient TTcpServer分析(转)
只描述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;
delphi TTcpClient TTcpServer分析(转)的更多相关文章
- delphi 内存泄露 分析
- delphi-TTcpServer与TTcpClient
最简单的TTcpServer与TTcpClient通信实例-Delphi_海盗船长_新浪博客http://blog.sina.com.cn/s/blog_5383794d0100nt9u.html d ...
- 吐血整理 Delphi系列书籍 118本(全)
Delphi 教程 系列书籍 网友(老帅)整理 001_<Delhpi6数据库设计思想与实践> 002_<Delphi6应用开发指南> 003_<Delphi6开发人员指 ...
- Delphi之萝莉调教篇
本文纯属技术交流.如果各位看官想与小生一起探讨萝莉的问题的话...PM我吧 关于Delphi的萝莉调教技术,很久以前就有大牛做过了...其实技术早掌握了只是觉得太无聊~估计大家也都会于是就没有写~既然 ...
- delphi 文件夹操作
文件的拖放和打开拖拽 user shellapi type TForm1 = class(TForm) ListView1: TListView; procedure FormCreate(Sende ...
- Delphi 文件操作(路径、目录)
Delphi利用系统环境变量获取常用系统目录 //譬如 %WINDIR% 是表示系统目录的系统变量, 可以这样获取: var s: string; begin s := GetEnvironmentV ...
- 最简单的TTcpServer与TTcpClient通信实例-Delphi
unit TcpSCDemo;//最简单的TTcpServer与TTcpClient通信实例-Delphi //Borland推出TTcpServer与TTcpClient作为主要的网络通信控件,意味 ...
- 教程-Delphi资源文件(全面分析于使用)
Delphi资源文件(全面分析之位图.光标.图标.AVI.JPEG.Wave) 几乎每个Windows应用程序都使用图标.图片.光标等资源.资源是程序的一部分,但是它是不可执行代码.下面我们就详细 ...
- Delphi中Android运行和JNI交互分析
Androidapi.JNIBridge负责和JNI交互.,既然要交互,那么首先就是需要获得JNI的运行环境,Android本身内置的就有一个Java(Dalvik)虚拟机.所以这个第一步就肯定是要这 ...
随机推荐
- cytoscape.js 教程
因为数据要展示双向关系,最终选用了cytoscape.js. 效果如图,echarts只能显示单项数据关系. 据我理解,这个插件分两种,一种是基于jquery的,另一种是原声的. 基于jquery是加 ...
- MySQL数据库基本操作以及SQL语句
连接mysql的语法 mysql -u用户名 -p密码 [-h主机名] [-P端口号] 在一个mysql服务器中, 可以有多个mysql数据库(本质是一个文件夹) 在一个mysql数据库中, 可以有多 ...
- 【leetcode】909. Snakes and Ladders
题目如下: 解题思路:天坑题,不在于题目多难,而是要理解题意.题目中有两点要特别注意,一是“You choose a destination square S with number x+1, x+2 ...
- 初识localstorage用法
最近在做一个类似填报信息的页面,一共有8页,当点击切换到下一页的时候要求把上一页的数据存到本地,以便下次切换到这个页面的时候自动把值填上去,并且在最后一页提交数据的时候直接用localstorage里 ...
- python--生成器、三元表达式、列表解析、生成器表达式
补充: 在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作 func_dic={} def make_dic(key): def deco(func): func_dic[ ...
- CJE-Jenkins认证工程师备考指南1-考试简介
CloudBees公司提供两项认证 Jenkins工程师(CJE)考试 包括60个选择题 测试开源Jenkins的知识. CloudBees 平台工程师(CCJE)考试 包含90个问题: 60个问题测 ...
- Centos7安装 Hadoop(单节点)
1.Hadoop简介 Hadoop是一个由Apache基金会所开发的开源分布式系统基础框架,使用Java开发,是处理大规模数据的软件平台. Hadoop可以从单一节点扩展到上千节点.用户可以在不了解分 ...
- 07、python的基础-->数据类型、集合、深浅copy
一.数据类型 1.列表 lis = [11, 22, 33, 44, 55] for i in range(len(lis)): print(i) # i = 0 i = 1 i = 2 del li ...
- Javascript优点和缺点
优点: 函数是顶级对象 基于原型继承的动态对象 对象字面量和数组字面量 缺点: 全局变量 作用域 自动插入分号 保留字 Unicode typeof parseInt + 浮点数 NaN
- 数据持久化之Android文件系统(一)
阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680 一.前言 文件系统一直是Android开发过程中经常接触的东西.而关于内 ...