kenlistian

厚积薄发. 勤为桨,思为帆

   :: 首页 :: 新随笔 ::  :: 聚合  :: 管理 ::
  73 随笔 :: 4 文章 :: 22 评论 :: 0 Trackbacks

#

    MFC中CEdit多行情况下,调用GetLine()时,发现老是后面跟有乱码。即使你把str初始化也是如此。

对于采用GetLine()调用,其函数说明有2种。

1.int GetLine(
   int nIndex,
   LPTSTR lpszBuffer 
) const;
2.int GetLine(
   int nIndex,
   LPTSTR lpszBuffer,
   int nMaxLength 
) const;

  代码如下:

   char str[10] = {'\0'};
   int nLineNum;//想要获取的行号
   nLineNum=0;
   m_ctlEditTest.GetLine(nLineNum,str);

Remarks : The copied line does not contain a null-termination character.

看了下msdn,一句话说得很明白,getline做了copy后是不给你加null结束符的。因此需要自己在定义的char字符串末尾添加。而一次实际上会copy回多少个字节。则在该函数的说明中,在msdn中清晰表达为:

Return Value

  The number of bytes actually copied. The return value is 0 if the line number specified by nIndex is greater than the number of lines in the edit control.

所以,上面的代码段得用一个值取得实际长度,把多余的截取掉,但我在用getline1时老是返回0,不知道为何?不过改成getline2,指定copy9个字节时,同时对第10个字节设置为null则正常返回,没有乱码的出现。如下

  int iLen =  m_ctlEditTest.GetLine(nLineNum,str, 9);

  str[10] = '\0'; 

即可。

。。。。。。。

在cedit中还有个linelength函数,

int LineLength(
   int nLine = -1 
) const;

该函数是应该返回指定行的长度,但是如果不仔细看msdn的说明,很容易误解nLine是指cedit行中的某一行行数而代入其中,并得到错误的结论。

而在msdn中,nLine的说明是:

nLine

Specifies the character index of a character in the line whose length is to be retrieved. If this parameter is –1, the length of the current line (the line that contains the caret) is returned, not including the length of any selected text within the line. When LineLength is called for a single-line edit control, this parameter is ignored.

就是说nLine是字符串的字符索引,在多行情况下所以它必须通过LineIndex函数来获取。在msdn中如下描述,

   Use the LineIndex member function to retrieve a character index for a given line number within a multiple-line edit control.

而LineIndex函数说明如下:

all this function to retrieve the character index of a line within a multiple-line edit control.

int LineIndex(
   int nLine = -1 
) const;

nLine

Contains the index value for the desired line in the text of the edit control, or contains –1. If nLine is –1, it specifies the current line, that is, the line that contains the caret.

该nLine才是真正的CEdit中的某一行列。也就是我们通过getlinecount()获取到cedit的行数后,在到每一行中去找一个character index,再才能确定一行的长度。

有时,觉得mfc是不是有点对个简单的问题把弯子绕得太远了点吧。

。。。。

下面摘抄另一种解决方案,采用CString方式来copy一行的长度,我在自己机子测试过,通过GetLine函数1没有通过,返回的是个空串,而采用GetLine2指定返回一个最大长度时,却能copy回所要的数据,不过当采用最大长度时,由于拷贝回来的是一个不带null终结符的串,则存在乱码。由于工作时间紧张,问题只能采用指定大小的char串处理,但是采用CString串如果不指定大小的方法做一个mark。下列代码供测试参考。

CString strTemp;
int nLineNum;
nLineNum=0;
m_ctlEditTest.GetLine(nLineNum,strTemp.GetBufferSetLength(m_ctlEditTest.LineLength(m_ctlEditTest.LineIndex(nLineNum))));
strTemp.ReleaseBuffer();

。。。。。。

总而言之,感觉vc中mfc太过于细节,莫免麻烦。但是感觉经历过一次后,就不再是磕脚的石头,而是心态的放心。也许,这是心理作用而已。

posted @ 2008-02-15 21:17 kenlistian 阅读(5416) | 评论 (0)编辑 收藏

方法步骤:

1.在创建静态dll文件时,定义宏变量DLL_FILE,如下:
   #ifndef DLL_FILE
   #define DLL_FILE
   #endif

2.以下面方式声明lib中classname的头文件。

#ifdef DLL_FILE
    class _declspec(dllexport) classname  //导出类classname
#else
    class _declspec(dllimport) classname  //导入类classname
#endif

   然后实现在cpp中实现该类。

3.将该头文件和lib文件加入到调用classname的工程文件。





Powered by ScribeFire.

posted @ 2008-02-15 00:45 kenlistian 阅读(482) | 评论 (0)编辑 收藏

   在vc中采用流指针处理写入数据库的方法如下。

采用_StreamPtr处理Loadfromfile,savetofile,open来处理。

/*
  测试插入流文件,
*/
void insert_flow1()
{
    _StreamPtr    pwStream;   
    _bstr_t        strPath("D:\\my\\21.wav");
    _bstr_t        strOpen("");

    _variant_t    varBLOB;
    _variant_t  varOptional(DISP_E_PARAMNOTFOUND,VT_ERROR);

    HRESULT hr = pwStream.CreateInstance(_uuidof(Stream));
    if(SUCCEEDED(hr))
    {
        pwStream->Type = adTypeBinary;
        hr = pwStream->Open(varOptional, adModeUnknown, adOpenStreamUnspecified, strOpen, strOpen);
    }

    _bstr_t strSql("select (max(id) +1) as id from t_mov1");
    m_pRecordset->Open(strSql, _variant_t((IDispatch*)m_pConnection,true),
                adOpenDynamic, adLockOptimistic, adCmdText);

    variant_t vRet = m_pRecordset->GetCollect("id");
    int id = vRet.lVal;

    strSql = "select * from t_mov1 where 1 = 0";
    m_pRecordset->Close();
    m_pRecordset->Open(strSql,  _variant_t((IDispatch*)m_pConnection,true),
                adOpenDynamic, adLockOptimistic, adCmdText);
   
    if(SUCCEEDED(hr))
    {
        pwStream->LoadFromFile(strPath);
        m_pRecordset->AddNew(vtMissing, vtMissing);
        m_pRecordset->PutCollect("id", (long)id);       
        m_pRecordset->PutCollect("b_flow", pwStream->Read(adReadAll));
        m_pRecordset->Update();
        pwStream->Close();
    }

}




/*
    从数据库中读出流数据
*/
void read_flow1(int id)
{       
    char    *m_pBuffer;
    _StreamPtr    prStream;
    _bstr_t        strOpen("");
    _variant_t  varOptional(DISP_E_PARAMNOTFOUND,VT_ERROR);
    HRESULT        hr;

    hr = prStream.CreateInstance(_uuidof(Stream));
    if(SUCCEEDED(hr))
    {
        prStream->Type = adTypeBinary;
        hr = prStream->Open(varOptional, adModeUnknown, adOpenStreamUnspecified, strOpen, strOpen);
    }

   
    CString strSql;
    strSql.Format("select * from t_mov1 where id = %d", id);

    m_pRecordset->Open((_bstr_t)strSql, _variant_t((IDispatch*)m_pConnection,true),
                adOpenDynamic, adLockOptimistic, adCmdText);

    if(SUCCEEDED(hr))
    {
        prStream->Write(m_pRecordset->Fields->GetItem("b_flow")->GetValue());
       
        long lDataSize;
        lDataSize = prStream->GetSize();   

        _variant_t  varBLOB;
       
        //流指针指向首位
        prStream->put_Position(0);
        //varBLOB.vt = VT_ARRAY | VT_UI1;
        //varBLOB.scode = DISP_E_PARAMNOTFOUND;
        varBLOB = prStream->Read(adReadAll);
           
        if(varBLOB.vt == (VT_ARRAY | VT_UI1))
        {   
            //分配必要的存储空间
            if(m_pBuffer = new char[lDataSize+1])               
            {   
                char *pBuf = NULL;
               
                //复制数据到缓冲区m_pBuffer
                SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);               
                memcpy(m_pBuffer, pBuf, lDataSize);           
                SafeArrayUnaccessData (varBLOB.parray);               
            }
        }
       

        prStream->SaveToFile("d:\\1.wav", adSaveCreateOverWrite);
        prStream->Close();
    }
}


Powered by ScribeFire.

posted @ 2008-02-13 19:22 kenlistian 阅读(1190) | 评论 (0)编辑 收藏

     摘要: 猛料!转帖保存学习用。 CString是一个动态TCHAR数组,BSTR是一种专有格式的字符串(需要用系统提供的函数来操纵)LPCTSTR只是一个常量的TCHAR指针。CString 是一个完全独立的类,动态的TCHAR数组,封装了+等操作符和字符串操作方法。typedef OLECHAR FAR* BSTR;type...  阅读全文
posted @ 2008-02-13 17:41 kenlistian 阅读(8927) | 评论 (0)编辑 收藏

    猛料资料,首先介绍SafeArray使用,在介绍SafeArray中的结构。看完该节文章,SafeArray的陌生感一扫而去。

    SafeArray 在ADO编程中经常使用。它的主要目的是用于automation中的数组型参数的传递。因为在网络环境中,数组是不能直接传递的,而必须将其包装成 SafeArray。实质上SafeArray就是将通常的数组增加一个描述符,说明其维数、长度、边界、元素类型等信息。SafeArray也并不单独使用,而是将其再包装到VARIANT类型的变量中,然后才作为参数传送出去。在VARIANT的vt成员的值如果包含VT_ARRAY|...,那么它所封装的就是一个SafeArray,它的parray成员即是指向SafeArray的指针。SafeArray中元素的类型可以是VARIANT能封装的任何类型,包括VARIANT类型本身。 

使用SafeArray的具体步骤:

方法一:

 包装一个SafeArray:

(1)定义变量,如:

   VARIANT varChunk;

   SAFEARRAY *psa;

   SAFEARRAYBOUND rgsabound[1];

(2) 创建SafeArray描述符:

 //read array from a file.

 uIsRead=f.Read(bVal,ChunkSize);

 if(uIsRead==0)

break;

 rgsabound[0].cElements = uIsRead;

 rgsabound[0].lLbound = 0;

 psa = SafeArrayCreate(VT_UI1,1,rgsabound);

(3)放置数据元素到SafeArray:

 for(long index=0;index<uIsRead;index++)         

 {

    if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))

    ::MessageBox(NULL,"出毛病了。","提示",MB_OK | MB_ICONWARNING);

  }

 一个一个地放,挺麻烦的。

(4)封装到VARIANT内:

   varChunk.vt = VT_ARRAY|VT_UI1;

   varChunk.parray = psa;

 这样就可以将varChunk作为参数传送出去了。

读取SafeArray中的数据的步骤:

(1)用SafeArrayGetElement一个一个地读

 BYTE buf[lIsRead];

 for(long index=0; index<lIsRead; index++)        

 {          

    ::SafeArrayGetElement(varChunk.parray,&index,buf+index);  

 }

 就读到缓冲区buf里了。

方法二:

 使用SafeArrayAccessData直接读写SafeArray的缓冲区:

(1)读缓冲区:

 BYTE *buf;

 SafeArrayAccessData(varChunk.parray, (void **)&buf);

 f.Write(buf,lIsRead);

 SafeArrayUnaccessData(varChunk.parray);

(2)写缓冲区:

 BYTE *buf;

 ::SafeArrayAccessData(psa, (void **)&buf);

 for(long index=0;index<uIsRead;index++)         

 {

     buf[index]=bVal[index]; 

 }

 ::SafeArrayUnaccessData(psa);

 varChunk.vt = VT_ARRAY|VT_UI1;

 varChunk.parray = psa;

   

    这种方法读写SafeArray都可以,它直接操纵SafeArray的数据缓冲区,比用SafeArrayGetElement和 SafeArrayPutElement速度快。特别适合于读取数据。但用完之后不要忘了调用::SafeArrayUnaccessData (psa),否则会出错的。

以下就是SAFEARRAY的Win32定义:

  typedef struct tagSAFEARRAY

   {

    unsigned short cDims;

    unsigned short fFeatures;

    unsigned long cbElements;

    unsigned long cLocks;

    void * pvData;

    SAFEARRAYBOUND rgsabound[ 1 ];

   } SAFEARRAY;

  这个结构的成员(cDims,cLocks等)是通过API函数来设置和管理的。真正的数据存放在pvData成员中,而SAFEARRAYBOUND结构定义该数组结构的细节。以下就是该结构成员的简要描述:

成员

描述

cDims

数组的维数

fFeatures

用来描述数组如何分配和如何被释放的标志

cbElements

数组元素的大小

cLocks

一个计数器,用来跟踪该数组被锁定的次数

pvData 

指向数据缓冲的指针

rgsabound

描述数组每维的数组结构,该数组的大小是可变的

 rgsabound是一个有趣的成员,它的结构不太直观。它是数据范围的数组。该数组的大小依safe array维数的不同而有所区别。rgsabound成员是一个SAFEARRAYBOUND结构的数组--每个元素代表SAFEARRAY的一个维。

  typedef struct tagSAFEARRAYBOUND

   {

    unsigned long cElements;

    unsigned long lLbound;

   } SAFEARRAYBOUND;

  维数被定义在cDims成员中。例如,一个\'C\'类数组的维数可以是[3][4][5]-一个三维的数组。如果我们使用一个SAFEARRAY来表示这个结构,我们定义一个有三个元素的rgsabound数组--一个代表一维。

  cDims = 3;

    ...

  SAFEARRAYBOUND rgsabound[3];

  rgsabound[0]元素定义第一维。在这个例子中ILBOUND元素为0,是数组的下界。cElements成员的值等于三。数组的第二维 ([4])可以被rgsabound结构的第二个元素定义。下界也可以是0,元素的个数是4,第三维也是这样。

   要注意,由于这是一个"C"数组,因此由0 开始,对于其它语言,例如Visual Basic,或者使用一个不同的开始。该数组的详细情况如下所示:

元素

cElements

ILbound

rgsabound[0] 

0

rgsabound[1]

0

rgsabound[2]

5

0

   关于SAFEARRAYBOUND结构其实还有很多没说的。我们将要使用的SAFEARRAY只是一个简单的单维字节数组。我们通过API函数创建数组的时候,SAFEARRAYBOUND将会被自动设置。只有在你需要使用复杂的多维数组的时候,你才需要操作这个结构。

  还有一个名字为cLocks的成员变量。很明显,它与时间没有任何的关系--它是一个锁的计数器。该参数是用来控制访问数组数据的。在你访问它之前,你必须锁定数据。通过跟踪该计数器,系统可以在不需要该数组时安全地删除它。 

创建SAFEARRAY

  创建一个单维SAFEARRAY的简单方法是通过使用SafeArrayCreateVector API函数。该函数可分配一个特定大小的连续内存块。

  SAFEARRAY *psa;

  //  create a safe array to store the stream data

  //  llen is the number of bytes in the array.

  psa = SafeArrayCreateVector( VT_UI1, 0, llen );

   SafeArrayCreateVector API创建一个SAFEARRAY,并且返回一个指向它的指针。首个参数用来定义数组的类型--它可以是任何有效的变量数据类型。为了传送一个串行化的对 象,我们将使用最基本的类型--一个非负的字节数组。VT--UI1代表非负整形的变量类型,1个字节。

  常数\'0\'定义数组的下界;在C++中,通常为0。最后的参数llen定义数组元素的个数。在我们的例子中,这与我们将要传送对象的字节数是一样的。我们还没有提数组大小(llen)是怎样来的,这将在我们重新考查串行化时提及。

  在你访问SAFEARRAY数据之前,你必须调用SafeArrayAccessData。该函数锁定数据并且返回一个指针。在这里,锁定数组意味着增加该数组的内部计数器(cLocks)。

  //  define a pointer to a byte array

  unsigned char *pData = NULL;

  SafeArrayAccessData( psa, (void**)&pData );

   ... use the safe array

  SafeArrayUnaccessData(psa);

  相应用来释放数据的函数是SafeArrayUnaccessData(),该功能释放该参数的计数。



Powered by ScribeFire.

posted @ 2008-02-13 16:08 kenlistian 阅读(17500) | 评论 (0)编辑 收藏

 采用mysql数据库中作为vc的连接数据库,当采用ado来处理时,只需要按下面步骤即可。
1.下载mysql odbc,or mysql ole,由于俺下了个mysql ole出了错,偷懒便直接下了mysql odbc,
    版本必须是3.51.11以上的,版本以下可能就出错。
2.安装后,采用odbc建立一个连接的mysql数据连接,通过udl文件取出其中的字符串。
3.替换在vc中连接ado的数据的字符串方法,即可正常连接。

采用myodbc的字符串如下:
 CString strConn = "Provider=MSDASQL.1;Password=zcc123;Persist Security Info=True;\
            User ID=root;Data Source=test";


 测试代码附在:http://www.cppblog.com/Files/kenlistian/mysql_test.rar
   


Powered by ScribeFire.

posted @ 2008-02-12 17:03 kenlistian 阅读(5039) | 评论 (0)编辑 收藏

  1 #include <list>
  2 
  3 using namespace std;
  4 
  5 class CYException
  6 {
  7 public:
  8     CString sMsg;
  9     int nError;
 10     CYException(int nErr,char* sErrMsg){
 11         nError = nErr;
 12         sMsg  = sErrMsg;
 13     }
 14     virtual ~CYException(){}
 15 
 16 };
 17 
 18 template   <class  T> 
 19 class   CYQueueSyn 
 20 
 21   public
 22       std::list<T>   queue; 
 23       int   m_outtime; 
 24       HANDLE   m_readSem; 
 25       HANDLE   m_writeSem; 
 26       HANDLE   m_synObject; 
 27   public
 28       CYQueueSyn(int length,int  outtime = INFINITE); 
 29       virtual   ~CYQueueSyn(void); 
 30   public
 31       void   inq(T type); 
 32       T   outq(); 
 33 
 34       void   free();       
 35 }; 
 36 ////////////////////////////////////////////////////////
 37 template<class   T> 
 38 void   CYQueueSyn<T>::free() 
 39 
 40     int   size = (int)queue.size(); 
 41     for(int i = 0; i<size; i++
 42         delete queue.back(); 
 43     queue.clear(); 
 44 
 45 template<class T> 
 46 CYQueueSyn<T>::CYQueueSyn(int   length,int   outtime) 
 47 
 48     m_readSem = CreateSemaphore(NULL,0,length,NULL); 
 49     m_writeSem = CreateSemaphore(NULL,length,length,NULL); 
 50     m_synObject = ::CreateMutex(NULL,false,NULL); 
 51 
 52     m_outtime = outtime;
 53 
 54 template<class T> 
 55 CYQueueSyn<T>::~CYQueueSyn() 
 56 
 57     CloseHandle(m_readSem); 
 58     CloseHandle(m_writeSem); 
 59     CloseHandle(m_synObject); 
 60 
 61 
 62 template<class T> 
 63 void   CYQueueSyn<T>::inq(T type)   throw(CYException) 
 64 
 65     int  rVal = WaitForSingleObject(m_writeSem,m_outtime); 
 66     if(rVal == WAIT_OBJECT_0) 
 67     { 
 68         if(WaitForSingleObject(m_synObject,INFINITE) == WAIT_OBJECT_0) 
 69         { 
 70             queue.push_back(type); 
 71             ReleaseMutex(m_synObject); 
 72         } 
 73     } 
 74     else if(rVal == WAIT_TIMEOUT) 
 75     { 
 76         throw CYException(1,"TIMEOUT"); 
 77     } 
 78     else if(rVal == WAIT_ABANDONED) 
 79     { 
 80         throw CYException(-1"ERROR"); 
 81     } 
 82     ReleaseSemaphore(m_readSem,1,NULL); 
 83 
 84 template<class T> 
 85 T  CYQueueSyn<T>::outq()   throw(CYException) 
 86 
 87     T  value; 
 88     int rVal = WaitForSingleObject(m_readSem,m_outtime); 
 89     if(rVal == WAIT_OBJECT_0) 
 90     { 
 91         if(WaitForSingleObject(m_synObject,INFINITE) == WAIT_OBJECT_0) 
 92         { 
 93             value = queue.front(); 
 94             queue.pop_front(); 
 95             ReleaseMutex(m_synObject); 
 96         } 
 97     } 
 98     else if(rVal == WAIT_TIMEOUT) 
 99     { 
100         throw CYException(1,"TIMEOUT"); 
101     } 
102     else if(rVal == WAIT_ABANDONED) 
103     { 
104         throw CYException(-1,"ERROR"); 
105     } 
106 
107     ReleaseSemaphore(m_writeSem,1,NULL); 
108     return   value; 
109 
110 
111 #endif 

如上,测试代码下载:http://www.pudn.com/downloads97/sourcecode/windows/other/109201257cqueue.rar
posted @ 2008-01-15 15:13 kenlistian 阅读(282) | 评论 (0)编辑 收藏

SDN,LPCTSTR:   A   32-bit   pointer   to   a   constant   character   string 
BOOL               A   Boolean   value.
BSTR               A   32-bit   character   pointer.
BYTE               An   8-bit   integer   that   is   not   signed.
COLORREF        A   32-bit   value   used   as   a   color   value.
DWORD            A   32-bit   unsigned   integer   or   the   address   of   a   segment   and   its   associated   offset.
LONG              A   32-bit   signed   integer.
LPARAM           A   32-bit   value   passed   as   a   parameter   to   a   window   procedure   or   callback   function.
LPCSTR           A   32-bit   pointer   to   a   constant   character   string.
LPSTR             A   32-bit   pointer   to   a   character   string.
LPCTSTR         A   32-bit   pointer   to   a   constant   character   string   that   is   portable   for   Unicode   and   DBCS.
LPTSTR          A   32-bit   pointer   to   a   character   string   that   is   portable   for   Unicode   and   DBCS.
LPVOID           A   32-bit   pointer   to   an   unspecified   type.
LRESULT         A   32-bit   value   returned   from   a   window   procedure   or   callback   function.
UINT          A   16-bit   unsigned   integer   on   Windows   versions   3.0   and   3.1;   a   32-bit   unsigned   integer   on   Win32.
WNDPROC   A   32-bit   pointer   to   a   window   procedure.
WORD        A   16-bit   unsigned   integer.
WPARAM     A   value   passed   as   a   parameter   to   a   window   procedure   or   callback   function:   16   bits   on   Windows   versions   3.0   and   3.1;   32   bits   on   Win32.  
Data   types   unique   to   the   Microsoft   Foundation   Class   Library   include   the   following:  
POSITION      A   value   used   to   denote   the   position   of   an   element   in   a   collection;   used   by   MFC   collection   classes.
LPCRECT       A   32-bit   pointer   to   a   constant   (nonmodifiable)   RECT   structure.  

posted @ 2008-01-11 11:28 kenlistian 阅读(425) | 评论 (0)编辑 收藏

在vc6中编译一个MFC程序时其中有段代码在创建数据库连接报错。

  其源码如下:

   其中声明

   _ConectPtr m_pConn,在函数中

    HRESULT hr = m_pConn.CreateInstance(__uuidof(Connection));  //_T("ADODB.Connection"));
    if(FAILED(hr))
    {       
          return false;
    }

   后来在检查,发现需要调用AfxOleInit()初始化组件即可解决。而我在文件中是加入了::CoInitialize(),但是在实际运行中该函数并没有起到作用。

而在所宣言中,AfxOleInit包含了CoInitialize()。但是为啥AfxOleInit能解决数据连接的CreateInterface问题呢?观察

AfxOleInit的源码。
--------------------------------------------------------------------------------  
BOOL AFXAPI AfxOleInit()
{
    _AFX_THREAD_STATE* pState = AfxGetThreadState();
    ASSERT(!pState->m_bNeedTerm);    // calling it twice?

    // Special case DLL context to assume that the calling app initializes OLE.
    // For DLLs where this is not the case, those DLLs will need to initialize
    // OLE for themselves via OleInitialize.  This is done since MFC cannot provide
    // automatic uninitialize for DLLs because it is not valid to shutdown OLE
    // during a DLL_PROCESS_DETACH.
    if (afxContextIsDLL)
    {
        pState->m_bNeedTerm = -1;  // -1 is a special flag
        return TRUE;
    }

    // first, initialize OLE
    SCODE sc = ::OleInitialize(NULL);    //该句子做初始化ole
    if (FAILED(sc))
    {
        // warn about non-NULL success codes
        TRACE1("Warning: OleInitialize returned scode = %s.\n",
            AfxGetFullScodeString(sc));
        goto InitFailed;
    }
    // termination required when OleInitialize does not fail
    pState->m_bNeedTerm = TRUE;

    // hook idle time and exit time for required OLE cleanup
    CWinThread* pThread; pThread = AfxGetThread();
    pThread->m_lpfnOleTermOrFreeLib = AfxOleTermOrFreeLib;

    // allocate and initialize default message filter
    if (pThread->m_pMessageFilter == NULL)
    {
        pThread->m_pMessageFilter = new COleMessageFilter;
        ASSERT(AfxOleGetMessageFilter() != NULL);
        AfxOleGetMessageFilter()->Register();
    }
    return TRUE;

InitFailed:
    AfxOleTerm();
    return FALSE;
}

可见,AfxOleInit()是封装了OleInitialize()来初始化com组件;
查询网上资料说:OleInitialize内部调用了CoInitialize 。在OleInitialize比ConInitialize多了以下支持:  
  Clipboard    
  Drag   and   drop    
  Object   linking   and   embedding   (OLE)    
  In-place   activation    
  如果你不需要这些附加功能,就用CoInitialize或CoInitializeEx。

但是在本人程序中调用CoInitialize不行而AfxOleInit可以,难道OleInitialize中创建的所附几个在ADO com控件上用到,但是在写了一个控制台上的ado程序,调用CoInitialize却是可行的。其中细节和玄妙打个标记,容以后在研究吧,偷懒了,如果发现CoInitalize不行就用AfxOleInit吧。

0-----

汗颜:因为在app的InitInstance中的粗心末尾加了一句::UnCoInitialize(),造成coinitalize错误。实际上2个函数都可以初始化ado组件。mark。

posted @ 2008-01-09 16:38 kenlistian 阅读(6075) | 评论 (2)编辑 收藏

在VC6中编译c++程序时报该错原因就是需要一个预编译的文件stdafx.h,如果不需要编译该头文件的话,只需要打开seting 页面设置中

在c/c++栏,选择PreCompiled headers,然后设置第一选项,选择不使用预编译头,解决这个问题。

or

直接打开dsp工程文件,找到 /Yu"stdafx.h"去掉即可。

 

一般来说,在编译某个文件时,往往发现编译器指向某个源码文件的末尾,并且提示以上信息时,直接在cpp顶上加一项"stdafx.h".

而且不管文件所处是否和stdafx.h文件在同一目录中。编译后即可通过,没必要去修改编译开关。一般而言:预编译功能是解决大量标头文件重复编译的问题。

在stdafx.h头中,我们一般把那些公用的头文件放置在其中。特别是调用dll组件包时,dll引入到工程中时。

posted @ 2008-01-09 15:49 kenlistian 阅读(7713) | 评论 (0)编辑 收藏

在MFC中,有CMap相关的map类, 在 map中有map。

// Maps (aka Dictionaries)
class CMapWordToOb;         // map from WORD to CObject*
class CMapWordToPtr;        // map from WORD to void*
class CMapPtrToWord;        // map from void* to WORD
class CMapPtrToPtr;         // map from void* to void*

// Special String variants
class CMapStringToPtr;      // map from CString to void*
class CMapStringToOb;       // map from CString to CObject*
class CMapStringToString;   // map from CString to CString

 

对于以上可以直接用stl中的一个map类型全部概况。

map<Word, CObject*>

map<Word, void*>

map<void*, Word>

map<void*, void*>

map<CString,void*>

map<CString, CObject*>

map<CString, CString>

由此,还是map简练。

再看遍历时,各个处理如下:

CMap<DWORD, DWORD&, ThreadInfo, ThreadInfo&> m_threadMap;   //定义一组线程map

则在

ThreadInfo info;

DWORD Key ;
POSITION pos = threadMap.GetStartPosition();
while (pos != NULL)
{

    threadMap.GetNextAssoc(pos, Key, info);
    {
     。。。
    }
}

如果采用std中的map处理则

map<DWORD ,ThreadInfo>m_threadMap;

map<DWORD ,ThreadInfo>::iteraotr pIt;

for(pIt = m_threadMap.begin(); pIt != m_threadMap.end(); pIt++){

     ......

}

 

简单明了,胜过用mfc中繁芜而又不清晰的过程。所以多用std做程序开发比用一些过时的要好得多。

何须浪费时间去知道几个茴字的写法呢。是不是。

posted @ 2008-01-08 12:45 kenlistian 阅读(4045) | 评论 (1)编辑 收藏

 

/*
  查询文件,返回CStringList
*/
void FindFileInDir(CString szPath, CStringList& m_List)
{  
    //CStringList m_List;      //可以用vector来取代

    WIN32_FIND_DATA FindData;//查找文件必须的变量
    HANDLE hFile;
    szPath += "\\*";
    hFile = FindFirstFile((LPCSTR)szPath, &FindData);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        return;
    }
    szPath.Delete (szPath.GetLength()-2,2);
    m_List.AddTail(szPath);
    do
    {
        if(strcmp(FindData.cFileName, ".") && strcmp(FindData.cFileName, "..") )
        {
            if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                CString s = szPath;
                s +="\\";
                s += FindData.cFileName;
                FindFileInDir(s,m_List);
            }
            else
            {
                CString s=szPath;
                s += "\\";
                s += FindData.cFileName;
                m_List.AddTail(s);
            }
        }
    }while(FindNextFile(hFile, &FindData));
    FindClose(hFile);
}

posted @ 2008-01-07 11:47 kenlistian 阅读(149) | 评论 (0)编辑 收藏

    一般来说,和数据库连接通常不容易断链,但是特别做些服务性质的,无人监守的程序时,则要考虑数据库连接的ado发生断链时的情况。在这种情况下,考虑建立数据库链路检测异常保护就是非常重要的。

通常,与数据库连接中断有以下2种情况:

1。数据库服务器服务停止,
2。与数据库服务器之间的网络发生中断


采用如下代码在使用做pRs做open查询操作时,抛出异常,此异常可判断为到数据库服务器的连接发生中断
Error   number:   80004005
[DBNETLIB][ConnectionWrite (WrapperWrite()).]一般性网络错误。请检查网络文档。
SQLState   :   08S0
NativeError   :   b

当pConn在Close,重新Open连接会发生异常,此异常是重新连接时抛出的。此时可判断为到远程服务器的连接完全中断
Error   number:   80004005
[DBNETLIB][ConnectionOpen (Connect()).]SQL Server 不存在或拒绝访问。
SQLState   :   08001
NativeError   :   11

检测代码基本如下,此代码的部分参考自ADO手册,并加以部分的修改
for(;;)
{
    try {
        if   (g_isConnected) {
                pRs-> Open(_bstr_t(“select 1”),  _variant_t((IDispatch*)pConn,true), adOpenStatic,
                                   adLockReadOnly, adCmdText);
                if (pRs)
                    if   (pRs-> State == adStateOpen)
                          pRs-> Close();
        } else {
             if   (pConn)
                 if   (pConn->State   ==   adStateOpen)
                        pConn->Close();
             pConn->Open(_bstr_t(strCnn),_bstr_t(""),_bstr_t(""),adModeUnknown);   //重新打开连接
             isConnected=TRUE;
        }
   }
   catch   (_com_error   &pCome)
   {

          _variant_t   vtConnect   =   (_variant_t)(pConn.GetInterfacePtr());//pRs-> GetActiveConnection();
          switch(vtConnect.vt)
         {
                case   VT_BSTR:
                        PrintComError(pCome);
                        break;
                case   VT_DISPATCH:
                         PrintProviderError(vtConnect);
                         break;
               default:
                     printf( "Errors   occured. ");
                    break;
           }
           isConnected=   FALSE;
     }
     Sleep(1000);
}
void   PrintProviderError(_ConnectionPtr   pConnection)
{
             ErrorPtr     pErr     =   NULL;
             if(   (pConnection-> Errors-> Count)   >   0)
            {
                    long   nCount   =   pConnection-> Errors-> Count;
                   for(long   i   =   0;   i   <   nCount;   i++)
                   {
                          pErr   =   pConnection-> Errors-> GetItem(i);
                          TRACE( "Error   number:   %x\n%s\n ",   pErr-> Number,  (LPCSTR)pErr-> Description);
                          TRACE( "SQLState   :   %s\n ",   (LPCTSTR)pErr-> SQLState);
                          TRACE( "NativeError   :   %x\n ",   pErr-> NativeError);
                  }
            }
}

void   PrintComError(_com_error   &e)
{
    _bstr_t   bstrSource(e.Source());
   _bstr_t   bstrDescription(e.Description());
   //   Print   Com   errors.    
    TRACE( "Error\n ");
    TRACE( "\tCode   =   %08lx\n ",   e.Error());
    TRACE( "\tCode   meaning   =   %s\n ",   e.ErrorMessage());
    TRACE( "\tSource   =   %s\n ",   (LPCSTR)   bstrSource);
    TRACE( "\tDescription   =   %s\n ",   (LPCSTR)   bstrDescription);
}

 

把它整理成到一个线程中不断作为数据库链路检测,发现该连接断开则返回连接来处理。测试代码附文。我用的sql server数据库做测试过。源码下载位置:

http://www.pudn.com/downloads95/sourcecode/database/detail389188.html

posted @ 2008-01-06 00:03 kenlistian 阅读(818) | 评论 (0)编辑 收藏

部分载自:http://www.tongyi.net/develop/mfc/1003840.html

CArchive类是使用了缓冲区,即一段内存空间作为临时数据存储地,对CArchive的读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储煤质。

一般来说,CArchive封装了对该缓冲区的操作,它的好处是可以少划动硬盘以及提高运行速度。不过对于使用MFC来说,是一个很好的封装。看看其对读写的操作,可见在实际编程上是非常容易出错的。对于这样的类,一定要封装好,作为一个公用函数或公用类调用,否则在实际中像c那样散在代码的各个地方的话,恐怕有时头会大。

有时我想,如果编程在这些地方磨洋工的话,恐怕一个大的工程软件写起来得好长时间。所以说尽量已组件的方式,类的思路来组合前人的算法,技巧也是一种编程的进步。

 

所下列看其对指针的娴熟操作(也是往往出错的啊),

例如双字的插入(写)

CArchive& CArchive::operator<<(DWORD dw)
{
   if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax) //缓冲区空间不够
       Flush();                                  //缓冲区内容提交到实际存储煤质。
   if (!(m_nMode & bNoByteSwap))
     _AfxByteSwap(dw, m_lpBufCur);  //处理字节顺序
   else
     *(DWORD*)m_lpBufCur = dw;      //添入缓冲区
   m_lpBufCur += sizeof(DWORD);     //移动当前指针
   return *this;
}

双字的提取(读)

CArchive& CArchive::operator>>(DWORD& dw)
{
  if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax) //缓冲区要读完了
      FillBuffer(sizeof(DWORD) - (UINT)(m_lpBufMax - m_lpBufCur));  //重新读入内容到缓冲区
   dw = *(DWORD*)m_lpBufCur;  //读取双字
   m_lpBufCur += sizeof(DWORD); //移动当前位置指针
   if (!(m_nMode & bNoByteSwap))
     _AfxByteSwap(dw, (BYTE*)&dw);  //处理字节顺序
   return *this;
}

 

内部指针

缓冲区指针 BYTE* m_lpBufStart,指向缓冲区,这个缓冲区有可能是底层CFile(如派生类CMemFile)对象提供的,但一般是CArchive自己建立的。

缓冲区尾部指针 BYTE* m_lpBufMax;

缓冲区当前位置指针 BYTE* m_lpBufCur;

初始化时,读模式,当前位置在尾部,是写模式,当前位置在头部:

m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;

 

 

这里粘贴一个日志文件的类文件中使用CArchive作为内部操作的例子吧。利用CArchive去Flush,Write,Open,这里先把CArchive的参量列出:

 

CArchive(CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf = NULL);

 

class CLogFile
{
protected:
    int     m_nCachBuffSize;
    int     m_nCurentBuffSize;
    char *  m_lpCachBuff;
    CString m_fileName;            

    CArchive  *m_pArchive;                 //作为文件读写到内存的实际操作。
    CFile     m_file;

    CRITICAL_SECTION m_csFile;
    SYSTEMTIME  m_tmCurTime;         //当前时间
    SYSTEMTIME    m_tmLastTime;      //上次日志时间
public:
    void Open(char *fileName);
    void Close();

    void Wirte(char * buff ,int size=0);             
    void WriteLog(char *szLog, int size =0);
    void Flush();

    CLogFile() ;
    ~CLogFile() ;
};

 

 

***************************************************

#include "stdafx.h"
#include "LogFile.h"
#include "pubfunc.h"

CLogFile :: CLogFile()
{
    ::InitializeCriticalSection(&m_csFile);
    m_pArchive = NULL;
    m_fileName = "";
    m_lpCachBuff = new char[MAX_LOG_BUFF + 1 ];             //作为内存缓冲区
    m_nCachBuffSize = MAX_LOG_BUFF ;
    m_nCurentBuffSize = 0;
}

CLogFile :: ~CLogFile()
{
    if(m_pArchive)
    {
        m_pArchive->Flush();
        m_file.Close();
        delete m_pArchive;
        m_pArchive= NULL;
    }
    if(m_lpCachBuff)
    {
        delete []m_lpCachBuff;
    }
    ::DeleteCriticalSection(&m_csFile);
}

void CLogFile::WriteLog(char *szLog, int size)
{
    if (size == 0)
        size = strlen(szLog);
    Wirte(szLog , size);
}


void CLogFile::Flush()
{
    EnterCriticalSection(&m_csFile);
    if(m_pArchive)
    {
        m_pArchive->Flush();
        m_nCurentBuffSize=0;
    }
    LeaveCriticalSection(&m_csFile);
}


void CLogFile::Wirte(char * buff ,int size)
{
    EnterCriticalSection(&m_csFile);
    GetLocalTime(&m_tmCurTime);
    char szTime[128]={0};
    int  timelen = sprintf(szTime,"\r\n%04d-%02d-%02d %02d:%02d:%02d\r\n\r\n",m_tmCurTime.wYear,m_tmCurTime.wMonth,m_tmCurTime.wDay,m_tmCurTime.wHour,m_tmCurTime.wMinute,m_tmCurTime.wSecond);
    if (m_tmCurTime.wYear != m_tmLastTime.wYear
        ||m_tmCurTime.wMonth != m_tmLastTime.wMonth
        ||m_tmCurTime.wDay != m_tmLastTime.wDay)
    {
        if(m_pArchive)
        {
            m_pArchive->Flush();
            m_file.Close();
        }

        char szFile[256],szPath[256];
        GetLocalTime(&m_tmLastTime);
        //GetAppPath(szPath);

        szPath = "自己抓取当前目录吧,出错我不管了";
        if(m_pArchive)
        {
            delete m_pArchive;
        }
        if(m_fileName.IsEmpty())
        {
            sprintf(szFile,"%s\\Log\\%d-%02d-%02d_%s.log",szPath,m_tmLastTime.wYear,m_tmLastTime.wMonth,m_tmLastTime.wDay,(LPTSTR)(LPCTSTR)m_fileName);
        }else
        {
            sprintf(szFile,"%s\\Log\\%d-%02d-%02d.log",szPath,m_tmLastTime.wYear,m_tmLastTime.wMonth,m_tmLastTime.wDay);
        }

        if(m_file.Open(szFile,CFile::modeCreate|CFile::modeNoTruncate|CFile::modeReadWrite|CFile::shareDenyNone,0))
        {
            m_file.SeekToEnd();
            m_pArchive = new CArchive(&m_file,CArchive::store,m_nCachBuffSize,m_lpCachBuff);
            int totalsize = timelen + size ;
            if( totalsize > ( m_nCachBuffSize - m_nCurentBuffSize)  )
            {  
                m_pArchive->Flush();
                m_pArchive->Write( szTime ,timelen );
                m_pArchive->Flush();
                for ( int i =0 ; i < totalsize/m_nCachBuffSize;i++)
                {
                    m_pArchive->Write(buff+m_nCachBuffSize * i ,m_nCachBuffSize);
                    m_pArchive->Flush();
                }
                m_nCurentBuffSize = totalsize - m_nCachBuffSize * (totalsize/m_nCachBuffSize);
                m_pArchive->Write(buff+m_nCachBuffSize * (totalsize/m_nCachBuffSize) * i ,m_nCurentBuffSize);
                m_lpCachBuff[m_nCurentBuffSize+1] = 0;
            }
            else
            {
                m_pArchive->Write( szTime ,timelen );
                m_pArchive->Write( buff   ,size );
                m_nCurentBuffSize=m_nCurentBuffSize+ totalsize;
                m_lpCachBuff[m_nCurentBuffSize+1]=0;
            }
        }
        else
        {
            m_pArchive = NULL;
        }   
    }   
    else
    {//续写log文件
        if(m_pArchive)
        {
            int totalsize = timelen + size ;
            if( totalsize > ( m_nCachBuffSize -m_nCurentBuffSize) )
            {  
                m_pArchive->Flush();
                m_pArchive->Write( szTime ,timelen );
                m_pArchive->Flush();
                for ( int i =0 ; i < totalsize/m_nCachBuffSize;i++)
                {
                    m_pArchive->Write(buff+m_nCachBuffSize * i ,m_nCachBuffSize);
                    m_pArchive->Flush();
                }
                m_nCurentBuffSize = totalsize - m_nCachBuffSize * (totalsize/m_nCachBuffSize);
                m_pArchive->Write(buff+m_nCachBuffSize * (totalsize/m_nCachBuffSize) * i ,m_nCurentBuffSize);
                m_lpCachBuff[m_nCurentBuffSize + 1]=0;
            }
            else
            {
                m_pArchive->Write( szTime ,timelen );
                m_pArchive->Write( buff, size );
                m_nCurentBuffSize = m_nCurentBuffSize + totalsize;
                m_lpCachBuff[m_nCurentBuffSize+1]=0;
            }       
        }
    }

       LeaveCriticalSection(&m_csFile);
}

/*
  fileName    文件名称  
*/
void CLogFile::Open( char *fileName)
{
    m_nCurentBuffSize = 0;
    ::InitializeCriticalSection(&m_csFile);
    char szFile[256],szPath[256];
    GetLocalTime(&m_tmLastTime);

    // GetAppPath(szPath);

   szPath = "把szPath修改为类变量吧,然后找个获取当前目录的函数,改动下吧,白给你用这个类不看看也是不行的"

    if(fileName == 0 || fileName[0] == 0)
    {
        sprintf(szFile,"%s\\Log\\%d-%02d-%02d.log",szPath,m_tmLastTime.wYear,m_tmLastTime.wMonth,m_tmLastTime.wDay);
    }
    else
    {
        m_fileName = fileName;
        sprintf(szFile,"%s\\Log\\%d-%02d-%02d_%s.log",szPath,m_tmLastTime.wYear,m_tmLastTime.wMonth,m_tmLastTime.wDay,fileName);
    }
    if(m_file.Open(szFile,CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite|CFile::shareDenyNone,0))
    {
        m_pArchive = new CArchive(&m_file,CArchive::store,m_nCachBuffSize, m_lpCachBuff);
    }
    else
    {
        m_pArchive = NULL;
    }
}

void CLogFile::Close()
{
    if(m_pArchive)
    {
        m_pArchive->Flush();
        m_file.Close();
        delete m_pArchive;
        m_pArchive= NULL;
    }
    m_nCurentBuffSize = 0;
}

posted @ 2008-01-05 17:27 kenlistian 阅读(6441) | 评论 (0)编辑 收藏

//   Module   Name:   overlap.cpp  
  //         This   sample   illustrates   how   to   develop   a   simple   echo   server   Winsock  
  //         application   using   the   Overlapped   I/O   model   with   event   notification.   This    
  //         sample   is   implemented   as   a   console-style   application   and   simply   prints  
  //         messages   when   connections   are   established   and   removed   from   the   server.  
  //         The   application   listens   for   TCP   connections   on   port   5150   and   accepts   them  
  //         as   they   arrive.   When   this   application   receives   data   from   a   client,   it  
  //         simply   echos   (this   is   why   we   call   it   an   echo   server)   the   data   back   in  
  //         it's   original   form   until   the   client   closes   the   connection.  
  //  
  //   Compile:  
  //  
  //         cl   -o   overlap   overlap.cpp   ws2_32.lib  
  //  
  //         Note:   There   are   no   command   line   options   for   this   sample.  
  #include   <winsock2.h>  
  #include   <windows.h>  
  #include   <stdio.h>  
  #define   PORT   5150  
  #define   DATA_BUFSIZE   8192  
  typedef   struct   _SOCKET_INFORMATION   {  
        CHAR   Buffer[DATA_BUFSIZE];  
        WSABUF   DataBuf;  
        SOCKET   Socket;  
        WSAOVERLAPPED   Overlapped;  
        DWORD   BytesSEND;  
        DWORD   BytesRECV;  
  }   SOCKET_INFORMATION,   *   LPSOCKET_INFORMATION;  
  DWORD   WINAPI   ProcessIO(LPVOID   lpParameter);  
  DWORD   EventTotal   =   0;  
  WSAEVENT   EventArray[WSA_MAXIMUM_WAIT_EVENTS];  
  LPSOCKET_INFORMATION   SocketArray[WSA_MAXIMUM_WAIT_EVENTS];  
  CRITICAL_SECTION   CriticalSection;        
  void   main(void)  
  {  
        WSADATA   wsaData;  
        SOCKET   ListenSocket,   AcceptSocket;  
        SOCKADDR_IN   InternetAddr;  
        DWORD   Flags;  
        DWORD   ThreadId;  
        DWORD   RecvBytes;  
        INT   Ret;  
        InitializeCriticalSection(&CriticalSection);  
        if   ((Ret   =   WSAStartup(0x0202,&wsaData))   !=   0)  
        {  
              printf("WSAStartup   failed   with   error   %d\n",   Ret);  
              WSACleanup();  
              return;  
        }  
        if   ((ListenSocket   =   WSASocket(AF_INET,   SOCK_STREAM,   0,   NULL,   0,    
              WSA_FLAG_OVERLAPPED))   ==   INVALID_SOCKET)    
        {  
              printf("Failed   to   get   a   socket   %d\n",   WSAGetLastError());  
              return;  
        }  
        InternetAddr.sin_family   =   AF_INET;  
        InternetAddr.sin_addr.s_addr   =   htonl(INADDR_ANY);  
        InternetAddr.sin_port   =   htons(PORT);  
        if   (bind(ListenSocket,   (PSOCKADDR)   &InternetAddr,   sizeof(InternetAddr))   ==   SOCKET_ERROR)  
        {  
              printf("bind()   failed   with   error   %d\n",   WSAGetLastError());  
              return;  
        }  
        if   (listen(ListenSocket,   5))  
        {  
              printf("listen()   failed   with   error   %d\n",   WSAGetLastError());  
              return;  
        }  
        //   Setup   the   listening   socket   for   connections.  
        if   ((AcceptSocket   =   WSASocket(AF_INET,   SOCK_STREAM,   0,   NULL,   0,  
              WSA_FLAG_OVERLAPPED))   ==   INVALID_SOCKET)    
        {  
              printf("Failed   to   get   a   socket   %d\n",   WSAGetLastError());  
              return;  
        }  
        if   ((EventArray[0]   =   WSACreateEvent())   ==   WSA_INVALID_EVENT)  
        {  
              printf("WSACreateEvent   failed   with   error   %d\n",   WSAGetLastError());  
              return;  
        }  


        //   Create   a   thread   to   service   overlapped   requests  
        if   (CreateThread(NULL,   0,   ProcessIO,   NULL,   0,   &ThreadId)   ==   NULL)  
        {  
              printf("CreateThread   failed   with   error   %d\n",   GetLastError());  
              return;  
        }    
        EventTotal   =   1;  
        while(TRUE)  
        {  
               // 服务端阻塞在这里等待
              if   ((AcceptSocket   =   accept(ListenSocket,   NULL,   NULL))   ==   INVALID_SOCKET)  
              {  
                      printf("accept   failed   with   error   %d\n",   WSAGetLastError());  
                      return;  
              }  
              EnterCriticalSection(&CriticalSection);  
              //   Create   a   socket   information   structure   to   associate   with   the   accepted   socket.  
              if   ((SocketArray[EventTotal]   =   (LPSOCKET_INFORMATION)   GlobalAlloc(GPTR,  
                    sizeof(SOCKET_INFORMATION)))   ==   NULL)  
              {  
                    printf("GlobalAlloc()   failed   with   error   %d\n",   GetLastError());  
                    return;  
              }    
              //   Fill   in   the   details   of   our   accepted   socket.  
              SocketArray[EventTotal]->Socket   =   AcceptSocket;  
              ZeroMemory(&(SocketArray[EventTotal]->Overlapped),   sizeof(OVERLAPPED));  
              SocketArray[EventTotal]->BytesSEND   =   0;  
              SocketArray[EventTotal]->BytesRECV   =   0;  
              SocketArray[EventTotal]->DataBuf.len   =   DATA_BUFSIZE;  
              SocketArray[EventTotal]->DataBuf.buf   =   SocketArray[EventTotal]->Buffer;  
              if   ((SocketArray[EventTotal]->Overlapped.hEvent   =   EventArray[EventTotal]   =    
                      WSACreateEvent())   ==   WSA_INVALID_EVENT)  
              {  
                    printf("WSACreateEvent()   failed   with   error   %d\n",   WSAGetLastError());  
                    return;  
              }  
              //   Post   a   WSARecv   request   to   to   begin   receiving   data   on   the   socket  
              Flags   =   0;  
              if   (WSARecv(SocketArray[EventTotal]->Socket,    
                    &(SocketArray[EventTotal]->DataBuf),   1,   &RecvBytes,   &Flags,  
                    &(SocketArray[EventTotal]->Overlapped),   NULL)   ==   SOCKET_ERROR)  
              {  
                    if   (WSAGetLastError()   !=   ERROR_IO_PENDING)  
                    {  
                          printf("WSARecv()   failed   with   error   %d\n",   WSAGetLastError());  
                          return;  
                    }  
              }  
              EventTotal++;  
              LeaveCriticalSection(&CriticalSection);  


              //   Signal   the   first   event   in   the   event   array   to   tell   the   worker   thread   to  
              //   service   an   additional   event   in   the   event   array  
              if   (WSASetEvent(EventArray[0])   ==   FALSE)  
              {  
                    printf("WSASetEvent   failed   with   error   %d\n",   WSAGetLastError());  
                    return;  
              }  
        }  
  } 

 

DWORD   WINAPI   ProcessIO(LPVOID   lpParameter)  
  {  
        DWORD   Index;  
        DWORD   Flags;  
        LPSOCKET_INFORMATION   SI;  
        DWORD   BytesTransferred;  
        DWORD   i;  
        DWORD   RecvBytes,   SendBytes;  
        //   Process   asynchronous   WSASend,   WSARecv   requests.  
        while(TRUE)  
        {  
              if   ((Index   =   WSAWaitForMultipleEvents(EventTotal,   EventArray,   FALSE,  
                    WSA_INFINITE,   FALSE))   ==   WSA_WAIT_FAILED)  
              {  
                    printf("WSAWaitForMultipleEvents   failed   %d\n",   WSAGetLastError());  
                    return   0;  
              }    
              //   If   the   event   triggered   was   zero   then   a   connection   attempt   was   made  
              //   on   our   listening   socket.  
              if   ((Index   -   WSA_WAIT_EVENT_0)   ==   0)  
              {  
                    WSAResetEvent(EventArray[0]);  
                    continue;  
              }  
              SI   =   SocketArray[Index   -   WSA_WAIT_EVENT_0];  
              WSAResetEvent(EventArray[Index   -   WSA_WAIT_EVENT_0]);  
              if   (WSAGetOverlappedResult(SI->Socket,   &(SI->Overlapped),   &BytesTransferred,  
                    FALSE,   &Flags)   ==   FALSE   ||   BytesTransferred   ==   0)  
              {  
                    printf("Closing   socket   %d\n",   SI->Socket);  
                    if   (closesocket(SI->Socket)   ==   SOCKET_ERROR)  
                    {  
                          printf("closesocket()   failed   with   error   %d\n",   WSAGetLastError());  
                    }  
                    GlobalFree(SI);  
                    WSACloseEvent(EventArray[Index   -   WSA_WAIT_EVENT_0]);  
                    //   Cleanup   SocketArray   and   EventArray   by   removing   the   socket   event   handle  
                    //   and   socket   information   structure   if   they   are   not   at   the   end   of   the  
                    //   arrays.  
                    EnterCriticalSection(&CriticalSection);  
                    if   ((Index   -   WSA_WAIT_EVENT_0)   +   1   !=   EventTotal)  
                          for   (i   =   Index   -   WSA_WAIT_EVENT_0;   i   <   EventTotal;   i++)  
                          {  
                                EventArray[i]   =   EventArray[i   +   1];  
                                SocketArray[i]   =   SocketArray[i   +   1];  
                          }  
                    EventTotal--;  
                    LeaveCriticalSection(&CriticalSection);  
                    continue;  
              }  
              //   Check   to   see   if   the   BytesRECV   field   equals   zero.   If   this   is   so,   then  
              //   this   means   a   WSARecv   call   just   completed   so   update   the   BytesRECV   field  
              //   with   the   BytesTransferred   value   from   the   completed   WSARecv()   call.  
              if   (SI->BytesRECV   ==   0)  
              {  
                    SI->BytesRECV   =   BytesTransferred;  
                    SI->BytesSEND   =   0;  
              }  
              else  
              {  
                    SI->BytesSEND   +=   BytesTransferred;  
              }  
              if   (SI->BytesRECV   >   SI->BytesSEND)  
              {  
                    //   Post   another   WSASend()   request.  
                    //   Since   WSASend()   is   not   gauranteed   to   send   all   of   the   bytes   requested,  
                    //   continue   posting   WSASend()   calls   until   all   received   bytes   are   sent.  
                    ZeroMemory(&(SI->Overlapped),   sizeof(WSAOVERLAPPED));  
                    SI->Overlapped.hEvent   =   EventArray[Index   -   WSA_WAIT_EVENT_0];  
                    SI->DataBuf.buf   =   SI->Buffer   +   SI->BytesSEND;  
                    SI->DataBuf.len   =   SI->BytesRECV   -   SI->BytesSEND;  
                    if   (WSASend(SI->Socket,   &(SI->DataBuf),   1,   &SendBytes,   0,  
                          &(SI->Overlapped),   NULL)   ==   SOCKET_ERROR)  
                    {  
                          if   (WSAGetLastError()   !=   ERROR_IO_PENDING)  
                          {  
                                printf("WSASend()   failed   with   error   %d\n",   WSAGetLastError());  
                                return   0;  
                          }  
                    }  
              }  
              else  
              {  
                    SI->BytesRECV   =   0;  
                    //   Now   that   there   are   no   more   bytes   to   send   post   another   WSARecv()   request.  
                    Flags   =   0;  
                    ZeroMemory(&(SI->Overlapped),   sizeof(WSAOVERLAPPED));  
                    SI->Overlapped.hEvent   =   EventArray[Index   -   WSA_WAIT_EVENT_0];  
                    SI->DataBuf.len   =   DATA_BUFSIZE;  
                    SI->DataBuf.buf   =   SI->Buffer;  
                    if   (WSARecv(SI->Socket,   &(SI->DataBuf),   1,   &RecvBytes,   &Flags,  
                          &(SI->Overlapped),   NULL)   ==   SOCKET_ERROR)  
                    {  
                          if   (WSAGetLastError()   !=   ERROR_IO_PENDING)  
                          {  
                                printf("WSARecv()   failed   with   error   %d\n",   WSAGetLastError());  
                                return   0;  
                          }  
                    }  
              }  
        }  
  } 

posted @ 2008-01-03 15:50 kenlistian 阅读(170) | 评论 (0)编辑 收藏

1.调试标记

  采用预处理#define标记,在代码中把调试部分使用#ifdef 和#endif 进行管理。

      当程序最终调试完成后,只需要使用#undef标记,调试代码就会消失。常用的调试标记为DEBUG, 语句序列:

  #define DEBUG 

  #ifdef DEBUG  

  调试代码 

  #endif

  2.宏定义方式。把变量和表达式转换成字符串

  可是使用字符串运算符来实现转换输出定义  

  #define PR(x) cout<<#x”=”<<x<<'\n'

  3.assert()

        断言:判断进入函数体的某个判断是对的。在c及其c++编译器中会产生测试该断言的代码,如果断言不为真,则发出一个错误信息告诉断言是什么以及它失败一会,程序会终止。在assert。h头文件中。在release中该断言失效。主要在debug版中使用。

posted @ 2007-12-29 15:58 kenlistian 阅读(198) | 评论 (0)编辑 收藏

1.采用类方式

class a2w
{
    wchar_t* buffer;
  public:
     explicit a2w(const char* str):buffer(0)
    {
       if(str) {
           size_t nu = strlen(str);
           size_t n = ::MultiByteToWideChar(CP_ACP,0,(const char *)str,int(nu),NULL,0);
           buffer = new wchar_t[n+1];
          ::MultiByteToWideChar(CP_ACP,0,(const char *)str,int(nu),buffer,int(n));
      }
    }
  ~a2w() {  delete[] buffer;  }

  operator const wchar_t*() { return buffer; }

};

备注:

   explicit:用来修饰类的构造函数,表明该构造函数是显式的。主要防止这种类构造时被编译器自动转换操作。

  

2.采用函数模式

/*

  wBuf 为申明指针即可。

*/

chr2wch(const char* buffer, wchar_t* wBuf)
{
      size_t len = strlen(buffer);

      size_t wlen = MultiByteToWideChar(CP_ACP, 0, (const char*)buffer, int(len), NULL, 0);

      wBuf = new wchar_t[wlen + 1];

      MultiByteToWideChar(CP_ACP, 0, (const_char*)buffer, int(len), wBuf, int(wlen));

}

posted @ 2007-12-29 15:48 kenlistian 阅读(6604) | 评论 (0)编辑 收藏

   在网络通讯中,socket处于阻塞模式运行时,其存在着超时处理。以下总结下在那些阻塞函数的处理方法。

这里摘抄一段描述阻塞函数的描述,非常到位。

所谓阻塞函数,是指其完成指定的任务之前不允许程序调用另一个函数,在Windows下还会阻塞本线程消息的发送。

所谓非阻塞函数,是指操作启动之后,如果可以立即得到结果就返回结果,否则返回表示结果需要等待的错误信息,不等待任务完成函数就返回。

首先,异步函数是非阻塞函数;

其次,获取远地信息的数据库函数是阻塞函数(因此,WinSock提供了其异步版本);

在Berkeley socket函数部分中,不涉及网络I/O、本地端工作的函数是非阻塞函数;

在Berkeley socket函数部分中,网络I/O的函数是可阻塞函数,也就是它们可以阻塞执行,也可以不阻塞执行。这些函数都使用了一个socket,如果它们使用的socket是阻塞的,则这些函数是阻塞函数;如果它们使用的socket是非阻塞的,则这些函数是非阻塞函数

    其实说明阻塞还是非阻塞也是我们可以设置相关。这里主要讲解下处于阻塞模式下的超时处理。

1.在我们直接调用socket创建时,如果不进行特意声明的话,创建的socket都是阻塞的。这样当我们调用accept,recv时,将有可能“block”,如果想设置为非阻塞,则方法有调用fcntl,select,WSAAsynSelect 来改变socket的阻塞

hsocket = socket(AF_INET, SOCK_STREAM, 0)

fcntl(hsocket, F_SETFL, 0_NONBLOCK);

 注意:

    其中fcntl是Unix系统环境中使用的,使用ioctl()函数和fcntl()函数实现对套接字的控制,而在Windows系统中则应使用ioctlsocket()函数。

 Ioctl和fcntl的区别是:

  ioctl   -   control   device  
  ioctl()   performs   a   variety   of   control   functions     on     devices  
            and   STREAMS.     For   non-STREAMS   files,   the   functions   performed  
            by   this   call   are   device-specific   control   functions.     request  
            and     an   optional   third   argument   with   varying   type   are   passed  
            to   the   file   designated   by   fildes   and   are   interpreted   by     the  
            device   driver.  
    The   fcntl()   function   provides   control   of   open   file   descriptors.   It   is   similar   to   ioctl().

   ( 这些带着unix的体味的函数,看着就头大。这个我是摘自某个文档,设置超时应该采用ioctlsocket。)

1.调用MFC的CAsyncSocket和CSocket类

  MFC提供了两个类CAsyncSocket和CSocket来封装WinSock API,

  CAsyncSocket在较低层次上封装了WinSock API,缺省情况下,使用该类创建的socket是非阻塞的socket,所有操作都会立即返回,如果没有得到结果,返回WSAEWOULDBLOCK,表示是一个阻塞操作。

  CSocket建立在CAsyncSocket的基础上,是CAsyncSocket的派生类。也就是缺省情况下使用该类创建的socket是非阻塞的socket,但是CSocket的网络I/O是阻塞的,它在完成任务之后才返回。CSocket的阻塞不是建立在“阻塞”socket的基础上,而是在“非阻塞”socket上实现的阻塞操作,在阻塞期间,CSocket实现了本线程的消息循环,因此,虽然是阻塞操作,但是并不影响消息循环,即用户仍然可以和程序交互。(即程序不会freeze)。

  其中设置超时函数如下:

CAsyncSocket::SetSockOpt(   int   nOptionName,   const   void*   lpOptionValue,   int   nOptionLen,   int   nLevel   =   SOL_SOCKET   );  
  nOptionName用SO_SNDTIMEO   or   SO_RCVTIMEO。  

2.采用select处理超时

int select(int nfds, fd_set FAR* readfds, fd_set FAR* writefds,fd_set FAR*exceptfds, const struct timeval FAR* timeout)

其中在windows中,其nfds可以可以设置为0,没有实际意义。

fd_set  fdR;
struct  timeval timeout = ..; //设置超时时间
...
for(;;) {
        FD_ZERO(&fdR);
        FD_SET(sockfd, &fdR);
        switch (select(sockfd + 1, &fdR, NULL, &timeout)) {
                case -1:
                    //错误,需要关闭端口。。。
                case 0:

                   //  timeout 处理

                default:
                        if (FD_ISSET(sockfd)) { 

                            //或读写操作或Accept()操作,按你设置处理。
                        }
        }
}

注意的是:

     由于Windows Sockets 某些函数在接口上虽然与Unix Sockets一致,但是它们的内部实现却不一样,例如,在函数select()的参数中,Unix Sockets实现套接字集合使用的是位掩码,但在Windows Sockets中却是使用一个SOCKET的数组。虽然套接字的集合仍由fd_set类型表示,但在Unix Sockets 源文件中直接修改fd_set结构的代码在Windows Sockets环境下将不能正常工作。故fd_set在微软中则采用FD_XXX宏处理。

3.采用WSAsyncSelect() 

   这个是消息事件模式来处理的,通过其中参数句柄返回一个消息,在自定义消息中处理。

 

4.采用setsockopt设置超时

int PASCAL FAR setsockopt( SOCKET s, int level, int optname,
const char FAR* optval, int optlen);

s:标识一个套接口的描述字。
level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:需设置的选项。
optval:指针,指向存放选项值的缓冲区。
optlen:optval缓冲区的长度。

     setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
  有两种套接口的选项:

      一种是布尔型选项,允许或禁止一种特性;

      一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。

        对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。

      SO_LINGER选项用于控制下述情况的行动:套接口上有排队的待发送数据,且closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。应用程序通过创建一个linger结构来设置相应的操作特性。

      这个setsockopt的水也太深了,对于一般运用大致运用就行,主要是其中参数太多,只需要了解几个就可以了,至于想了解更多,则读msdn吧,以及相关的文档吧,不过感觉这些东西纯粹就是个技术指标细节,不需要动脑筋却需要了解的一个方面,说白了是这些都是技术活而不是脑力活。

常见的命令:

//确定套接字自动读入的数据量

#define FIONREAD _IOR(''''f'''', 127, u_long) /* get # bytes to read */

//允许或禁止套接字的非阻塞模式,允许为非0,禁止为0

#define FIONBIO _IOW(''''f'''', 126, u_long) /* set/clear non-blocking i/o */

//确定是否所有带外数据都已被读入

#define SIOCATMARK _IOR(''''s'''', 7, u_long) /* at oob mark? */

设置接受超时

posted @ 2007-12-27 18:04 kenlistian 阅读(7539) | 评论 (0)编辑 收藏

     在网络通讯中,由于网络拥挤或一次发送的数据量过大等原因,经常会发生交换的数据在短时间内不能传送完,收发数据的函数因此不能返回,这种现象叫做阻塞。 Winsock对有可能阻塞的函数提供了两种处理方式:阻塞和非阻塞方式。

阻塞模式

     在阻塞方式下,收发数据的函数在被调用后一直要到传送完毕或者出错才能返回。在阻塞期间,被阻的函数不会断调用系统函数GetMessage()来保持消息循环的正常进行。

非阻塞模式
        将一个套接字置为非阻塞模式之后, Winsock API调用会立即返回。一般这些调用都会“失败”,并返回一个WSAEWOULDBLOCK。表明其操作在调用期间没有时间完成。如在系统的输入缓冲区中,并不存在等待的数据,那recv调用就会返回WSAEWOULDBLOCK错误。通常,我们需要重复调用同一个函数,直至获得一个成功返回代码。这不是一个好的方法。通常采用Winsock的套接字I/O模型去处理。

套接字I/O模型共有五种类型,如下:

  select(选择) 
  WSAAsyncSelect(异步选择)
  WSAEventSelect(事件选择)
  overlapped(重叠)
  completion port(完成端口)

 

*WSAAsyncSelect

      Winsock通过WSAAsyncSelect()自动地设置套接字处于非阻塞方式。使用WindowsSockets实现Windows网络程序设计的关键就是它提供了对网络事件基于消息的异步存取,用于注册应用程序感兴趣的网络事件。它请求Windows Sockets DLL在检测到套接字上发生的网络事件时,向窗口发送一个消息。

 int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);
hWnd:窗口句柄
wMsg:需要发送的消息
lEvent:事件(以下为事件的内容)
值: 含义:
FD_READ 期望在套接字上收到数据(即读准备好)时接到通知
FD_WRITE 期望在套接字上可发送数据(即写准备好)时接到通知
FD_OOB 期望在套接字上有带外数据到达时接到通知
FD_ACCEPT 期望在套接字上有外来连接时接到通知
FD_CONNECT 期望在套接字连接建立完成时接到通知
FD_CLOSE 期望在套接字关闭时接到通知

       进行异步选择使用WSAAsyncSelect()函数时,有以下几点需要引起特别的注意:
  .连续使用两次WSAAsyncSelect()函数时,只有第二次设置的事件有效,如:
           WSAAsyncSelect(s,hwnd,wMsg1,FD_READ);
           WSAAsyncSelect(s,hwnd,wMsg2,FD_CLOSE);
        这样只有当FD_CLOSE事件发生时才会发送wMsg2消息。
  .可以在设置过异步选择后通过再次调用WSAAsyncSelect(s,hwnd,0,0);的形式取消在套接字上所设置的异步事件。
  .Windows Sockets DLL在一个网络事件发生后,通常只会给相应的应用程序发送一个消息,而不能发送多个消息。但通过使用一些函数隐式地允许重发此事件的消息,这样就可能再次接收到相应的消息。
  .在调用过closesocket()函数关闭套接字之后不会再发生FD_CLOSE事件。


     对UDP协议,这些网络事件主要为:
      FD_READ   期望在套接字收到数据(即读准备好)时接收通知;
      FD_WRITE 期望在套接字可发送数(即写准备好)时接收通知;
    FD_CLOSE 期望在套接字关闭时接电通知
  消息变量wParam指示发生网络事件的套接字,变量1Param的低字节描述发生的网络事件,高字包含错误码。如在窗口函数的消息循环中均加一个分支:
int ok=sizeof(SOCKADDR);
case wMsg;
switch(1Param)
{
    case FD_READ:  //套接字上读数据 
    if(recvfrom(sr.lpPlayData[j],dwDataSize,0,(struct sockaddr FAR*)&there1,
     (int FAR*)&ok)==SOCKET_ERROR0) {
                MessageBox(hwnd,“数据接收失败!”,“”,MB_OK);
                return(FALSE);
       }
    case FD_WRITE:    //套接字上写数据
  }
break;

 

 

*WSAEventSelect
      事件通知模型要求在程序中针对使用的每个套接字创建一个事件对象,然后通过事件模式通知程序其套接字是否收到或发送的信息。一般来说这种模式,一般就是通过类似调用waitformultipleObject一样在一个线程中等待信号事件来,来了就处理。具体调用的函数如下:

    创建WSACreateEvent函数.该函数的返回值是一个创建好的事件对象句柄。事件对象句柄完后,接下来将其与某个套接字关联在一起,同时注册自己感兴趣的网络事件类型,方法是调用WSAEventSelect函数,对它的定义如下:

   int WSAEventSelect (
              SOCKET s,                              //需要非阻塞处理的套接字
              WSAEVENT hEventObject,    //WSACreateEvent 创建来的,关联到socket
              long lNetworkEvents     
               );
    lNetworkEvents,对应一个“位掩码”,用于指定应用程序感兴趣的各种网络事件类型的一个组合。要想获知对这些事件类型的详细说明,请参考早先讨论过的WSAAsyncSelect I/O模型。
      为WSAEventSelect创建的事件拥有两种工作状态,以及两种工作模式。
    两种工作状态分别是“已传信”(signaled)和 “未传信”(nonsignaled)。
    工作模式则包括“人工”(manual reset)和“自动”(auto reset)。
      

      WSACreateEvent缺省时其信号状态为0,且为人工设置,当网络事件触发了与一个套接字关联在一起的事件对象,其事件信号置1。在完成了一个I/O请求的处理之后,需要调用WSAResetEvent复位处理(置信号为0)。
     一个套接字同一个事件对象句柄关联在一起后,应用程序便可开始I/O处理;方法是等待网络事件触发事件对象句柄的工作状态。
     一般而言,在等待网络传来事件时,类似WaitforMultipleObject,其WSAWaitForMultipleEvents函数的设计宗旨便是用来等待一个或多个事件对象句柄,并在事先指定的一个或所有句柄进入有信号状态后,或在超过了一个规定的时间周期后,立即返回(线程往往在这里死等)。

   下面是 WSAWaitForMultipleEvents函数的定义:
DWORD WSAWaitForMultipleEvents(
  DWORD cEvents,                 
  const WSAEVENT FAR *lphEvents
  BOOL fWaitAll,                 
  DWORD dwTimeOUT,               
  BOOL fAlertable                
);

其用法和WaitForMultipleObject类似。
cEvents和lphEvents参数定义了由WSAEVENT对象构成的一个数组。在这个数组中,cEvents指定的是事件对象的数量,而lphEvents对应的是一个指针,用于直接引用该数组。
     要注意的是, WSAWaitForMultipleEvents只能支持由WSA_MAXIMUM_WAIT_EVENTS对象规定的一个最大值,在此定义成64个。故该I/O模型一次最多都只能支持64个套接字。假如想让这个模型同时管理不止64个套接字,必须创建更多的工作者线程,以便等待更多的事件对象。

fWaitAl l 参数指定了WSAWaitForMultiple Events如何等待在事件数组中的对象。
   =TRUE,那么只有等lphEvents数组内包含的所有事件对象都处于有信号状态,函数才会返回;

   =FALSE,任一个事件对象进入有信号时,函数就会返回。

 dwTimeout参数规定了 WSAWaitForMultipleEvents最多可等待一个网络事件发生有多长时间。超过规定的时间,函数就会立即返回。并返回WSA_WAIT_TIMEOUT。如dwsTimeout设为WSA_INFIN ITE(永远等待),那么根据fWaiiAll或等待一个网络事件或所有网络事件都传信号后,才能从该函数退出。
 fAlertable,缺省设为FALSE。主要用于在重叠式I/O模型中.

  当设置fWaiAll=false,WaitForMultipleObject再有网络事件时,会返回一个值,指出造成函数返回的事件对象。根据WSAWaitForMultipleEvents的返回值,减去预定义值WSA_WAIT_EVENT_0,得到具体的引用值(即索引位置),程序便可用事件数组中已发信号的事件,检索与那个事件对应的套接字,知道了造成网络事件的套接字后,调用 WSAEnumNetworkEvents函数,调查发生了什么类型的网络事件。该函数定义如下:
int WSAEnumNetworkEvents (
  SOCKET s,                                      //检索该套接字
  WSAEVENT hEventObject,             
  LPWSANETWORKEVENTS lpNetworkEvents 
);
    hEventObject参数则是可选的;它指定了一个事件句柄,对应于打算重设的那个事件对象。由于我们的事件对象处在一个有信号状态,所以可将它传入,令其自动成为无信号状态。
    也可以采用使用 WSAResetEvent 函数复位事件信号。
   lpNetworkEvents,就是返回的结果信息,它是一个指向WSANETWORKEVENTS结构的指针,用于接收套接字上发生的网络事件类型以及可能出现的任何错误代码。

其WSANETWORKEVENTS结构的定义:
typedef struct _WSANETWORKEVENTS
{
     long lNetworkEvents;
     int iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
lNetworkEvents参数指定了一个值,对应于套接字上发生的所有网络事件类型。
      注意一个事件进入置1(有信号)状态时,可能会同时发生多个网络事件类型。如,一个忙的服务器可能同时收到FD_READ和FD_WRITE通知。 iErrorCode参数指定的是一个错误代码数组,同lNetworkEvents中的事件关联在一起。针对每个网络事件类型,都存在着一个特殊的事件索引,名字与事件类型的名字类似,只是要在事件名字后面添加一个“ _BIT”后缀字串即可。如,对FD_READ事件类型来说,iErrorCode数组的索引标识符便是FD_READ_BIT。

posted @ 2007-12-27 18:01 kenlistian 阅读(6616) | 评论 (1)编辑 收藏

未命名

在其中,要注意的是:

  1.关于服务端在客户端连接之前

      如果没有客户端连接时,在调用accept()时程序将会出现freeze,即阻塞,而一旦有客户端连接过来,accept将会新建一Socket与客户端的Socket相通,原先Socket继续进入监听状态,等待他人的连接要求。
     该函数调用成功返回一个新产生的Socket对象,否则返回INVALID_SOCKET,该新socket就是一个和一个客户端对话的套接字。有点类似孙悟空对阵天兵天将时,当遇到某个具体敌手来,拔出一根毫毛变出一个孙行者去应付,再来再拔毫毛一样。accept()定义如下:
SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen );
s:Socket的识别码;
addr:存放来连接的客户端的地址;
addrlen:addr的长度

  当客户端连接上后一直没有断开情况下,如果连接越来越多时,则创建的Socket也越多,其最大上限在listen中已经设置。

2.关于服务端的accept()使用

  accept过后才是真正的和客户端进行交互,在accept时,由于程序会freeze,在调用accept时有多种方法,其中方法有:

*事件处理模式:

     通过WSAAsyncSelect()函数,其异步通知有accept信号来,然后在一个窗体自定义事件中处理accept信号。   

   如下在listen()之后调用:

        WSAAsyncSelect(m_hSocket, m_hWnd, WM_CLIENT_ACCEPT,FD_ACCEPT);  //wm_xxx_xxz自定义消息。

  这样在构建的自定义消息中处理accept()连接请求。如下,OnAccept()单元

       LRESULT CPublicNetSoftDlg::OnAccept(WPARAM wParam,LPARAM lParam)
       {

             。。。。
               if(WSAGETSELECTEVENT(lParam) == FD_ACCEPT)//如果
                {
                         Client = accept(ServerSocket,(LPSOCKADDR)&m_sockServerAddr,0);

                        if (Client == INVALID_SOCKET) 
                            return 0L;
                }

            。。。。。
    }

*线程处理模式:将accept放在线程中让其freeze,一旦来了连接,则自然从freeze中出来进行处理下一步。下面就是直接把accetp放在线程中处于等待状态。

//连接请求队列长度为1,即只允许有一个请求,若有多个请求, 则出现错误,给出错误代码WSAECONNREFUSED。
listen(sock,1);
//开启线程避免主程序的阻塞
AfxBeginThread(Server,NULL);
……

//处理线程,等待客户端连接
UINT Server(LPVOID lpVoid)
{
……
   int nLen = sizeof(SOCKADDR);
   connSocket = accept(ListSocket,(LPSOCKADDR)& sockin,(LPINT)& nLen);
   ……
    WSAAsyncSelect(connSocket,
                 m_hWnd,
                 WM_SOCKET_MSG,
                 FD_READ|FD_CLOSE);
    return 1;
}
  把accept()放到线程中去是因为在执行到该函数时如没有客户连接请求到来,服务器就会停在accept语句上处于等待阻塞,这势必会引起进程的阻塞,虽然也可以通过设置套接字为非阻塞方式使在没有客户等待时可以使accept()函数调用立即返回,但这种轮询套接字的方式会使CPU处于忙等待方式,从而降低程序的运行效率大大浪费系统资源(我觉得做法很多,暂不考虑非阻塞情况)。
      在阻塞工作方式,为其单独开辟一个子线程,将其阻塞控制在子线程范围内而不会造成整个应用程序的阻塞。对于网络事件的响应显然要采取异步选择机制,只有采取这种方式才可以在由网络对方所引起的不可预知的网络事件发生时能马上在进程中做出及时的响应处理,而在没有网络事件到达时则可以处理其他事件,这种效率是很高的。前面那段代码中的WSAAsyncSelect()函数便是实现网络事件异步选择的核心函数。
    第4个参数注册应用程序关心的网络事件,在这里通过FD_READ|FD_CLOSE指定了网络读和网络断开两种事件,当这种事件发生时变会发出由第三个参数指定的自定义消息 WM_SOCKET_MSG,接收该消息的窗口通过第二个参数指定其句柄。

其响应函数如下:

void CNetServerView::OnSocket(WPARAM wParam,LPARAM lParam)
{
    int iReadLen=0;
   int message=lParam & 0x0000FFFF;
   switch(message)
   {
     case FD_READ:     //读事件发生。此时有字符到达,需要进行接收处理
        char cDataBuffer[MTU*10];
        //通过套接字接收信息
        iReadLen = recv(newskt,cDataBuffer,MTU*10,0);
       //将信息保存到文件
        if(!file.Open("ServerFile.txt",CFile::modeReadWrite))
             file.Open("E:ServerFile.txt",
                 CFile::modeCreate|CFile::modeReadWrite);
         file.SeekToEnd();
         file.Write(cDataBuffer,iReadLen);
         file.Close();
         break;
         case FD_CLOSE://网络断开事件发生。此时客户机关闭或退出。
             ……//进行相应的处理
                break;
         default:
               break;
      }
}

   对于recv和send的处理一般就是在事件中处理,通过WSAAsySelect()来传递信号,这种方式形成一种固定写socket方式,比如有的人喜欢把recv和send各自放入一个线程中通过轮询+阻塞模式,或者采用事件通知模式,一般来说采用I/O模型是较为专业的做法。

3.客户端的connect()

    客户端连接时存在阻塞现象,就是程序在connect会出现freeze,一般可以容忍。但若想通过超时设置来解决这个问题,可采用在vckbase中,对于connect()超时的处理办法。不过觉得有时调用封装好的socket,直接是指connectionTimeout属性倒是简单的方法。

WSADATA wsd;
SOCKET cClient;
int ret;
struct sockaddr_in server;
hostent *host=NULL;

if(WSAStartup(MAKEWORD(2,0),&wsd))

{

   return 0;

}
cClient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(cClient == INVALID_SOCKET){return 0;}
//set Recv and Send time out
int TimeOut=6000; //设置发送超时6秒
if(::setsockopt(cClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
     return 0;
}
TimeOut = 6000;//设置接收超时6秒
if(::setsockopt(cClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
return 0;
}
//设置非阻塞方式连接
unsigned long ul = 1;
ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul);
if(ret==SOCKET_ERROR)  return 0;

//连接
server.sin_family = AF_INET;
server.sin_port = htons(25);
server.sin_addr .s_addr = inet_addr((LPCSTR)pSmtp);
if(server.sin_addr.s_addr == INADDR_NONE){return 0;}

//运行这里将不会阻塞,而是直接运行下去,通过select中设置的 timeval结构参数设定连接超时处理。

connect(cClient,(const struct sockaddr *)&server,sizeof(server));

//select 模型,即设置超时
struct timeval timeout ;
fd_set r;

FD_ZERO(&r);
FD_SET(cClient, &r);
timeout.tv_sec = 15; //连接超时15秒
timeout.tv_usec =0;
ret = select(0, 0, &r, 0, &timeout);            //超时socket将关闭
if ( ret <= 0 )
{
         ::closesocket(cClient);
          return 0;
}
//一般非锁定模式套接比较难控制,可以根据实际情况考虑 再设回阻塞模式,又把状态设置为 阻塞状态。
unsigned long ul1= 0 ;
ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul1);
if(ret==SOCKET_ERROR){
         ::closesocket (cClient);
          return 0;
}

 

4.select()

a. 当你希望服务器监听连接服务请求,而又不想通过轮询的方式,则理想的方式是调用select().它运行你把程序本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现了活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了程序的选出是随机变化的,而不必由程序本身对输入进行测试而浪费cpu开销,

    在socket编程中,select函数一般在非阻塞的socket中,用来检查socket缓冲区中是否有数据可读,或是否可以写数据到socket缓冲区。  
  有时,select()也被用来当作延时函数使用。sleep()延时会释放cpu,用select的话,可以在占用cpu的情况下,延时。

   select()是用来进行多路转接的函数。它可以同时等待n(n大于等于1)个文件描述字或者socket套接口。只要它等待的任意描述字准备好或者等待时间超过了设定时间程序就往下执行。可以防止进程长时间阻塞,占用资源。

b.简单说法:

  如果你要发数据用select(sock+1,&s,NULL,NULL,NULL);  
     if(FD_ISSET(sock,&s)   ,你可以发了。send   it  
  如果你要收数据用select(sock+1,NULL,&s,NULL,NULL);  
  if(FD_ISSET(sock,&s)   ,你可以收了。recv   it  

socket默认情况下是阻塞的,除非你用WSAAsyncSelect   OR   select   就变成NOBBLOCKING,  
  将阻塞设为非阻塞如下:  
  int   opt=1;  
  ioctlsocket(sock,FIONBIO,&opt)  
  若opt=0就是阻塞的了

 

c.用法一

  fd_set m_readfds;  
  fd_set m_exceptfds;  
  timeval m_tmOut;  
  m_tmOut.tv_sec =   120;     //接收时间如果超过120秒,即认为网络连接已经中断,  
  m_tmOut.tv_usec =   0;      //客户端应该定时每40秒发送一次空闲信号,以防止被误认为是网络连接中断。  
  FD_ZERO(   &m_readfds   );  
  FD_ZERO(   &m_exceptfds   );  
  FD_SET(   m_scSocket,   &m_exceptfds   );  
  FD_SET(   m_scSocket,   &m_readfds   );  
  int CNet::Receive(   char   *   szBuff,   int   iSize   )  
  {  
        int   iRet;  
        if(   m_ntType   ==   _NET_SERVER_   )  
        {  
              iRet   =   select(   m_scSocket   +   1,   &m_readfds,   NULL,   &m_exceptfds,   &m_tmOut   );  
              if(   iRet   ==   0   )  
             {  
                   m_iError =   13; //超时  
                   return   -2;  
             }  
             if(   iRet   ==   SOCKET_ERROR   )  
            {  
                 GetLastError(   );  
                  return   -1;  
            }  
            if(   FD_ISSET(   m_scSocket,   &m_exceptfds   )   )  
            {  
                  m_iError =   14; //连接被终止  
                  return   -1;  
           }  
      }  
      iRet   =   recv(   m_scSocket,   szBuff,   iSize,   0   );  
      if(   iRet   ==   0   )  
     {  
               m_iError =   14; //连接被终止  
              return   -1;  
    }  
   if(   iRet   ==   SOCKET_ERROR   )  
   {  
          GetLastError(   );  
          return   -1;  
   }  
    return   iRet;  
  }  

用法二

int   recvex_sock(SOCKET   sock,   void*   buf,   int   len,   int   sec)  
  {  
                  int   rs;  
                  fd_set   fd;  
                  struct   timeval   tv;  
                  memset(&tv,   0,   sizeof(tv));  
                  if   (   sec   >   0   )  
                                  tv.tv_sec   =   sec;  
                  FD_ZERO(&fd);  
                  FD_SET(sock,   &fd);  
                  rs   =   select(sock   +   1,   &fd,   0,   0,   sec   >=   0   ?   &tv   :   NULL);  
                  if   (   rs   ==   0   )  
                                  return   SOCKET_TIMEOUT;  
                  if   (   rs   <   0   )  
                                  return   SOCKET_ERROR;  
                  if   (   !FD_ISSET(sock,   &fd)   )  
                                  return   SOCKET_ERROR;  
                  return   (recv_sock(sock,   buf,   len));  
  } 

posted @ 2007-12-06 14:21 kenlistian 阅读(3027) | 评论 (0)编辑 收藏

Windows Socket API 基础
本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API。

一、WSAStartup函数
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
      使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。
例:假如一个程序要使用2.1版本的Socket,那么程序代码如下
wVersionRequested = MAKEWORD( 2, 1 );
err = WSAStartup( wVersionRequested, &wsaData );

二、WSACleanup函数
int WSACleanup (void);
   应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。

三、socket函数
SOCKET socket(
int af,
int type,
int protocol
);
   应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数指定应用程序所使用的通信协议。
   该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。
struct protoent *ppe;
ppe=getprotobyname("tcp");
SOCKET ListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);

四、closesocket函数
int closesocket(
SOCKET s
);
closesocket函数用来关闭一个描述符为s套接字。由于每个进程中都有一个套接字描述符表,表中的每个套接字描述符都对应了一个位于操作系统缓冲区中的套接字数据结构,因此有可能有几个套接字描述符指向同一个套接字数据结构。套接字数据结构中专门有一个字段存放该结构的被引用次数,即有多少个套接字描述符指向该结构。当调用closesocket函数时,操作系统先检查套接字数据结构中的该字段的值,如果为1,就表明只有一个套接字描述符指向它,因此操作系统就先把s在套接字描述符表中对应的那条表项清除,并且释放s对应的套接字数据结构;如果该字段大于1,那么操作系统仅仅清除s在套接字描述符表中的对应表项,并且把s对应的套接字数据结构的引用次数减1。
closesocket函数如果执行成功就返回0,否则返回SOCKET_ERROR。

五、send函数
int send(
SOCKET s,
const char FAR *buf,
int len,
int flags
);
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。这里只描述同步Socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

六、recv函数
int recv(
SOCKET s,
char FAR *buf,
int len,
int flags
);
    不论是客户端还是服务端程序都用recv函数从TCP连接的另一端接收数据。
    该函数的第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

七、bind函数
int bind(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);
     当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。服务端必须调用bind函数来给其绑定一个IP地址和一个特定的端口号。客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。
    该函数的第一个参数指定待绑定的Socket描述符;第二个参数指定一个sockaddr结构,该结构是这样定义的:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};
sa_family指定地址族,对于TCP/IP协议族的套接字,给其置AF_INET。当对TCP/IP协议族的套接字进行绑定时,我们通常使用另一个地址结构:
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中sin_family置AF_INET;sin_port指明端口号;sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。有的服务器是多宿主机,至少有两个网卡,那么运行在这样的服务器上的服务程序在为其Socket绑定IP地址时可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;如果只给运行在多宿主机上的服务程序的Socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。我们用0来填充sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。下面是一个bind函数调用的例子:
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr));

八、listen函数
int listen( SOCKET s, int backlog );
    服务程序可以调用listen函数使其流套接字s处于监听状态。处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。假如该函数执行成功,则返回0;如果执行失败,则返回SOCKET_ERROR。

九、accept函数
SOCKET accept(
SOCKET s,
struct sockaddr FAR *addr,
int FAR *addrlen
);
    服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,连接成功返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;失败返回INVALID_SOCKET。
    该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。例子:
struct sockaddr_in ServerSocketAddr;
int addrlen;
addrlen=sizeof(ServerSocketAddr);
ServerSocket=accept(ListenSocket,(struct sockaddr *)&ServerSocketAddr,&addrlen);

十、connect函数
int connect(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);
   客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接。连接成功返回0;失败返回SOCKET_ERROR。
struct sockaddr_in daddr;
memset((void *)&daddr,0,sizeof(daddr));
daddr.sin_family=AF_INET;
daddr.sin_port=htons(8888);
daddr.sin_addr.s_addr=inet_addr("133.197.22.4");
connect(ClientSocket,(struct sockaddr *)&daddr,sizeof(daddr));

posted @ 2007-12-05 16:54 kenlistian 阅读(263) | 评论 (0)编辑 收藏

 1、总结自己一天任务的完成情况
  最好的方式是写工作日志,把自己今天完成了什么事情,遇见了什么问题都记录下来,日后翻看好处多多。

  2、考虑自己明天应该做的主要工作
  把明天要做的事情列出来,并按照优先级排列,第二天应该把自己效率最高的时间分配给最重要的工作。

  3、考虑自己一天工作中失误的地方,并想出避免下一次再犯的方法
  出错不要紧,最重要的是不要重复犯相同的错误,那是愚蠢。

  4、考虑自己一天工作完成的质量和效率能否还能提高
  一天只提高1%,365天你的效率就能提高多少倍你知道吗? (1+0.01)^365 = 37 倍。

  5、看一个有用的新闻网站或读一张有用的报纸,了解业界动态
  闭门造车是不行的,了解一下别人都在做什么,对自己能带来很多启示。

  6、记住一位同事的名字及其特点
  你认识公司的所有同事吗?你了解他们吗?

  7、清理自己的代码
  今天完成的代码,把中间的调试信息,测试代码清理掉,按照编码风格整理好,注释都写好了吗?

  8、清理自己的桌面
  当日事当日毕,保持清洁干劲的桌面才能让你工作时不分心,程序员特别要把电脑的桌面清理干净。

  程序员每周该做的事

  1、向你的老板汇报一次工作
  让你的老板知道你在做什么,这很重要。可以口头、书面、邮件,看你老板的工作方式而定。

  2、进行一次自我总结(非正式)
  这周之内自己表现得怎么样?该加分还是扣分?

  3、制定下周计划
  把下周要做的事情列出来,一样要分清楚优先级。

  4、整理自己的文件夹、书柜和电脑文件
  把桌面以外的地方也要清理干净,电脑的文件夹,收到的邮件,把过时的垃圾全部清理掉。

  5、与一个非公司的朋友沟通
  它山之石,可以攻玉。

  6、看一本杂志
  找一本适合自己的专业杂志。

  7、纠正自己或同事一个细节上的不正确做法
  《细节决定成败》看过了吗?没看过强烈建议先看看。

  程序员每月该做的事

  1、至少和一个同事一起吃饭或喝茶
  不光了解自己工作伙伴的工作,还要了解他们的生活。

  2、自我考核一次
  相对正式地考核自己一下,你对得起这个月的工资吗?

  3、对你的同事考核一次
  你的同事表现怎么样?哪些人值得学习,哪些人需要帮助?

  4、制定下月的计划,确定下月的工作重点

  5、总结自己工作质量改进状况
  自己的质量提高了多少?

  6、有针对性地对一项工作指标做深入地分析并得出改进的方案
  可以是对自己的,也可以是对公司的,一定要深入地分析后拿出自己的观点来。要想在老板面前说得上话,做的成事,工作上功夫要做足。

  7、与老板沟通一次
  最好是面对面地沟通,好好表现一下自己,虚心听取老板的意见,更重要的是要了解老板当前关心的重点。

  程序员每年该做的事

  1、年终总结
  每个公司都会做的事情,但你真正认真地总结过自己吗?

  2、兑现给自己、给家人的承诺
  给老婆、儿子的新年礼物买了没有?给自己的呢?

  3、下年度工作规划
  好好想想自己明年的发展目标,争取升职/加薪、跳槽还是自己出来干?

  4、掌握一项新技术
  至少是一项,作为程序员一年要是一项新技术都学不到手,那就一定会被淘汰。掌握可不是看本书就行的,要真正懂得应用,最好你能够写一篇教程发表到你的blog 。

  5、推出一种新产品
  可以是一个真正的产品,也可以只是一个类库,只要是你创造的东西就行,让别人使用它,也为世界作点贡献。当然如果真的很有价值,收点注册费也是应该的。

  6、与父母团聚一次
  常回家看看,常回家看看。

posted @ 2007-12-04 17:03 kenlistian 阅读(236) | 评论 (0)编辑 收藏

在windows下: 信号量(Semaphore)内核对象对线程的同步方式,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。 CreateSemaphore() OpenSemaphore() ReleaseSemaphore(), WaitForSingleObject()/WaitForMultipleObjects() CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。 一般是将当前可用资源计数设置为最大资源计数, 每增加一个线程对共享资源的访问,当前可用资源计数就会减1, 只要当前可用资源计数是大于0的,就可以发出信号量信号。 但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。 线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。 说明如下: HANDLE CreateSemaphore(  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针  LONG lInitialCount, // 初始计数  LONG lMaximumCount, // 最大计数, 定义了允许的最大资源计数  LPCTSTR lpName // 对象名指针, 创建的信号量定义一个名字,其创建的是一个内核对象,因此在其他进程中可以通过该名字而得到此信号量 ); OpenSemaphore()函数即可用来根据信号量名打开在其他进程中创建的信号量,函数原型如下: HANDLE OpenSemaphore(  DWORD dwDesiredAccess, // 访问标志  BOOL bInheritHandle, // 继承标志  LPCTSTR lpName // 信号量名 );  在线程离开对共享资源的处理时,通过ReleaseSemaphore()来增加当前可用资源计数。否则将会出现当前正在处理共享资源的实际线程数并没有达到要限制的数值, 而其他线程却因为当前可用资源计数为0而仍无法进入的情况。 BOOL ReleaseSemaphore(  HANDLE hSemaphore, // 信号量句柄  LONG lReleaseCount, // 计数递增数量  LPLONG lpPreviousCount // 先前计数,可以设置为NULL, );   该函数将lReleaseCount中的值添加给信号量的当前资源计数,一般将lReleaseCount设置为1, WaitForSingleObject和WaitForMultipleObjects主要用在试图进入共享资源的线程函数入口处, 主要用来判断信号量的当前可用资源计数是否允许本线程的进入。 只有在当前可用资源计数值大于0时,被监视的信号量内核对象才会得到通知。
posted @ 2007-12-04 14:18 kenlistian 阅读(4557) | 评论 (0)编辑 收藏

Lua中的文件操作部分

在lua中内含了io,file模块,直接调用该模块中函数即可。下面列出可用的函数集合。(细节见lua文档)

io.close([file]) file:close()
io.flush() file:flush()
io.input([file])
io.read(...) file:read(...)
io.lines([filename]) file:lines()
io.open(filename, [,mode])
io.output([file])
io.write(...) file:write(...)
io.tmpfile()
io.type(obj)

file:setvbuf(mode, [size]) 设置buf区为文件映射
mode = "no"    立即写
              "full"   满buf区写
              "line"  写行

file:seek([whence],[,offset])
whence = "set"/"cur"/"end"

操作系统相关函数

os.clock() 当前cpu时间
os.date() 日期
os.difftime (t2, t1) t2-t1的时间,单位为秒
os.execute ([command]) shell命令
os.exit ([code]) 退出
os.getenv (varname) 获取环境
os.remove (filename) 删文件,目录(须空)
os.rename (oldname, newname) 改名
os.setlocale (locale [, category]) 改类型
os.time ([table]) 时间
os.tmpname ()

 

 

 

posted @ 2007-04-27 14:23 kenlistian 阅读(303) | 评论 (0)编辑 收藏

单独用lua是不够的,这里我简单推出cbuilder版本和lua的结合运用例子一篇。

这个例子是初步实现了在cbuilder中调用lua实现的函数的例子,同时,也演示了lua中如何调用cbuilder中的函数例子。闲话少说,把例子帖上来。

有几个方面要注意,在cbuilder中创建一个工程,把lua中的src导入进来,其中,要在单元。h文件中做如下声明

extern "c"{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}//end extern "c"

否则编译时,cbuilder是不认

################################

#include <vcl.h>
#pragma hdrstop
#include "uni2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
tform1 *form1;
//---------------------------------------------------------------------------
__fastcall tform1::tform1(tcomponent* owner)
        : tform(owner)
{
}

/*
 提供给lua调用,有函数格式要求
 在cbuilder中通过堆栈来获得lua的参数,经过该函数处理后,
 也将按栈方式返回,按左序压入
 通过lua_gettop获得调用函数的传入参数个数。
 第一个参数在索引 1 的地方,最后一个参数在索引 lua_gettop(l) 处。
 返回一个结果时,也是通过左序压到堆栈上(第一个返回值最先压入),然后返回这些返回值的个数。
 在这些返回值之下的,堆栈上的东西都会被 lua 丢掉。
*/
int mytest_call(lua_state *l)
{
   int n = lua_gettop(l);

   //typedef double lua_number;
   //lua 中数字的类型。确省是 double ,可以在luaconf.h 中修改它
   lua_number sum = 0;
   int i;
   for (i = 1; i <= n; i++) {
        //lua调用该函数参数时是从栈上索引为1开始
         sum += lua_tonumber(l, i);   // 保证传入的参数是数值类型,否则会报错。
   }

   lua_pushnumber(l, sum);    //返回值压入栈传回
   return 1;                 //返回参数数量值
}


/*
    调用lua 中的函数模式
     1.使用lua_getglobal()来获得函数,
     2.将参数压入堆栈,
     3.调用 lua_pcall(),
     4.然后处理结果。
*/
void __fastcall tform1::button1click(tobject *sender)
{
      //加载后要用lua_resume运行.
      lua_resume(l,0);

      //调用脚本中函数
      lua_getglobal(l, "lua_func1");

      //传给lua_func1参数1,参数2
      lua_pushnumber(l, 21);
      lua_pushnumber(l, 23);

      //调用lua中函数,传入个参数,有一个返回值 ,看lua_call 和lua_pcall区别
      lua_pcall(l, 2, 1, 0);

      //取值
      lua_number retsum;

      //注意返回值是通过-1来获取
      retsum = lua_tonumber(l, -1);
      label1->caption = inttostr((int)retsum);
}

void __fastcall tform1::formclose(tobject *sender, tcloseaction &action)
{
      lua_close(l);
}

void __fastcall tform1::formcreate(tobject *sender)
{
     l = lua_open();
     luaopen_base(l);
     luaopen_string(l);

     //将主程序中的mytest_call函数注册到lua引擎中,
     //脚本用mytest回调宿主程序
     lua_register(l, "mytest", mytest_call);

      //加载脚本
     string sfilename = "lua_test1.lua";
     lual_dofile(l, sfilename.c_str());
}

/*
 测试返回字符串
*/
void __fastcall tform1::button2click(tobject *sender)
{
      //用lua_resume运行.
     // lua_resume(l,0);

      lua_getglobal(l,"lua_fun3");
      lua_pushnumber(l, 100);
      lua_pcall(l, 1, 1, 0);
      label2->caption = lua_tostring(l, -1);


      //测试调用lua中全局变量,
      lua_getglobal(l, "retstr");
      label3->caption = lua_tostring(l, -1);
}

############

其中lua_test1.lua测试文件如下:
retstr="123"
function lua_fun3(ss)
  ss = "aaaaaaaa"
  retstr = ss
  return ss
end

function lua_func2(val1, val2)
 sum = val1+val2
 return sum
end

function lua_func1(val1, val2)
 --调用cbuilder中函数
 val1 = mytest(val1, val2)
 return val1
end

运行效果图:

 

实践源码见:

http://256617.tomore.com/1/44360.html

 

 

 

posted @ 2007-04-24 11:35 kenlistian 阅读(736) | 评论 (0)编辑 收藏

手机开发学习二

如何调试手机程序

我运行调试环境是vc6 .

写好一个手机程序,就需要调试,在symbian下有epoc,其就是模拟手机的一个程序.在s60目录下有2个版本,一个版本包含符号调试信息,另一个是发布版本。

发布版模拟器限制为只能评估或演示应用程序--它启动得很快,因为它不带有调试信息。在开发途中,可以任意挂接2个版本之一.我觉得采用发布版更好,因为我不想关注模拟器中的一些信息.

发布版模拟器;对于Visual C++在
\Symbian\6.1\Series60\Epoc32\Release\wins\urel\epoc.exe
其他针对cbuilder及其它的模拟器.可不管它.

调试版模拟器下位于:vc6
\Symbian\6.1\Series60\Epoc32\Release\wins\udeb\epoc.exe

由于对手机程序编程,就像是对dll编程,因而弹出需要执行程序时,输入

epoc的文件名加路径即可.或手工加入,如下:

 

调试应用程序

设上断点,按F5运行它.

其中弹出模拟器,找到hello world 按手机模式操作看看.


  

在vc6下随意设置断点跟跟看.

 

posted @ 2006-09-16 12:00 kenlistian 阅读(181) | 评论 (0)编辑 收藏

因工作需要,不得不从事symbian开发.没接触,但是工作上要求使用,

把关于资料及其试炼笔记一同贴上来吧.

*如何在vc6下编译代码,以hello world为例子

 首要条件,安装symbian sdk包,安装activeper,其安装目录不再絫述.

 1.在命令行建立bat文件

  打开命令提示符并把当前路径改变到包含Series 60 SDK的驱动器/文件夹中。导航到包含项目定义(helloworld.mmp)和组件描述(bld.inf)文件的文件夹——输入:

bldmake bldfiles

  一两秒钟之后这个命令就完成了。它使用bld.inf和helloworld.mmp文件生成了一个新文件abld.bat。这个命令文件一般是根据需要生成的。与bld.inf和.mmp文件不同,abld.bat在不同的IDE之间是不能迁移的,并且不应该手动修改它。

2.在vc6 IDE中编译运行

通常,在产生bat后,在cmd输入下面的命令编译和链接项目:

abld build wins udeb

—用于Visual C++
abld build winsb udeb

—用于Borland C++

abld build winscw udeb —用于CodeWarrior

 Abld命令将为Series 60模拟器(wins、winscw或winsb)建立该项目(换句话说,就是编译和链接),并把调试信息包含在二进制可执行文件中。但是实际上要在vc6中调试并运行的话,需要在cmd下输:

   abld makefile vc6


 这将产生vc6的项目和工作空间文件(helloworld.dsp和.dsw文件)。位于\Epoc32\Build子文件夹结构下;其完整的路径依赖于SDK的位置.

  最后打开vc6 ide,可以编译或链接通过inf和mmp产生的项目文件

posted @ 2006-09-16 11:23 kenlistian 阅读(177) | 评论 (0)编辑 收藏


呵呵,水文

posted @ 2006-08-19 14:21 kenlistian 阅读(165) | 评论 (0)编辑 收藏

转自网络,流的概念主要在cbuilder  和delphi编程环境下的处理,使用流有以下方面

  • 1. 使用统一的流的概念来操作各种不同类型的资源。
    2. 通过流将资源保存到不同的介质上,如将组件保存到文件中,将ICON资源调入内存等。 3. 简化一些对象的操作,如TBlobStream和TClientBlobStream
    4. 实现一些原来不好实现的功能,如TWinSocketStream实现超时操作

 

 

1.概述cbuilder流
在CBUILDER中,存在以下几种流对象:
TFileStream
TMemoryStream
TStringStream
TWinSocketStream
TBlobStream
TOleStream
TClientBlobStream
TResourceStream


下面是以上各类的继承关系
TObject
  |
TStream
  |
+---------------------------+---------------------------+--------------------+-------------------+------------+
|                  |                  |              |            |        |
THandleStream TCustomMemoryStream TWinsocketStream TBlobStream TOleStream  TStringStream
|                  |
TFileStream  TMemoryStream
                    |
            +-----------------------------+
            |                            |
        TClientBlobStream          TResourceStream

 


2 流对象的属性及方法简介
关于各属性和方法的详细语法请见CBUILDER的帮助

一. TStream介绍
属性
        Position:当前位置指针
        Size:流的大小,单位为字节
方法
        CopyFrom:   从一个流往另一个流拷贝数据
        Read:       从流中读取一定字节的数据到缓冲区,返回读取的字节数
        ReadBuffer: 从流中读取一定字节的数据到缓冲区,如不正确则异常
        ReadComponent:从流中取出一个组件
        ReadComponentRes:以Windows的资源文件格式从流中读取一组件
        Seek:         定位流的当前位置指针
        SetSize:     设置流的大小。
        Write:       从缓冲区中将一定字节的数据写入到流中,返回写入的字节数
        WriteBuffer: 从缓冲区中将一定字节的数据写入到流中,失败则异常
        WriteComponent:将一组件写入到流中
        WriteComponentRes:将一组件以Windows资源文件的格式写入到流中


二. THandleStream介绍
属性
        Handle:流对象要读写的通迅资源的句柄
        Size:句柄所标识的资源的大小,单位为字节
方法
        Read:从流中读数据到缓冲区中
        Seek:设置流的当前位置
        SetSize:设置流的大小,不成功则返回一个异常
        THandleStream:通过一个打开的句柄建立一个句柄流对象
        Write:将数据从缓冲区中写入到流

可以使用THandleStream流对象来存取如文件、套接字、命名管道、邮槽等打开了句柄的通迅资源。

采用句柄流对象进行文件操作的片段代码:
//////////////////////////////////////////////////////////////////////////////////////////////
//将c:config.sys文件中的内容读入到一个文本框中
int fileHandle;
THandleStream *fileStream;

  fileHandle = FileOpen(“c:\config.sys”,fmOpenRead);
  if(fileHandle == -1)
     return ;  //打开文件出错

  fileStream = new THandleStream(fileHandle);
  char buffer[10001];
  unsigned long bufferSize;
  do
  {
    bufferSize = fileStream->Read(buffer, 10000);
    if (bufferSize > 0 )
    {
        buffer[bufferSize] = 0;
        Memo1->Text += buffer;
    }
  }while( bufferSize == 10000 );

  delete fileStream;
  FileClose(fileHandle); //请注意一定要先注销流对象才能关闭句柄

 

三. TCustomMemoryStream介绍
属性
        Memory:指向内存流对象的实际内存的指针,可用该指针直接访问内存流的内存
方法
        Read:从流中读数据到缓冲区中
        SaveToFile:将内存流中的数据保存到文件中
        SaveToStream:将内存流中的数据保存到其它流对象中
        Seek:设置流对象的当前位置
        SetPointer:设置与内存流对象相关联的内存池

    该类为纯虚类,不能直接建立其对象。
    应使用TMemoryStream或是TResourceStream。

四. TWinSocketStream介绍
属性
        TimeOut:设置在读或写Socket时的超时值,单位:毫秒
方法
        Read:从Socket中读取指定字节的数据到缓冲区中,返回实际读取的字节数
        Seek:没有意义
        TWinSocketStream:根据指定的Socket句柄和超时时间建立一个Socket流对象
        WaitForData:确认是否可以通过Socket连接来发送或接收数据了。
        Write:通过Socket连接发送缓冲区中指定字节的数据

TWinSocketStream流对象用来在阻塞方式的Socket连接中发送和接收数据,
从而避免一般情况下的挂起现象。

下列是一个用TWinSocketStream进行收发数据的代码片段:
/////////////////////////////////////////////////////////////////////////////////////////////////////
//在一单独的线程中通过阻塞式Socket连接发送数据
void __fastcall TMyClientThread::Execute()
{
   TWinSocketStream *pStream = new TWinSocketStream(ClientSocket1->Socket, 60000);
   try
   {
     while (!Terminated && ClientSocket1->Active)
     {
        try
        {
           char buffer[10];
           GetNextRequest(buffer); // GetNextRequest must be a thread-safe method

          // write the request to the server
           pStream->Write(buffer, strlen(buffer) + 1);
          // continue the communication (eg read a response from the server)
           ...
         }
        catch (Exception &E)
        {
          if (!E.ClassNameIs("EAbort"))
          //you must write HandleThreadException
          Synchronize(HandleThreadException());
         }
     }
    }
    __finally
    {
       delete pStream;
    }
}


五. TBlobStream介绍
方法
        Read:读取数据到缓冲区中
        Seek:定位流的当前位置
        TBlobStream:根据一个TBlobField字段建立一个流对象
        Truncate:从当前位置截短流对象
        Write:将缓冲区中的数据写入到流对象中
 
 TBlobStream流对象只用于对TblobField进行操作,
 注意,当改变了数据集对象的当前记录时,
 要重新建立TBlobStream对象并在使用完后将之删除。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//读取Blob字段的数据
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  int MemSize;
  char *Buffer;
  TBlobStream *Stream;
  if (!Query1->Eof)
     Query1->Next();
  
   Stream = new TBlobStream((TBlobField *)Query1->FieldByName("Notes"), bmRead);
    try
     {
        MemSize = Stream->Size + 1; // add one for null terminator
        Buffer = new char[MemSize]; // Allocate the memory.
        try
        {
           Stream->Read(Buffer, MemSize); //Read Notes field into buffer.
           Memo1->SetTextBuf(Buffer);  // Display the buffer's contents.
         }
         catch (...)
        {
           delete Buffer;
           throw;
        }
       delete Buffer;
      }
      catch (...)
      {
         delete Stream;
          throw;
      }
      delete Stream;
}

六. TOleStream介绍
方法
        TOLEStream:通过一个流界面建立Ole流对象
        Read:从流对象中读数据到缓冲区中
        Seek:定位流的当前位置
        Write:从缓冲区中将数据写入到流对象中

七. TStringStream介绍
属性
        DataString:流对象的实际存储字符串
        Size:流对象大小
方法
        Read:从流对象中读数据到缓冲区中
        ReadString:以字符串形式返回流中指定数据
        Seek:定位流的当前位置
        TStringStream:根据一个字符串建立字符串流对象
        Write:从缓冲区中将数据写入到流对象中
        WriteString:将字符串中数据写入到流中

八. TFileStream介绍
方法
        TFileStream:根据文件名建立或打开一个文件,并建立相应的文件流对象
例:TFileStream *fsBootINI = new TFileStream(“c:\boot.ini”,fmOpenRead);


九. TMemoryStream介绍
方法
        Clear:清除流对象中所有数据
        LoadFromFile:从文件中读入数据到流对象中
        LoadFromStream:从其它流中读入数据
        SetSize:设置流对象的大小
        Write:从缓冲区中写数据到内存流中

十. TClientBlobStream介绍
方法
        TClientBlobStream:根据TblobField对象建立一个流对象
        Truncate:截短流
        Write:将缓冲区中数据写入到流中
该流对象用来操作TClientDataSet数据集中的BLOB类型的字段

十一. TResourceStream介绍
方法
TResourceStream:根据EXE文件名柄或DLL文件句柄和资源描述生成流对象
        Write:不支持写操作,产生异常

十二. 与流对象相关的对象介绍
TFilter
+--------------------------+
|                 |
TReader        TWriter
以上对象用于将组件写入到流对象中或是从流对象生成组件
流对象中的方法ReadComponent 和WriteComponent在实现时就是使用TReader和TWriter来实际操作的。

十三. 与流对象相关的函数

extern PACKAGE void __fastcall ObjectTextToBinary(TStream* Input, TStream* Output);
extern PACKAGE void __fastcall ObjectBinaryToText(TStream* Input, TStream* Output);
extern PACKAGE void __fastcall ObjectTextToResource(TStream* Input, TStream* Output);
extern PACKAGE void __fastcall ObjectResourceToText(TStream* Input, TStream* Output);

以上函数都是针对存储对象的流而言的,在流之间转换流中所存储对象的描述方式。

 

 


 

posted @ 2006-08-19 13:45 kenlistian 阅读(1004) | 评论 (0)编辑 收藏

据结构结构定义,
lua只有一种数据结构,真是太好了,以前学习数组啊,结构啊,类啊,再加上以后的vector,map啊,头都搞大了,
看个例子,这个就是lua学习的最基本的一个例子。
myData = {}                    --如果不要这句恐怕不行
myData[0] = “foo”      --在table中可以放置任意类型数值。
myData[1] = 42
myData[“bar”] = “baz”   --还可以在[]中下标为字符的,这个就像map了,

for key, value in myData do    --对table中的所有值遍历
  print(key .. “=“ .. value)      --打印table时,要这样写吗?来2个点?
end
 
---
table的引用
如果
a = {}
a[“test”] = “test”
b=a           b就是a的引用,指向同一个内存区
a=nil         实际好像就是把指针给释放了,
内存体还是没有
b=nil        再也  指不到内存区了,可见,内存区是自动管理的,不需要自己去创建和释放,拿来用就是。
 
----
table可以嵌套
mytable ={
    close = “red“
    {xpos = 0,       --这样看去就像结构了,
      ypos = 0
    }
}
 
---------------------
继续看demo
function contact(t)
end
contact {
name = "kenlistian"
email = kenlistian@tom.com
url = "http:\\www.cppblog.com"
quote = [[
    test
    test
    test
]]
contact{
}
 
刚看到这个代码看不懂,稍微解释一下,
[[。。。]] 表示多行字符串,(咳,麻烦)
function 咋和table在一起,这个函数好像通过多个contact达到保存
多条记录的可能。
 
笔记记录介绍到此。

 

posted @ 2006-06-17 19:13 kenlistian 阅读(192) | 评论 (0)编辑 收藏

有人问;学习lua有什么好处,这里我就不再重复列出别人的反复道说了,
我只认为,lua是具有最高效率的脚本语言。又小巧,又强大,又易学。凭这,就该去把握它。
如何用lua,下面有个描述:
Lua使用者分为三大类:使用Lua嵌入到其他应用中的、独立使用Lua的、Lua和C混合使用的。
1:很多人使用Lua嵌入在应用中,比如CGILua (搭建动态网页) 、 LuaOrb (访问 CORBA 对象). 这些类型的应用使用Lua-API注册新函数,创建新类型,通过配置Lua就可以改变应用宿主语言的行为。通常,这种应用的使用者并不知道Lua是一种独立的语言。例如:CGILua用户一般会认为Lua是一种用于Web的语言。
(不光嵌入到网站!)
2:作为一种独立运行的语言,Lua也是很有用的,主要用于文本处理或者只运行一次的小程序。这种应用Lua主要使用它的标准库实现,标准库提供模式匹配和其他一些字串处理的功能。我们可以这样认为:Lua是对文本处理领域的嵌入式语言。(这个方面的应用基本上就是玩具,)
3:还有一些使用者使用其他语言开发,把Lua当作库使用。这些人大多使用c语言开发,但使用Lua建立简单灵活的易于使用的接口。(值得考虑)
 
---------
唉,好处就不用讲了,开始做笔记吧,
--------
那个demo研究是最快的学习方法,现在就那个demo来研究吧
cf。lua
for c0=-20,50-1,10 do                     -- 循环语句 for 变量1,变量2,变量3 do 
 io.write("C ")
 for c=c0,c0+10-1 do                        --变量3可以作为控制台输入     
  io.write(string.format("%3.0f ",c))   --io?莎玩意,不就是cout吧,打印出c的值
 end
 io.write("\n")
 
 io.write("F ")
 for c=c0,c0+10-1 do
  f=(9/5)*c+32                                    --f连声明都没有就用,太哪个了吧。
  io.write(string.format("%3.0f ",f))
 end
 io.write("\n\n")
end
晕阿,注释方式是sql方式,最讨厌这种的注释,不知道支持/**/不
for 循环,省去c/c++ 运算符号啊。
for循环,要以end来结束,其函数结尾也以end结束。看来在做编译器时
其实就是要考虑不断减少关键字,同时看上去又非常简洁为好啊。
-----
在看demo2
for i=1,5 do
     print(“i is now “ .. i)
       if i < 2 then      
             print(“small”)  
       elseif i < 4 then              --这个elseif 太像python啦,看来脚本都是差不多阿
             print(“medium”)  
       else      
             print(“big”)  
       end                                --end就象是结束if判断,如果在if中有很多语句,
                                            --都无{}也无end,不像python还有格式规范
end
 
 
好像没什么了,其实就是不需要上面介绍,拿起代码来看都可以。
 
 
 
 
 
 

 
posted @ 2006-06-17 18:48 kenlistian 阅读(428) | 评论 (0)编辑 收藏

lua是一个脚本语言,可以嵌入到c/c++中,它是世界上最小巧的脚本语言。
学习它可以大大的扩充对数据设置方面的许多要求。
lua,在西班人称之月亮宝石,的确,把它掌握,把它嵌在vc之宝剑剑柄之上,
将让你砍杀一切“拦路之怪兽”增加魔法之光彩!
 
首先,lua可以运行在windows平台,但是你必须下载源码对它编译,下载地址在
http://www.lua.org/
 
如果采用vc6以上的话,编译文件在\LUA\lua-5.1.1\etc中的bat中,
如何构建lua,help有很清楚的解释
* Building Lua on Windows and other systems
  -----------------------------------------
  If you're not using the usual Unix tools, then the instructions for
  building Lua depend on the compiler you use. You'll need to create
  projects (or whatever your compiler uses) for building the library,
  the interpreter, and the compiler, as follows:
  library: lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c
  lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c
  ltable.c ltm.c lundump.c lvm.c lzio.c
  lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c
  ltablib.c lstrlib.c loadlib.c linit.c
  interpreter: library, lua.c
  compiler: library, luac.c print.c
  If you use Visual Studio .NET, you can use etc/luavs.bat
  in its "Command Prompt".
 
 If all you want is to build the Lua interpreter, you may put all .c files
  in a single project, except for luac.c and print.c. Or just use etc/all.c.
  To use Lua as a library in your own programs, you'll need to know how to
  create and use libraries with your compiler.
  As mentioned above, you may edit luaconf.h to select some features before
  building Lua.
 
在vc中做,很简单,如果想静态的,先生成一个空静态工程文件,加入以上文件,就建出library。lib文件,如果想生成动态的,则建一个动态工程文件,加入以上文件,就建成library。dll和library。lib文件。
 
如果要建一个一个编译器,则把上面的lib,和lua。c和print。c放在一个新win32控制台工程文件编译,形成了dos界面的exe,在dos下运行,生成的lua.exe如下
http://kk
脚本解释器,就是把lua。c和lib文件放在一个空win32工程文件编辑成luaw.exe
 
写一个简单的“hello world”文件,如下
 print( “hello world”)  保存为hello。lua,
 
在dos下运行huae hello。lua
 将会打印出字符串。
 
好了,第一篇介绍完。
 
 
 
 
 
 
 
 
posted @ 2006-06-17 17:53 kenlistian 阅读(331) | 评论 (0)编辑 收藏

这是我做的测试例子,考虑到一个线程用于去读数据库,一次性插入多条数据到共享数据区,另外搞2个上的线程去读这个共享数据区,以后打算搞个线程池来处理读部分。

目下的问题是我想把这个共享数据区做成可变化的动态区,当读入数据大时,一次读入全部读完。当数据量小时,则在规范范围的空间中运行。

采用vector<mystructData>方式动态变化,比如要删除超过长度之外的设置,只需要earse就可以了,在线程中每次通过begin,end自动扫描处理中数据部分。

如果把线程部分放到dll中,那么考虑到如何读取共享数据区,采用导出函数方式,把主线程中的一个函数地址传入到dll中,然后在这个函数中通过

find_if(a.begin(),a.end(),myfunc)照样可以处理dll中跑线程的问题,之所以考虑用dll分离各个线程部分,是考虑到业务不同时,每个dll处理扫描共享数据区,发现自己的业务就去处理,不是则不理会,这样考虑设计可以通过编写dll方式来扩展不同的业务。


#include "stdafx.h"
#include <vector>
#include <iostream>
#include <windows.h>
#include <process.h>
#include <algorithm>

using namespace std;

struct te
{
 int IsGet;
 int a1;
 int s1;
 char b1[20];
};

bool isget(te t)
{
 return t.IsGet ==1;
}
bool getdata(te t)
{
 return t.IsGet==0;
}
vector<te> a; 
HANDLE hMutex;

void myprint(te t)

 cout<<"   ALL "<< (t.IsGet == 0? "Have": "No ")<< "("<<t.a1<<")"<<t.s1<<" " << t.b1 <<endl;
  
}

DWORD WINAPI insertdata( LPVOID lpParam )

 vector<te>::iterator p; 
 int i = 0;
 while (1)
 {  

  do{
   
    p = find_if(a.begin(), a.end(), isget);  
    if (p!= a.end())
    {      
   WaitForSingleObject(hMutex, INFINITE);
   i++;
   p->IsGet = 0; 
   p->s1 = i;
   sprintf(p->b1, "%s- %d", "thread_insert" , i); 
      cout<<" insert("<<p->a1<<") " << p->s1<<"  " << p->b1 <<endl;  
      ReleaseMutex(hMutex);
    }
    //Sleep(1000);
   
  }
  while(p != a.end());
  
  WaitForSingleObject(hMutex, INFINITE);
  cout<<"---------------------------"<<endl;
  for_each(a.begin(), a.end(), myprint);
  ReleaseMutex(hMutex);  
  Sleep(11000);
 }


 return 0;
}

//读线程,读完对IsGet标志为1
DWORD WINAPI processdata( LPVOID lpParam )
{
 vector<te>::iterator p;
 
 while(1)
 {
  WaitForSingleObject(hMutex, INFINITE);
  p = find_if(a.begin(), a.end(), getdata);
  if (p != a.end())
  {   
    cout<<"("<< (char*)lpParam << ") get (" << p->a1<<") " << p->s1 <<" " << p->b1 <<endl;   
    p->IsGet = 1;   
    p->s1 = 0;
    p->b1[0] = '\0';
  
  }  
  ReleaseMutex(hMutex);
  Sleep(500);
 }

 return 0;
}

int main(int argc, char* argv[])
{
 
 HANDLE hInsertHandle, hProcessHandle1,hProcessHandle2;
 DWORD dwThreadId;

 te tt1;
 for(int i = 0; i < 10;i++)
 {
  tt1.IsGet = 1;             //可以写入标志
  tt1.a1 = i;
  tt1.s1 = 0;
  sprintf(tt1.b1,"%d", i);
  a.push_back(tt1);
 }

 hMutex = CreateMutex(NULL, false, "mutex");

  hInsertHandle = CreateThread(
        NULL,                 
        0,                    
  insertdata,        
        NULL,                   // argument to thread function
        0,                           // use default creation flags
        &dwThreadId);
 
  hProcessHandle1 = CreateThread(  //读出线程1
        NULL,                 
        0,                    
  processdata,        
        "proc1" ,              
        0,                     
        &dwThreadId);
    hProcessHandle2 = CreateThread(
        NULL,                 
        0,                    
  processdata,        
        "proc2",            
        0,                  
        &dwThreadId);

 
 while(1)
 { 
  printf("main thread ...\n");
  char c = getchar();
  if (c== 'q')
   break;
 }

 return 0;
}

posted @ 2006-05-28 15:27 kenlistian 阅读(3226) | 评论 (0)编辑 收藏

仅列出标题
共3页: 1 2 3