DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: 123glimmer
今日帖子: 19
在线用户: 16
导航: 论坛 -> 数据库专区 斑竹:liumazi,waterstone  
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/15 10:21:46
标题:
求救!不能在IdTCPServerExecute事件中在ado数据库中写数据 浏览:816
加入我的收藏
楼主: 我用ADOQuery.SQL.Add('insert into UserInfomation1(…)') , 能在IdTCPServerConnect事件中在ado数据库中写数据,也能通过Button1Click事件在ado数据库中写数据,但用同样的语句在IdTCPServerExecute事件中在ado数据库中写数据,系统也不报错,但我检查ado数据库表,数据没被写上,我在IdTCPServerExecute事件中调用Button1Click事件,数据同样没被写上(我单击Button1数据可以被写上)。不做什么原因,望高手指教,万分感谢!
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲△△ -
注册会员
2020/10/15 10:51:08
1楼: see if this article help you.

https://stackoverflow.com/questions/16583601/idtcpserverexecute-runs-but-does-not-receive-data

Your server code is calling IOHandler.ReadLn(), which expects a (CR)LF after the text. Your client is calling IOHandler.Write(), which does not send a (CR)LF. The client needs to call IOHandler.WriteLn() instead.

thanks to REMY LEBEAU - TeamB and Indy provider!

https://silverpeacock.wordpress.com/2017/07/30/delphi-client-server-indy-embarcadero-tcp-tiditcpserver-tidtcpclient-components/
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/15 12:03:41
2楼: Thank you very much!但我的问题不是IdTCP服务器与客户机的通讯问题,我的问题是IdTCPServerExecute事件中成功接收到客户机发来的信息后,不能存入ADO数据表。
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲△△ -
注册会员
2020/10/15 12:09:12
3楼: did you verifyed the "TRANSACTION" is activated and commited?
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2020/10/15 16:45:14
4楼: IdTCPServerExecute事件,这个是线程在执行。你试试同步到主线程看看。

ADO 本身是基于 COM 的,如果线程调用它,需要先执行 CoInitialize。

具体我也不确定。最好的测试方法是同步到主线程。也就是把你的操作数据库的代码,用同步到主线程的那个函数包起来。如果测试通过,则说明就是线程调用 ADO 导致的问题。
----------------------------------------------
-
作者:
男 dmzn (dmzn) ★☆☆☆☆ -
盒子活跃会员
2020/10/15 18:55:49
5楼: 4楼的答复就是问题所在.
在IdTCPServerExecute事件中调用ADO组件,要注意线程同步问题.最重要的是: ADOConnection组件不能共用一个,否则会死锁.

解决方法是:
1.创建一个ADOConnection的连接池.
2.每次在IdTCPServerExecute事件中写数据库时,从连接池取一个连接对象.
3.写入完毕后,释放连接对象

可参考附件中的连接池,方法: 
1.获取连接: TDBConnManager.GetConnection
2.释放连接: TDBConnManager.ReleaseConnection
此帖子包含附件:dmzn_20201015185549.txt 大小:49.1K
----------------------------------------------
生活愉快.
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/15 23:46:37
6楼:  非常感谢4楼和5楼的指点!我将按你们的指点试一试。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2020/10/15 23:59:31
7楼: 5 楼提到的连接池非常好。

IdTCPServerExecute 这个事件是被 IdTCPServer 的线程调用的。在 INDY9 上面你可以看作每个连接一个线程;Indy10 不清楚具体的细节,但肯定是多个线程在并发跑。因此在 IdTCPServerExecute 里面写的代码,你看到是一个函数,但这个函数可能同一时间有多个调用同时发生。因此,所有用到的东西,其实例不能是某一个全局的,如果必须是,则需要加上临界区,避免多个线程同时访问同一个实例。

具体到 ADOCONNECTION,那就是把它定义为局部变量,每次用的时候创建实例。当然这样做比较没效率。要想有效率,就是连接池。
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/18 6:30:34
8楼: 谢谢4楼、5楼、7楼3位高手的指点!尤其感谢5楼给我发了一份关于连接池的附件,但由于本人属于菜鸟级,看了后对连接池的理解还不是很清楚,如何运用到我的程序还有困难,但我用了一个非正规的方法把问题解决了,前面提到,我在IdTCPServerExecute事件中调用Button1Click事件,仍然不能在数据库表中写数据,这是因为Button1Click事件代码仍然在IdTCPServerExecute事件中运行。我尝试将数据库表处理代码写到一个定时器时间到事件中,在IdTCPServerExecute事件中设定时器enabled属性等于true,启动定时器程序,能成功将数据写于数据库,这种方法有两个不大的问题,一是浪费一毫秒的服务器机时(定时器最小定时事件为一毫秒,如果客户端特别多,请求也特别多,这一毫秒也很宝贵),二是要将有关局部变量改为全局变量。我想请教几位高手,这种不正规的方法除了我说的两个问题外,还有没有其它问题。我将继续学习、理解连接池,完全理解后,再将程序改过来。再次感谢3位高手!
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2020/10/18 18:15:17
9楼: 你的问题要解决,可以不用连接池。连接池只是提高效率而已。

但你用 Timer 来处理是有问题的。

你的问题的正规做法就是在你调用数据库的代码,用同步的方法包起来。原理是让主线程去执行。你使用 Timer 的事件去执行,也是让主线程去执行。但 Timer 的问题是如果有多个连接的并发来了,可能会出事。
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/18 23:41:43
10楼: 谢谢!我将按你的指教慢慢试。
----------------------------------------------
-
作者:
男 dmzn (dmzn) ★☆☆☆☆ -
盒子活跃会员
2020/10/19 13:50:27
11楼: procedure TForm1.TCPServer1Execute(AContext: TIdContext);
var nStr: string;
    nQuery: TADOQuery;
    //nConn: TADOConnection;
    //如果需要事务,可创建conn对象
begin
  nStr := AContext.Connection.Socket.ReadLn();
  if nStr <> 'WriteDB' then Exit;

  CoInitialize(nil);
  //初始化ADO-COM对象

  nQuery := nil;
  try
    nQuery := TADOQuery.Create(nil);
    with nQuery do
    begin
      Close();
      ConnectionString := Trim(Edit1.Text);
      
      SQL.Text := 'insert into B_Books(B_ID) Values(''111'')';
      ExecSQL;
    end;
  finally
    nQuery.Free;
    CoUnInitialize;
    //释放COM对象
  end;
end;
此帖子包含附件:dmzn_20201019135027.rar 大小:4.2K
----------------------------------------------
生活愉快.
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2020/10/19 17:14:47
12楼: 楼上的办法是用 CoInitialize(nil); 对 COM 进行线程同步,是正确的做法。
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/20 12:19:40
13楼:  谢谢11楼、12楼两位高手的指教,万分感谢11楼高手给我提供源代码!!!
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/21 8:58:18
14楼: 万分感谢高手dmzn给我提供源代码,我编了个一个数据表、一个字节的ado数据库,按您提供的程序框架试了试,由于本人太菜,不成功,显示“SQL Server不存在或拒绝访问”,程序见附件,麻烦您帮我看一看,帮我找出问题,太麻烦您了!
此帖子包含附件:fuhoutun_2020102185757.rar 大小:778.1K
----------------------------------------------
-
作者:
男 dmzn (dmzn) ★☆☆☆☆ -
盒子活跃会员
2020/10/21 12:35:51
15楼: ADO组件有个很重要的属性ConnectionString,用来描述: 使用什么驱动和参数,连接哪些数据库.

11楼Demo里连接的是SQL Server数据库,所以ConnectionString为:
Provider=SQLOLEDB.1;Password=sa;User ID=sa;Initial Catalog=ZXLibrary;Data Source=localhost

楼主要连接的是Access数据库,ConnectionString大致为:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Temp\fuhoutun_2020102185757\新建文件夹\test.mdb;Mode=Share Deny None;Jet OLEDB:SFP=False

具体可以在窗口放一个ADOConnection组件,双击打开ConnectionString编辑器.
----------------------------------------------
生活愉快.
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/21 13:25:37
16楼: 非常感谢!我将按你的指导试试。
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/27 12:44:24
17楼: 万分感谢高手dmzn给我提供源代码和不厌其烦地指导!我的程序原来是用ini文件处理客户机发来的数据,但遇到ini文件没有自动排序功能的问题,编排序程序虽然简单,但开发系统不让排序数组太大,太大的数组排序也很耗机时,改用ADO又遇到我前面提到的问题,这几天我按dmzn提供的框架,成功将ini数据处理改换称为了ado,再次感谢高手dmzn和其他多位高手的指导!
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/28 13:04:45
18楼: 改ado后,刚才出现新问题,原来用ini文件时 我的IdTCPServerr程序,连接几十个客户端没问题,改用ado后,当客户端数还不到20时,新的客户端试图登录时,新的客户端显示“connection closed gracefully”,请高手们帮我分析一下可能是什么原因,万分感谢!
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2020/10/28 17:08:03
19楼: 你必须要知道 TIdTCPServer.OnExecute 是一个多线程。你只是在 Execute 里面写了一段代码,但实际上那段代码,可能是多个线程在同时并发执行。简单点考虑,你可以认为有一个客户端连接就有一个线程。

首先,如果你在服务器端,把收到的数据,写入 INI 文件,就有隐患。多个线程并发写入同一个 ini 文件,我不知道你的代码是怎么写的,如果没加上临界区或者同步到主线程,你写入的数据,可能会有问题。简单说,多线程同时去写同一个对象(内存、文件,等等),如果不加上临界区,是可能出各种奇怪问题的,比如丢数据。

其次,如果你在 OnExecute 里面,调用 ADO 将数据写入数据库,我不知道你的代码是怎么写的,但有两种可能:
1. 你在里面使用了一个全局的 ADOCONNECTION 对象。如果是这样,相当于多个线程并发访问同一个对象;

2. 在 OnExecute 里面,你使用在该函数里面创建的 AdoConnection 对象,那么,当多线程并发调用 OnExecute 的时候,其实是同时创建了多个 AdoConnection 对象,每个线程使用自己创建的对象,那么,不存在多线程同时写一个对象的问题。
2.1. 上述每个线程临时创建 AdoConnection 对象的方法,可以改为使用连接对象缓冲池。最终原理一样,都是每个线程使用一个单独的连接对象。

假设你的代码是正确的,每个线程使用一个单独的连接对象,那么,当有20个客户端连上来,你的程序就创建了20个 AdoConnection 对象实例,你的程序对数据库的连接就有 20 条连接。

我不知道你后台数据库采用什么数据库。如果是这种情况,你需要设置 ADO 或者设置后台数据库,扩展其最大连接数。

如果你使用了现成的数据库连接池的代码,这种连接池通常会有一个最大连接数的限制,你可以去看看是否有和这个设置有关的代码。
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/10/29 6:43:16
20楼: 谢谢高手pcplayer的指导。附件是IdTCPServerConnect事件登录部分的代码,请各位高手帮助分析,万分感谢!
此帖子包含附件:fuhoutun_2020102964316.txt 大小:3,896B
----------------------------------------------
-
作者:
男 fuhoutun (fuhoutun) ▲▲▲▲▲ -
注册会员
2020/11/7 5:43:43
21楼: 非常感谢各位高手的耐心指教,我按你们的指教将代码进行了一些优化,情况有所改善,但连接超过66个客户后仍然出错,后尝试将IdTCPServer的MaxConnection属性从默认值0改成100,连接100个客户没问题,我又将其改成200,连接200个客户也没问题。一般认为默认值0是不限制客户数,但为什么会出现以上情况,没法解释。我的两个问题都解决了,再次感谢各位高手,尤其是 dmzn和pcplayer 还给我提供源代码和不厌其烦地指导!
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行414.0625毫秒 RSS