主頁 > 後端開發 > delphi xe10.1 用idhttpserver作了個web服務程式,有記憶體泄露,請移步入內

delphi xe10.1 用idhttpserver作了個web服務程式,有記憶體泄露,請移步入內

2020-09-11 23:08:14 後端開發

IdHTTPServer1CommandGet事件中寫的代碼
原本想寫個執行緒類,每次請求就生成一個執行緒單獨處理,結果網上說這個事件好像是'執行緒方法',就是在獨立的子執行緒中執行的
最后執行緒寫好了,但是不知道怎么回發資訊,貌似AResponseInfo傳入執行緒就不行了,沒有辦法
只有把執行緒的代碼移植到當前事件中,結果悲劇了,運行了一天,記憶體暴漲,從開始的20多m,漲到300多m,
沒有辦法,我有把代碼反反復復檢查了幾次,該釋放的釋放,結果還是不行,沒有辦法,特來請求各位大神


1:如果在自己寫的執行緒中,怎么回傳資訊,AResponseInfo傳入執行緒就不行了?


2:CommandGet事件中怎么釋放變數,才不會記憶體泄露,我都是freeandnil(變數),按理說不應該啊,求指點?
部分代碼:
procedure Tfrmmain.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var   mymemory:TMemoryStream;
        buffer,jpgbuffer: Tbytes;
        Jpg:TjpegImage;
        Image1: TImage;
        user_info:string;
begin
       業務邏輯...
       //釋放變數
      if assigned(mymemory) then
         freeandnil(mymemory);

     if assigned(jpg) then
        freeandnil(jpg);

     if assigned(image1) then
        freeandnil(image1);

      setlength(buffer,0);
      setlength(jpgbuffer,0);
end;

實際上就是接收客戶端的人臉圖片,我的后臺服務做人臉比對(百度人臉),然后回傳結果

uj5u.com熱心網友回復:

你的變數如果不是自己創建的,就不要去釋放。

uj5u.com熱心網友回復:

打開你的專案的dpr檔案(project->view source),然后在
Application.Initialize; 
這行之前加一句
ReportMemoryLeaksOnShutdown := true;
或者 
ReportMemoryLeaksOnShutdown := DebugHook <> 0; 
然后,save all、build,運行你的程式,結束之后會列出程式中出現記憶體泄露的地方

uj5u.com熱心網友回復:

回復tangth釋放的變數都是我自己創建的

uj5u.com熱心網友回復:

我用的fastmm4,現在想問問第一個問題:
1:如果在自己寫的執行緒中,怎么回傳資訊,AResponseInfo傳入執行緒就不行了?

uj5u.com熱心網友回復:

我在CommandGet事件中修改為執行緒
procedure Tfrmmain.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var  cloud_gate_thread:Tcloud_gate_thread;
begin
        cloud_gate_thread:=Tcloud_gate_thread.create(ARequestInfo; AResponseInfo);
       cloud_gate_thread.FreeOnTerminate:=true;
        cloud_gate_thread.Start;
end;
//資料能收到,但是用AResponseInfo回發訊息就不行了,
//我跟蹤代碼后發現AResponseInfo=nil,好像作為參賽傳入執行緒就失效了樣?

uj5u.com熱心網友回復:

你應該
AResponseInfo.ContentText := ...

uj5u.com熱心網友回復:

參考 5 樓 aidpoint20126 的回復:
我在CommandGet事件中修改為執行緒
procedure Tfrmmain.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var  cloud_gate_thread:Tcloud_gate_thread;
begin
        cloud_gate_thread:=Tcloud_gate_thread.create(ARequestInfo; AResponseInfo);
       cloud_gate_thread.FreeOnTerminate:=true;
        cloud_gate_thread.Start;
end;
//資料能收到,但是用AResponseInfo回發訊息就不行了,
//我跟蹤代碼后發現AResponseInfo=nil,好像作為參賽傳入執行緒就失效了樣?


這樣傳到執行緒里不行的,你把他轉為你需要的資料后再傳到執行緒里去。
如:你可執行緒里增加變數,把你需要的資料先寫到執行緒的變數里。(這個變數如果需要創建,就在執行緒里創建、執行緒里釋放)

uj5u.com熱心網友回復:

AResponseInfo不要單獨去用執行緒寫。因為,CommandGet事件執行完成后就會回傳資訊,而這時你的執行緒還在運行,AResponseInfo自然就為nil了。

uj5u.com熱心網友回復:

多謝tanqth一直的關注和回復,謝謝
我現在就是不知道到在執行緒里面怎么回發訊息?
以前我用serversocket的時候,都有個客戶端連接,直接傳入到執行緒里面,
處理完了用這個連接回發就可以了
但是現在用idhttpserver,后執行緒里面怎么回發訊息呢?AContext嗎?

uj5u.com熱心網友回復:

參考 10 樓 aidpoint20126 的回復:
多謝tanqth一直的關注和回復,謝謝
我現在就是不知道到在執行緒里面怎么回發訊息?
以前我用serversocket的時候,都有個客戶端連接,直接傳入到執行緒里面,
處理完了用這個連接回發就可以了
但是現在用idhttpserver,后執行緒里面怎么回發訊息呢?AContext嗎?


因為,CommandGet事件執行完成后就會回傳資訊,他本身也是執行緒處理。就不要單獨去用執行緒寫。

所以,你現在就是把你目前執行緒里的代碼移到CommandGet事件里就OK。洗掉你的執行緒,多余且有問題。

uj5u.com熱心網友回復:

我現在就是把執行緒的代碼移植到CommandGet里面了,但是有記憶體泄露,又回到原來的問題了,
快被折磨瘋了
好吧,我把代碼全部貼上來,拜托幫我分析一下,看是哪里出現記憶體泄露了

procedure Tfrmmain.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var issucc:boolean;
    len,inum: integer;
    isdetail_log,isfind:boolean;
    acc_type,recstr,enterprise_no,ssql,door,
    face_door,twocode_door,user_info,serial,
    cardno,week_num,image_base64,user_list,
    errmsg,file_path,res_enterprise_no,
    res_cardno,score:string;
    user_info_array:Tstringdynarray;
    data_array:Tstringdynarray;
    //需要釋放的變數
    buffer,jpgbuffer: Tbytes;
    rec_json,temp_json:Tqbjson;
    mymemory:TMemoryStream;
    Jpg:TjpegImage;
    Image1: TImage;
    user_list_qbarray:Tqbjsonarray;
    procedure show_log(msg:string;iscan:boolean=false);
    begin
         //假如日期改變那么重新生成一個新的日志檔案
         //初始化,報錯日志等寫入, 其它日志根據開關寫入
         if iscan or checkbox1.Checked then
         memo_log.Lines.Add(formatdatetime('yyyy-mm-dd hh:mm:ss',now)+'->'+msg);
    end;
    procedure freeandnil_obj;
    begin
         try
           if assigned(rec_json) then
           freeandnil(rec_json);
           if assigned(temp_json) then
           freeandnil(temp_json);
           if assigned(mymemory) then
           freeandnil(mymemory);
           if assigned(jpg) then
           freeandnil(jpg);
           if assigned(image1) then
           freeandnil(image1);
           if assigned(user_list_qbarray) then
           freeandnil(user_list_qbarray);
           setlength(buffer,0);
           setlength(jpgbuffer,0);
         except
           on e:exception do
           begin
               show_log('釋放變數例外:'+e.Message,true);
           end;
         end;
    end;
    function registed_check:boolean;
    begin
        try
          result:=false;
          serial:=trim(rec_json.GetString('Serial'));
          if not auth_conn.Connected then
          try
             auth_conn.Connected:=true;
          except
             on e:exception do
             begin
                  show_log('連接授權庫例外:'+e.Message);
                  exit;
             end;
          end;
           //首先查詢當前閘機是否已經授權,根據序列號,此號唯一,未授權的閘機可以心跳,不能開門
          ssql:='select id,企業代碼,人臉開門序號,二維碼刷卡開門序號 from '+
                 '客戶_智能設備表 where 設備序列號='''+trim(serial)+'''';
          q.close;
          q.Connection:=auth_conn;
          q.sql.text:=ssql;
          q.open;
          if q.IsEmpty then
          begin
               q.Close;
               show_log('閘機序列號沒有授權:'+serial);
               exit;
          end;
          enterprise_no:=q.Fields[1].AsString.trim;
          face_door:=q.Fields[2].AsString.trim;
          twocode_door:=q.Fields[3].AsString.trim;
          q.Close;
          result:=true;
        except
          on e:exception do
          begin
               q.Close;
               result:=false;
               show_log('檢查閘機是否注冊例外:'+e.Message,true);
          end;
        end;
     end;

     function cardno_check:boolean;
     begin
          try
            result:=false;
            if not registed_check then exit;
            if not member_conn.Connected then
            try
               member_conn.Connected:=true;
            except
               on e:exception do
               begin
                    show_log('連接會員庫例外:'+e.Message);
                    exit;
               end;
            end;
            {type:
              0 = 卡;
              1 = 串口232介面輸入字串,如二維碼等,
                  波特率in,out:9600,
                  Base64編碼資料,如果不設定,好像刷卡沒反應;
              2 = 密碼;
              3 = 按鈕請求;
              6 = 二代證資料;
              9 = Base64編碼資料,用于串口輸入,二維碼;
              10 = 指紋資料;
              11 = 指靜脈資料;
              12 = RFID卡;
              13 = 人臉;
              101 = 歷史報警記錄;
              100 = 歷史刷卡記錄;
              }
            cardno:=trim(rec_json.GetString('Card'));
            //EncdDecd 單元: EncodeString、DecodeString、EncodeBase64、DecodeBase64
            {Base64 編解碼,對流的編解碼:
              procedure EncodeStream(Input, Output: TStream); // 編碼
              procedure DecodeStream(Input, Output: TStream); // 解碼

              // 對字串的編解碼:
              function EncodeString(const Input: string): string; // 編碼
              function DecodeString(const Input: string): string; // 解碼
            }
            cardno:=trim(DecodeString(cardno));//進行base64解碼
            //假如是手機端券掃二維碼,格式:demo,202006221024370102
            data_array:=system.StrUtils.SplitString(cardno,',');
            ssql:='select getdate() as systime,'+
                         '限制使用星期 as limte_week '+
                         'from 券_記錄表 with (nolock) '+
                         //'適用連鎖企業代碼'為空那么默認只能在發行店使用,否則可以在相應的分店使用
                         'where ((isnull(適用連鎖企業代碼,企業代碼)='''+trim(data_array[0])+''') or ((isnull(適用連鎖企業代碼,'''')<>'''') and '+
                         '('''+trim(data_array[0])+''' in (select enterprise_no from dbo.stringtotable(適用連鎖企業代碼))))) '+
                         ' and 券號='''+trim(data_array[1])+''' and 當前狀態 in (''0'',''2'') and 企業代碼='''+trim(data_array[0])+''' '+
                         'and getdate()>=生效時間 and getdate()<=截至時間';

            q.close;
            q.Connection:=member_conn;
            q.sql.text:=ssql;
            q.open;
            if q.IsEmpty then
            begin
                  q.Close;
                  show_log('沒有查詢到券資料,請查看該券是否已經使用或者到期,或者不能在本店使用:'+cardno,true);
                  exit;
            end;
             //檢查是否限制星期幾使用
          {對于給定的TDateTime型別的日期時間,使用 DayOfTheWeek 函式能得到該日期是該星期的 第幾天。
          DayOfTheWeek 函式的回傳數值為 1 到 7,其中 1 表示星期一,而 7 表示星期日。
          注意:DayOfTheWeek 是 ISO 8601 標準的(此標準為 星期一是一周的第一天)。
          對于一周 的第一天是星期日的標準,如果想獲得星期數,請使用 DayOfWeek 函式。}
          week_num:=inttostr(dayoftheweek(q.fieldbyname('systime').asdatetime));
          if (trim(q.fieldbyname('limte_week').asstring)<>'') and
           (pos(week_num,trim(q.fieldbyname('limte_week').asstring))<0) then
            begin
                q.Close;
                show_log('該券'+trim(data_array[1])+#13#10+
                         '只能在星期'+q.FieldByName('limte_week').asstring+'才能使用'+#13#10+
                         '當前是星期'+week_num+',未滿足條件,不能使用',true);
                exit;
            end;

            ssql:='update 券_記錄表 set 當前狀態=''1'',使用面值=isnull(面值,0),'+
                  '使用賬單流水號='''+trim(rec_json.GetString('Index'))+''','+
                  '用券企業代碼='''+enterprise_no+''','+
                  '使用時間=getdate(),使用收銀員='''+trim(rec_json.GetString('Serial'))+''','+
                  '備注=(case when isnull(備注,'''')='''' then ''云閘機驗券成功'' else  isnull(備注,'''')+''云閘機驗券成功'' end) '+
                  'where 企業代碼='''+trim(data_array[0])+''' and 券號='''+trim(data_array[1])+'''';
            q.close;
            q.sql.text:=ssql;
            q.ExecSQL;
            if twocode_door='LOCKA' then
            door:='0'
            else
            door:='1';
            q.Close;
            result:=true;
          except
            on e:exception do
            begin
               q.Close;
               result:=false;
               show_log('刷卡/掃碼例外:'+e.Message,true);
            end;
          end;
     end;
     function face_check:boolean;
     var i:integer;
     begin
          try
             result:=false;
             if not registed_check then exit;
             try
               member_conn.Connected:=true;
             except
               on e:exception do
               begin
                    show_log('連接會員庫例外:'+e.Message);
                    exit;
               end;
             end;
             //定位'='位置,之后為圖片資料
             len:=system.pos('=',recstr);
             //設定buffer長度,分配記憶體
             SetLength(jpgbuffer, mymemory.Size-len);
             //從指定位置開始截取流資料
             mymemory.Position:=len;
             mymemory.readBuffer(jpgbuffer,mymemory.Size-len);
             //清除記憶體流,重新裝載圖片資料
             mymemory.Clear;
             mymemory.WriteBuffer(jpgbuffer,length(jpgbuffer));
             mymemory.Position:=0;
             jpg:=TJPEGImage.Create;
             jpg.LoadFromStream(mymemory);
             image1:=Timage.Create(nil);
             image1.Picture.Assign(jpg);
             file_path:=formatdatetime('yyyymmddhhmmsszzz',now)+'.jpg';
             image1.Picture.SaveToFile(file_path);
             //轉換為base64
             image_base64:=Base64Image(file_path);
             setlength(jpgbuffer,0);
             setlength(buffer,0);
             if image_base64='' then
             begin
                  q.Close;
                  exit;
             end;
             //比對人臉
             if not search_face(baidu_token,
                                image_base64,
                                enterprise_no,
                                '',
                                '1',
                                trim(quality_control.Text),
                                user_list,
                                errmsg) then
             begin
                  DeleteFile(file_path);
                  show_log('搜索人臉錯誤:'+errmsg,true);
                  exit;
             end;
             DeleteFile(file_path);
             if user_list='' then
             begin
                 show_log('人臉錯誤user_list為空');
                 exit;
             end;
            // "user_list":[
            {
              "group_id":"demo",              企業代碼
              "user_id":"hushiyong",          會員卡號
              //企業代碼,卡號,卡型別,客人姓名,與客人關系,關系者名稱
              "user_info":"demo**hushiyong**鳳凰儲值卡**hushiyong**本人**hushiyong**",
              "score":97.809585571289
            }
          //]
             isfind:=false;
             user_info:='';
             user_list_qbarray:=Tqbjsonarray.create(user_list);
             inum:=user_list_qbarray.length;
             for I := 0 to inum-1 do
             begin
                  score:=trim(user_list_qbarray.getJSon(i).GetString('score'));
                  score:=system.StrUtils.LeftStr(score,pos('.',score)-1);
              

uj5u.com熱心網友回復:

哎算了,貼代碼也貼不上來了,郁悶死了

uj5u.com熱心網友回復:

重新整理了一下,
procedure Tfrmmain.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var   mymemory:TMemoryStream;
        buffer,jpgbuffer: Tbytes;
        Jpg:TjpegImage;
        Image1: TImage;
        user_info:string;
        //這些子函式都是在下面的業務邏輯中,會反復呼叫的,會不會是這樣記憶體泄露了?
        function proc1:boolean;
        begin
              ...
        end;
        function proc2:boolean;
        begin
              ...
        end;
        function proc3:boolean;
        begin
              ...
        end;
       function proc4:boolean;
        begin
              ...
        end;
begin
       業務邏輯...
       //釋放變數
      if assigned(mymemory) then
         freeandnil(mymemory);

     if assigned(jpg) then
        freeandnil(jpg);

     if assigned(image1) then
        freeandnil(image1);

      setlength(buffer,0);
      setlength(jpgbuffer,0);
end;

uj5u.com熱心網友回復:

#2已經說過了,Delphi的記憶體管理有內置的泄露檢測,只需要在dpr檔案中加入ReportMemoryLeaksOnShutdown := true;

uj5u.com熱心網友回復:

最后把代碼整理了一下,原版發出來了,太長了,一次貼不完,子函式的代碼就省略了
procedure Tfrmmain.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var issucc:boolean;
    len,inum: integer;
    isdetail_log,isfind:boolean;
    acc_type,recstr,enterprise_no,ssql,door,
    face_door,twocode_door,user_info,serial,
    cardno,week_num,image_base64,user_list,
    errmsg,file_path,res_enterprise_no,
    res_cardno,score:string;
    user_info_array:Tstringdynarray;
    data_array:Tstringdynarray;
    //需要釋放的變數
    buffer,jpgbuffer: Tbytes;
    rec_json,temp_json:Tqbjson;
    mymemory:TMemoryStream;
    Jpg:TjpegImage;
    Image1: TImage;
    user_list_qbarray:Tqbjsonarray;
    //顯示日志
    procedure show_log(msg:string;iscan:boolean=false);
    begin
         if iscan or checkbox1.Checked then
         memo_log.Lines.Add(formatdatetime('yyyy-mm-dd hh:mm:ss',now)+'->'+msg);
    end;
    //釋放變數
    procedure freeandnil_obj;
    begin
         try
           if assigned(rec_json) then
           freeandnil(rec_json);
           if assigned(temp_json) then
           freeandnil(temp_json);
           if assigned(mymemory) then
           freeandnil(mymemory);
           if assigned(jpg) then
           freeandnil(jpg);
           if assigned(image1) then
           freeandnil(image1);
           if assigned(user_list_qbarray) then
           freeandnil(user_list_qbarray);
           setlength(buffer,0);
           setlength(jpgbuffer,0);
         except
           on e:exception do
           begin
               show_log('釋放變數例外:'+e.Message,true);
           end;
         end;
    end;
    //檢查云閘機是否注冊
    function registed_check:boolean;
    begin
        ...
     end;
    //閘機掃二維碼
     function cardno_check:boolean;
     begin
          呼叫registed_check函式
          ...
     end;
     //人臉比對
     function face_check:boolean;
     var i:integer;
     begin
          呼叫registed_check函式
          ...
          //根據流創建圖片的部分代碼
             
             len:=system.pos('=',recstr);
             //設定buffer長度,分配記憶體
             SetLength(jpgbuffer, mymemory.Size-len);
             //從指定位置開始截取流資料
             mymemory.Position:=len;
             mymemory.readBuffer(jpgbuffer,mymemory.Size-len);
             //清除記憶體流,重新裝載圖片資料
             mymemory.Clear;
             mymemory.WriteBuffer(jpgbuffer,length(jpgbuffer));
             mymemory.Position:=0;
             jpg:=TJPEGImage.Create;
             jpg.LoadFromStream(mymemory);
             image1:=Timage.Create(nil);
             image1.Picture.Assign(jpg);
             file_path:=formatdatetime('yyyymmddhhmmsszzz',now)+'.jpg';
             image1.Picture.SaveToFile(file_path);
             //轉換為base64
             image_base64:=Base64Image(file_path);
             setlength(jpgbuffer,0);
             setlength(buffer,0);
             
             //開始比對
             ...
     end;
     //回傳給閘機資料的函式
     procedure result_data(is_heart,is_succ:boolean);
     begin
           ...
     end;


//主函式開始............
begin
    try
       if isok then
       begin
           freeandnil_obj;
           AResponseInfo.FreeContentStream:=true;
           exit;
       end;
       isok:=true;
       if (UpperCase(ARequestInfo.command)='POST') and
          (ARequestInfo.PostStream<>nil) and
          (ARequestInfo.PostStream.Size > 0) then
       begin
           mymemory:=TMemoryStream(ARequestInfo.PostStream);
           mymemory.Position:=0;
           //釋放原來的記憶體
           SetLength(buffer, 0);
           //從新分配記憶體
           SetLength(buffer, mymemory.Size);
           //把流資料讀到buffer中
           mymemory.ReadBuffer(buffer, mymemory.Size);
           //轉換為字串
           recstr:=stringof(buffer);
           //轉換為json
           rec_json:=Tqbjson.Create(recstr);
           //心跳
           if not rec_json.IsNull('Key') then
           begin
                 //show_log('收到心跳資料:'+rec_json.ToString2(2));
                result_data(true,false);
           end
           else
           if not rec_json.IsNull('type') then
           begin
               acc_type:=rec_json.GetString('type');
               //普通刷卡/掃二維碼
               if (acc_type='1') or (acc_type='9') then
               begin
                    //issucc:=cardno_check;
                    result_data(false,cardno_check);
               end
               else //人臉識別
               if acc_type='13' then
               begin
                    //issucc:=face_check;
                    result_data(false,face_check);
               end
               else
               result_data(false,false);
           end
           else
           result_data(false,false);
       end;
       //釋放創建的變數
       freeandnil_obj;
    except
       on e:exception do
       begin
           result_data(false,false);
           freeandnil_obj;
           show_log('請求執行例外:'+e.Message,true);
       end;
    end;
    isok:=false;
end;

uj5u.com熱心網友回復:

to:早打大打打核戰爭 
我已經加了,但是沒有看到,我的是xe10.1

uj5u.com熱心網友回復:

參考 17 樓 aidpoint20126 的回復:
to:早打大打打核戰爭 
我已經加了,但是沒有看到,我的是xe10.1


1、你說的(或是你理解的)”記憶體泄露“,可能并不一定就是真正的記憶體泄露,最好是給圖,錯誤內容的圖。因為,你說沒看到,那你怎么會知道是”記憶體泄露“,又怎么定位到是CommandGet。

2、不用執行緒,但也并不是說所有代碼都必須寫到CommandGet程序里,可以抽象出一些功能代碼為函式,在CommandGet事件里呼叫就好,也就是說通常不要讓事件里代碼太多。

3、我大致在你的代碼里看到了資料庫操作,你使用的是idhttpserver,而不是三層架構方式,估計你的資料庫操作是使用的共用控制元件,所以,你這里的資料庫肯定會出問題(但不會是記憶體泄露)。之前一直說這個CommandGet是一個執行緒處理,執行緒里不要去使用公共變數(全域變數),包括公共資料庫連接操作控制元件。他們會相互影響的,如執行緒A影響執行緒B(要用怎么辦:臨界區加鎖、執行緒同步等方式,你需要的解決方法是動態創建資料庫操作控制元件,同時使用資料庫連接池的方式,”不會連接池怎么辦?“很簡單,動態創建資料庫的連接,粗暴了一些,系統不會有太多問題,但效率會慢,如果資料庫有連接數量限制就會有問題)。

uj5u.com熱心網友回復:

再補充一點,你貼代碼的方式不對,正確的是下面這個:


     //回傳給閘機資料的函式
     procedure result_data(is_heart,is_succ:boolean);
     begin
           ...
     end;

uj5u.com熱心網友回復:

參考 17 樓 aidpoint20126 的回復:
to:早打大打打核戰爭 
我已經加了,但是沒有看到,我的是xe10.1


可能沒有運行到出現泄露的代碼,你可以打開tools->code guard,選擇自己需要監控的內容,然后save all、build,再運行,程式結束之后會有詳細的報告,能定位到原始碼,這個功能很好用,比自己手動除錯要方便多了

uj5u.com熱心網友回復:

HttpServer除了直接的回應訊息,要主動給客戶端發訊息,就是傳統上的客戶端輪詢,AJAX等。最好的技術肯定是現在的WebSocket技術了。
貌似delphi的WebSocket開源也不少,Git是找下。
你這個HttpServer是專用的,可以用土辦法,客戶端輪詢,呵呵

uj5u.com熱心網友回復:

to:看那山瞧那水
這個是硬體發送過來的資料,云閘機發送過來的,http協議,沒有websocket的,沒有辦法啊,如果是websocket的話,就好辦了

uj5u.com熱心網友回復:

to :tanqth
我的資料庫連接控制元件確實是放在form上的,沒有自動創建,公用的

uj5u.com熱心網友回復:

參考 23 樓 aidpoint20126 的回復:
to :tanqth
我的資料庫連接控制元件確實是放在form上的,沒有自動創建,公用的


參照我說的,不能公用。問題及解決辦法都給你說過。

uj5u.com熱心網友回復:

這幾天他忙了,還沒來得及驗證,搞好了就結貼,謝謝了

uj5u.com熱心網友回復:

to:tanqth
我現在想這樣做,資料庫連接控制元件公用,啟用連接池(unidac的uniconnection),然后資料庫訪問控制uniquery,每次收到請求時動態創建,這樣行不行?

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/8232.html

標籤:網絡通信/分布式開發

上一篇:cxGrid過濾的時候能不能同一列多個值一起過濾

下一篇:delphi 如何以樹節點展示資料庫的某一列資料

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more