回 帖 发 新 帖 刷新版面

主题:为什么多线程使用时老是出现空指针错误?附源代码如下

我是多个客户端连接服务器端,服务器端收到客户端的请求后,到数据库里查询数据,然后返回给客户端,服务端用TServerSocket,方式为stNonBlocking,数据库连接用TADOConnection,查询用TADOQuery,数据库用的是sql server,建了一个100万的数据表,服务器从数据库里查询出来一个结果的时间是比较快,我用6个客户端同时连接服务器端,然后不断地发送6000个请求,老是出现以下错误:
1、eaccessviolation
2、Function call terminated by unhandled exception oxeedfade at address 0x7c812a5b
3、invaliation pointer operator
不知道怎么解决咯,请各位协助?
附我的源代码如下:

<--Frmmain.cpp-->//主窗体

//------------------------------------------------------------------------

void __fastcall TMain::ServerSocketClientError(TObject *Sender,
        TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
    ErrorCode = 0;
    Socket->Close();
}
//------------------------------------------------------------------------
void __fastcall TMain::ServerSocketClientConnect(TObject *Sender,
        TCustomWinSocket *Socket)
{
    TRecvStreamThread *RecvThread;
    
    try
    {
        //如果目前连接的数量大于设置的最大数量,则不接收该客户端的请求
        //MaxThreadMaxNum:设置的最大允许连接数
        //TotalThreadNum:当前的连接数

        if (TotalThreadNum>MaxThreadMaxNum)
        {
            //关闭连接
            Socket->Close();
            return;
        }
        else
        {
            RecvThread = new TRecvStreamThread(true);
            if (RecvThread==NULL)
            {
                MessageBoxA(NULL,"创建线程失败!","系统提示",0 );
                return;
            }
            RecvThread->FreeOnTerminate = true; // 线程执行完毕自动释放资源
            RecvThread->Priority = tpLower; // set the priority lower than normal
            RecvThread->RemoteAddress=Socket->RemoteAddress ;
            RecvThread->hWndNo =RecvThread->Handle;
            RecvThread->TermScoket=Socket->SocketHandle;
            RecvThread->Resume();  // 开始执行线程
        }
    }

    catch(...)
    {
        MessageBoxA(NULL,"出现错误!","系统提示",0 );
        if (!RecvThread==NULL)
        {
            RecvThread->Free();
            TotalThreadNum--;
        }
        return;
    }

}

<--Frmmain.h-->

//------------------------------------------------------------------------

#ifndef FrmMainH
#define FrmMainH
//------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Menus.hpp>
#include <ExtCtrls.hpp>
#include <ComCtrls.hpp>
#include <FileCtrl.hpp>
#include <ImgList.hpp>
#include <NMFtp.hpp>
#include <Psock.hpp>
#include <ScktComp.hpp>
#include <ToolWin.hpp>
#include <IniFiles.hpp>
#include <ComCtrls.hpp>
#include <NMFtp.hpp>
#include <Psock.hpp>
#include <ScktComp.hpp>
#include <Dialogs.hpp>

//------------------------------------------------------------------------
class TMain : public TForm
{
__published:// IDE-managed Components
    TServerSocket *ServerSocket;
    TTimer *Timer1;

    TLabel *lblClientNum;
    TLabel *Label1;
    void __fastcall FormCreate(TObject *Sender);
    void __fastcall ServerSocketClientConnect(TObject *Sender,
            TCustomWinSocket *Socket);
    void __fastcall ServerSocketClientError(TObject *Sender,
                                            TCustomWinSocket *Socket, TErrorEvent ErrorEvent,
                                            int &ErrorCode);
    void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
    void __fastcall Timer1Timer(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
    void __fastcall FormKeyDown(TObject *Sender, WORD &Key,
                                TShiftState Shift);
    void __fastcall N3Click(TObject *Sender);
private:// User declarations
    bool __fastcall Logo_Oper(void);
    bool __fastcall ConnectDataBase();
    long packHadle[50];
    bool accessRight[50];
    AnsiString shutdownkind;

    int MaxThreadMaxNum;//最大允许连接数量

protected:
public:// User declarations
   int ServerPort;
   int TotalThreadNum;//当前连接总数量
   AnsiString       ServerAddress;
   AnsiString DataConnectString;//数据库连接字符串
   AnsiString ExeFilePath;//系统所在路径
    __fastcall TMain(TComponent* Owner);
};
//------------------------------------------------------------------------
extern PACKAGE TMain *Main;
//------------------------------------------------------------------------
#endif

回复列表 (共5个回复)

沙发

<--RecvStream.cpp-->//多线程处理

//---------------------------------------------------------------------------
#include <vcl.h>
#include <dateutils.hpp>
#include <stdio.h>
#include <SysUtils.hpp>
#pragma hdrstop
#include "RecvStream.h"
#include "PublicVal.h"
#include "Socket.cpp"
#include "DataSourcewin.h"
#include "FrmMain.h"
#include "windows.h"
#include <iostream>
#pragma package(smart_init)

__fastcall TRecvStreamThread::TRecvStreamThread(bool CreateSuspended)
        : TThread(CreateSuspended)
{
    TCriticalSection *pLockX;
    pLockX=new TCriticalSection;

    //记录总线程的数量
    pLockX->Acquire();
    Sleep(5);
    Main->TotalThreadNum++;
    pLockX->Release();

    delete   pLockX  ;
}


//----------------------------------------------------------------------
void __fastcall TRecvStreamThread::ExecuteData()
{
    char buf[1024];
    int pktlen,m_pack_long;
    AnsiStringRsText,RcData;
    
    try
    {
        try
        {
            m_pack_long=0 ;
            RsText="";

            do
            {
                Sleep(3);
                Application->ProcessMessages();
                pktlen = recv(TermScoket,buf,sizeof(buf),0);
                m_pack_long++;
            }
            while (m_pack_long<1000 &&  pktlen<0);

            if (pktlen<0)
            {
                //接收数据失败
                return ;
            }
            else if (pktlen == 0)
            {
                //收到的数据为空字串  ,表示对方已经关闭连接
                return;
            }
            else
            {
                //处理接收到的数据,验证是否为合法的数据

                RsText= StrPas(buf);

                //数据长度是否大于12位,数据头两位是否为10
                if(pktlen > 12 && RsText.SubString(1,2)=="10") 
                
                {
                    RsText = AnsiString(buf).SubString(1,pktlen);

                    m_pack_long= StrToInt("0" +Trim(RsText.SubString(3,4)));
                     //如果数据包实际长度和数据包中的长度不一致
                    if (RsText.Length()!=m_pack_long && RsText.Length()<13)
                    {
                        
                    }
                    else
                    {
                        RcData= AnsiReplaceText(RsText,char(26),"");
                        //执行处理数据业务
                        ProcessData(RcData);
                    }

                  }
              }
        }  
        catch(Exception &exception)
        {
           Application->ShowException(&exception);
        }
    }
    __finally
    {
        try
        {
            delete buf;
        }
        __finally
        {
        }
    }
    
}

板凳

//--------------------------------------------------------
void __fastcall TRecvStreamThread::ProcessData(AnsiString ReceiveText)
{
    /*变量定义部分*/
    AnsiString Services[2]={"0000","0001"};//服务类型列表
    AnsiString BodyInfo[10];//存储包体中的各参数内容
    AnsiString RecBody;//存储包体
    AnsiString OpDateStr;//当前日期,格式为yyyymmdd
    AnsiString Sendstr;//返回给客户端的数据
    AnsiString SQLString;//数据库操作的SQL语句
    AnsiString ResultInfo;
    data_record_head RcTxt;//接收到的数据结构
    FILE *FpLog;//写日志
    int ServeType;//服务类型对应的在服务类型列表中的序号
    int i,ChPos,cmdnum;
    unsigned short int len,actlen;
    
    AnsiString TermDir;//日志文件路径
    AnsiString ErrCode;//错误编码
    AnsiString UpdateDataKind;//保存数据的结果 0:成功 1:失败
    AnsiString CmdResult;//是否存在从数据包中解析出来的服务类型 0:存在 1:不存在
    AnsiString ReceiveLog,SendLog;//接收到的数据日志信息和发送的数据日志信息

    TADOConnection *AdoConn;//数据库连接对象
    TADOQuery *AdoMeth;//数据库查询对象

    try
    {
        //初始化COM对象
        CoInitialize(NULL);
        try
        {
            //获取客户请求数据包信息
            RcTxt.m_Size=Trim(ReceiveText.SubString(3,4)); //数据包大小
            RcTxt.m_Serv_Type=Trim(ReceiveText.SubString(7,4)); //服务类型
            RcTxt.m_Err_Number=Trim(ReceiveText.SubString(11,2)); //错误码
            RecBody=ReceiveText.SubString(13,ReceiveText.Length()-12)+char(9); //获取包体

            if (RcTxt.m_Size==NULL || RcTxt.m_Serv_Type==NULL || RcTxt.m_Err_Number==NULL || RecBody==NULL )
            {
                //如果获取的上述数据有一个为空,则返回
                return;
            }

            //初始化一些参数值
            ......

           //获取服务类型对应的序号
           for (i=0;i<cmdnum;i++)
               if (Services[i]==RcTxt.m_Serv_Type)
               {
                   ServeType=i;
                   CmdResult="0";
                   break;
               }

           //如果获取的服务类型不存在,则退出
           if (CmdResult=="1")
           {
               return;
           }

            //创建数据库连接
            AdoConn=new TADOConnection(NULL);
            //创建是否成功,如果不成功,则退出
            if (AdoConn==NULL)
            {
                //销毁COM对象
                CoUninitialize();
                return;
            }

3 楼

//如果数据库连接字符串为空
            //数据库连接字符串是保存在主窗体的DataConnectString中
            if (Main->DataConnectString==NULL)
            {
                delete AdoConn;
        AdoConn = NULL;
                //销毁COM对象
                CoUninitialize();
                return;
            }
            
            AdoConn->ConnectionString=Main->DataConnectString;
            AdoConn->Connected=true;
            //创建数据操作对象
            AdoMeth=new TADOQuery(NULL);
            //创建是否成功,如果不成功,则退出
            if (AdoMeth==NULL)
            {
                AdoConn->Close();
                AdoConn->Connected = false;
        delete AdoConn;
        AdoConn = NULL;
                //销毁COM对象
                CoUninitialize();
                return;
            }
        
            AdoMeth->Connection = AdoConn;
            //AdoMeth->Connection =DataSourceWindow->Database1 ;
            AdoMeth->CursorLocation=clUseClient;
        
            //把包体中的各参数数据记录到BodyInfo数组中
            for (i=0;RecBody.Length()>0;i++)
            {
                ChPos=RecBody.Pos(char(9));
                BodyInfo[i]=Trim(RecBody.SubString(1,ChPos-1));
                RecBody.Delete(1,ChPos);
            }
        
            //获取当前日期
            OpDateStr=DateToAnsiString(Date());

            //如果日志文件夹不存在,则自动创建,日志文件夹以客户端的IP地址命名
            TermDir= Main->ExeFilePath+"data\\"+RemoteAddress;
            if (!DirectoryExists(TermDir))
                CreateDir(TermDir);

            //记录客户请求数据日志
            if (ServeType!=24)
            {
               ReceiveLog=(Time().TimeString()+"    请求包:"+AnsiReplaceText(ReceiveText,char(26),"")+"\n");
            }

            AdoMeth->Close();
            AdoMeth->SQL->Clear();
        

4 楼

//开始处理客户请求服务
            switch (ServeType)
            {
            case 0:  
                .......//具体业务处理
                break;
            case 1: 
                .......//具体业务处理
                break;
            }

            //如果是数据库操作失败,则返回给客户端数据库操作失败的信息
            if (UpdateDataKind=="1")
            {
                Sendstr[11]='6';
                Sendstr[12]='4';
            }

            //计算要返回给客户端数据包的长度
            RcTxt.m_Size=(IntToStr(Sendstr.Length())+"     ").SubString(1,4);
            for (int i=1;i<5;i++)
                Sendstr[2+i]=RcTxt.m_Size[i];

            //返回给客户端回复数据
            send(TermScoket,Sendstr.c_str(),Sendstr.Length(),0);

            //记录数据接收和发送日志
            if (ServeType!=24 )
            {
                SendLog=(Time().TimeString() +"    回复包:"+AnsiReplaceText(Sendstr,char(26),"")+"\n");
                FpLog = fopen((Main->ExeFilePath+"\\data\\"+ RemoteAddress+"\\"+OpDateStr +".txt").c_str(), "a+"); //记录日志
                //判断文件是否打开失败
                if (FpLog==NULL)
                {
                }
                else
                {
                    fprintf(FpLog,ReceiveLog.c_str());
                    fprintf(FpLog,SendLog.c_str());
                }
                fclose(FpLog);
                delete FpLog;
            }

        }
        catch(...)
        {
        }
    }
    __finally
    {
        try
        {
            //关闭数据库连接
            if (!AdoMeth==NULL)
            {
                AdoMeth->Close();
                delete AdoMeth;
                AdoMeth=NULL;
            }
            Sleep(5);
            if (!AdoConn==NULL)
            {
                AdoConn->Close();
                AdoConn->Connected = false;
        delete AdoConn;
        AdoConn = NULL;
            }
            //销毁COM对象
            CoUninitialize();
        }
        __finally
        {
        }
    }
    
}

5 楼

<--RecvStream.h-->

//------------------------------------------------------------------------
#ifndef RecvStreamH
#define RecvStreamH
//------------------------------------------------------------------------
#include <vcl.h>
#include <Classes.hpp>
#include <IniFiles.hpp>
#include <ComCtrls.hpp>
#include <ADODB.hpp>
#include <winsock.h>
#include <StrUtils.hpp>


//------------------------------------------------------------------------
class TRecvStreamThread : public TThread
{
private:
    /* -------------------------------------------------------------------
        数据包接口结构
     ---------------------------------------------------------------------    */
    typedef structReceive_RECORD
    {
        AnsiStringm_Size;//数据包大小(5位)
        AnsiStringm_Serv_Type;//服务类型
        AnsiStringm_Err_Number;//错误码
    }
    data_record_head;

    void __fastcall ProcessData(AnsiString ReceiveText);
    void __fastcall ExecuteData();
    long nIndex;

protected:
    void __fastcall Execute();

public:
    __fastcall TRecvStreamThread(bool CreateSuspended);
    AnsiString       RemoteAddress;
    SOCKET TermScoket;
    
    unsigned int   hWndNo;   //本身的句柄,用来判断是否结束。
    int SendLen;
__published:


};
//------------------------------------------------------------------------
#endif

我来回复

您尚未登录,请登录后再回复。点此登录或注册