??xml version="1.0" encoding="utf-8" standalone="yes"?>婷婷综合久久狠狠色99h,久久国产免费直播,久久强奷乱码老熟女网站http://www.shnenglu.com/eday/安全Ҏ不{于安全的特?/description>zh-cnThu, 08 May 2025 20:52:32 GMTThu, 08 May 2025 20:52:32 GMT60利用宏来生成C++函数的注? 减少写代码时重复输入的烦?/title><link>http://www.shnenglu.com/eday/archive/2007/07/15/28079.html</link><dc:creator>独孤九剑</dc:creator><author>独孤九剑</author><pubDate>Sun, 15 Jul 2007 14:47:00 GMT</pubDate><guid>http://www.shnenglu.com/eday/archive/2007/07/15/28079.html</guid><description><![CDATA[     摘要:         在定义函数时Q一直以来都是手工加入函数注释,虽然每个函数的注释内容ƈ不是太多Q但L会有很多重复性的工作Q哎。这两天有点旉看看有什么方法了Q昨天发现原?Vistual Studio 2005里有个宏IDE工具可以实现我的目的Q嘿嘿见W,怎么早没发现Q,研究一下了...Q晕LBasic语言Q还好要实现我的功能...  <a href='http://www.shnenglu.com/eday/archive/2007/07/15/28079.html'>阅读全文</a><img src ="http://www.shnenglu.com/eday/aggbug/28079.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/eday/" target="_blank">独孤九剑</a> 2007-07-15 22:47 <a href="http://www.shnenglu.com/eday/archive/2007/07/15/28079.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用VS2005也能制作体积很小的Win32E序(2KB - 3KB)http://www.shnenglu.com/eday/archive/2007/05/23/24707.html独孤九剑独孤九剑Wed, 23 May 2007 11:51:00 GMThttp://www.shnenglu.com/eday/archive/2007/05/23/24707.html话就不多说了Q你一定能看懂。这里运行时库一定要指定?多线E?MT"Q否则最后Release版本的程序在其他机器上无法运行.
命o?/MT  ?


q里指定自定义入口函数名 命o?/entry:Start  ?img height=420 alt="" src="http://www.shnenglu.com/images/cppblog_com/eday/2991/o_vc8link.JPG" width=659 border=0>

//Win32控制台程?br>//----------------------------------------------
//stdafx.h file
//----------------------------------------------
#pragma once

#ifndef _WIN32_WINNT 
#define _WIN32_WINNT 0x0501
#endif      

#include <Windows.h>

//----------------------------------------------
//console.cpp
//----------------------------------------------

#include "stdafx.h"

HANDLE hStdIn;
HANDLE hStdOut;

BOOL __stdcall CtrlHandler(DWORD CtrlType)
{
    
if(CtrlType==CTRL_C_EVENT || CtrlType==CTRL_BREAK_EVENT)
        CloseHandle(hStdIn);
    
return TRUE;
}

void Start()
{
    hStdIn 
= GetStdHandle(STD_INPUT_HANDLE);
    hStdOut 
= GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleMode(hStdIn,ENABLE_LINE_INPUT
|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT);
    SetConsoleCtrlHandler(CtrlHandler,TRUE);

    HANDLE hHeap 
= GetProcessHeap();
    PVOID szBuffer 
= HeapAlloc(hHeap,HEAP_ZERO_MEMORY,1024);
    DWORD dwBytesRead,dwBytesWrite;
    
while(TRUE){
        
if(!ReadConsole(hStdIn,szBuffer,1024,&dwBytesRead,NULL) || ((char*)szBuffer)[0== 'q' )
            
break;
        WriteConsole(hStdOut,szBuffer,dwBytesRead,
&dwBytesWrite,NULL);
    }
    HeapFree(hHeap,HEAP_NO_SERIALIZE,szBuffer);
    ExitProcess(
0);
}


//WindowsE序
//----------------------------------------------------
//stdafx.h
//----------------------------------------------------
#ifndef WINVER    
#define WINVER 0x0501  
#endif

#ifndef _WIN32_WINNT  
#define _WIN32_WINNT 0x0501 
#endif      

#ifndef _WIN32_WINDOWS  
#define _WIN32_WINDOWS 0x0410
#endif

#ifndef _WIN32_IE   
#define _WIN32_IE 0x0600 
#endif

#define WIN32_LEAN_AND_MEAN  

#include <Windows.h>

//----------------------------------------------------
// winapp.cpp
//----------------------------------------------------

#include "stdafx.h"

void __stdcall Start()
{
    MessageBoxA(NULL,
"Hello World!","?",MB_OK);
    ExitProcess(
0);
}

// Release版本


//制作Win32 - DLL文g的方法与前面相同

以上仅?span style="COLOR: #ff0000">Windows标准?/span>Q最后生成的E序只有 3KB大小Q基本上和汇~写的程序大差不多了. 
 
如果要再点Q我们可以把 只读数据、导入表以及导出表节.rdata与代码节.text合ƈ?q里提到的节区是以VC~译器ؓ准,不同的编译器对节的命名也怼有些不同)
q接器命令行d /merge:.rdata=.text ?


现在再看看大?:(  2KB 了?br>

独孤九剑 2007-05-23 19:51 发表评论
]]>
从BMP得到ICON句柄最单的Ҏhttp://www.shnenglu.com/eday/archive/2007/05/05/23459.html独孤九剑独孤九剑Sat, 05 May 2007 12:37:00 GMThttp://www.shnenglu.com/eday/archive/2007/05/05/23459.html
    HBITMAP newimg = (HBITMAP)LoadImage(AfxGetInstanceHandle(),_T("f:\\1.bmp"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
    
if(newimg)
    {
        CBitmap 
*pBitmap = CBitmap::FromHandle(newimg);
        BITMAP bmpData;
        
if(pBitmap->GetBitmap(&bmpData))
        {
            ICONINFO iconInfo 
= { true, bmpData.bmWidth/2, bmpData.bmHeight/2, newimg, newimg  };
            HICON bIcon 
= CreateIconIndirect(&iconInfo);
            
if(bIcon)
            {
                CClientDC dc(
this);
                dc.DrawIcon(
0,0,bIcon);
                ::DestroyIcon(bIcon);
            }
        }
    }

昄ICON仅仅Z演示Q?q里主要的目的是得到 HICON 句柄?

独孤九剑 2007-05-05 20:37 发表评论
]]>
在CWndzH口中动态创建CheckBox,RadioButton, 去除控g背景(WM_PAINT)http://www.shnenglu.com/eday/archive/2007/04/19/22263.html独孤九剑独孤九剑Wed, 18 Apr 2007 18:04:00 GMThttp://www.shnenglu.com/eday/archive/2007/04/19/22263.html
template<class BASE_CLASS>
class CTransparentButton : public BASE_CLASS
{
public:
    
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
        
switch (message)
        {
        
case WM_PAINT:
            {
                CPaintDC dc(
this);
                CString iText;
                CRect boxRc, textRc;
                
int wh = 14;
                boxRc.SetRectEmpty();
                GetClientRect(
&textRc);
                textRc.bottom = 20;
                boxRc.top 
= ((int)textRc.Height()/2)-7;
                textRc.left 
= boxRc.right = boxRc.bottom = boxRc.top+wh;
                textRc.left 
+= 2;
                
switch(GetButtonStyle())
                {
                
case BS_AUTOCHECKBOX:
                    {
                        
if(GetCheck())
                            dc.DrawFrameControl(
&boxRc,DFC_BUTTON,DFCS_CHECKED);            
                        
else
                            dc.DrawFrameControl(
&boxRc,DFC_BUTTON,DFCS_BUTTONCHECK);
                    }
                    
break;
                
case BS_AUTORADIOBUTTON:
                    {
                        
if(GetCheck())
                        {
                            dc.DrawFrameControl(
&boxRc,DFC_BUTTON,DFCS_BUTTONRADIO);
                            boxRc.DeflateRect(
6,5);
                            CBrush bkBrush(RGB(
0,0,0));
                            dc.SelectObject(
&bkBrush);
                            dc.RoundRect(
&boxRc,CPoint(boxRc.Width()/2,boxRc.Height()/2));
                        }
                        
else
                            dc.DrawFrameControl(
&boxRc,DFC_BUTTON,DFCS_BUTTONRADIO);
                    }
                    
break;
                }
                GetWindowText(iText);
                dc.SelectStockObject(DEFAULT_GUI_FONT);
                dc.DrawText(iText,
&textRc,DT_VCENTER|DT_SINGLELINE);
            }
            
break;
        }
        
return BASE_CLASS::WindowProc(message, wParam, lParam);
    }
};

使用ҎQ?br>protected:
        CTransparentButton<CButton>  m_checkbox;
        CTransparentButton<CButton>  m_radiobtn1;
        CTransparentButton<CButton>  m_radiobtn2;

//-----------------------------------------------------------------
CCustomWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
        if (CWnd::OnCreate(lpCreateStruct) == -1)
                return -1;
         //创徏控g
         m_checkbox.Create(_T("CheckBox"),WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
                CRect(0,20,80,40), this, IDC_CHECKBOX1);
         m_radiobtn1.Create(_T("RadioButton1"),WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON ,
                CRect(0,50,80,70), this, IDC_RADIOBTN1);
         m_radiobtn2.Create(_T("RadioButton2"),WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON ,
                CRect(0,80,80,100), this, IDC_RADIOBTN2);
         //....................
}
      

独孤九剑 2007-04-19 02:04 发表评论
]]>
Win32 调用pȝ命o取得l果http://www.shnenglu.com/eday/archive/2007/03/29/20811.html独孤九剑独孤九剑Wed, 28 Mar 2007 16:16:00 GMThttp://www.shnenglu.com/eday/archive/2007/03/29/20811.html
// ----------------------------------------------------------------------------------
//  Use:> 
//         CString resultContext;
//         ExecuteCmdEx( (LPTSTR)(LPCTSTR)CString("net help"), resultContext);
// ----------------------------------------------------------------------------------
BOOL ExecuteCmdEx(LPTSTR cmdline, CString &  outputResult)
{
    SECURITY_ATTRIBUTES sa;
    sa.nLength 
=   sizeof (SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor 
=  NULL;
    sa.bInheritHandle 
=  TRUE;
    HANDLE hInput,hOutput;
    
if  ( ! CreatePipe( & hInput, & hOutput, & sa, 0 )) 
        
return  FALSE;

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory( 
& si,  sizeof (si) );
    ZeroMemory( 
& pi,  sizeof (pi) );
    si.cb 
=   sizeof (si);
    si.hStdError 
=  hOutput;
    si.hStdOutput 
=  hOutput;
    si.wShowWindow 
=  SW_HIDE;
    si.dwFlags 
=  STARTF_USESHOWWINDOW  |  STARTF_USESTDHANDLES;   
    
if  ( ! CreateProcess(NULL,cmdline,NULL,NULL,TRUE,NULL,NULL,NULL, & si, & pi)){
        CloseHandle(hInput);
        CloseHandle(hOutput);
        
return  FALSE;
    }
    WaitForSingleObject( pi.hProcess, INFINITE );
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

    DWORD rByte 
=   4095 ;
    
char  outputBuffer[ 4096 ];
    
while (rByte == 4095 ){
        ZeroMemory(outputBuffer,
sizeof ( char ) * 4096 );
        ReadFile(hInput,outputBuffer,
sizeof ( char ) * 4095 , & rByte, NULL);
        outputResult 
+=  outputBuffer;
    }
    CloseHandle(hInput);
    CloseHandle(hOutput);

    
return  TRUE;
}


独孤九剑 2007-03-29 00:16 发表评论
]]>
解决H口h闪烁http://www.shnenglu.com/eday/archive/2007/03/19/20106.html独孤九剑独孤九剑Sun, 18 Mar 2007 16:14:00 GMThttp://www.shnenglu.com/eday/archive/2007/03/19/20106.html一般的windows 复杂的界面需要用多层窗口而且要用贴图来美化,所以不可避免在HUd或者改变大的时候出现闪烁?/p>

先来谈谈闪烁产生的原?/p>

原因一Q?br />如果熟悉昑֍原理的话Q调用GDI函数向屏q输出的时候ƈ不是立刻显C在屏幕
上只是写C昑֭里,而显卡每隔一D|间把昑֭的内容输出到屏幕上,q就是刷新周期?/p>

一般显卡的h周期?1/80U左叻I具体数字可以自己讄的?/p>

q样问题来了,一般画N是先画背景色Q然后再把内容画上去Q如果这两次操作不在同一?br />h周期内完成,那么lh的视觉感受就是,先看到只有背景色的图像,然后看到M内容的图像,
q样׃感觉闪烁了?/p>

解决ҎQ尽量快的输出图像,使输出在一个刷新周期内完成Q如果输出内容很多比较慢Q那么采?br />内存~冲的方法,先把要输出的内容在内存准备好Q然后一ơ输出到昑֭。要知道一ơAPI调用一般可?br />在一个刷新周期内完成?/p>

对于GDIQ用创徏内存DC的方法就可以?/p>

原因二:

复杂的界面有多层H口l成Q当windows在窗口改变大的时候是先重ȝH口Q然后重dH口Q子?br />H口重画的过E一般无法在一个刷新周期内完成Q所以会呈现闪烁?/p>

我们知道父窗口上被子H口挡住的部分其实没必要重画?/p>

解决ҎQ给H口加个风格 WS_CLIPCHILDREN ,q样父窗口上被子H口挡住的部分就不会重画了?/p>

如果同H口之间有重叠,那么需要再加上 WS_CLIPSIBLINGS 风格

原因三:

有时候需要在H口上用一些控Ӟ比如IEQ当你的H口改变大小的时候IE会闪烁,即你有了WS_CLIPCHILDREN
也没用。原因在于窗口的c风格有CS_HREDRAW 或?CS_VREDRAWQ这两个风格表示H口在宽度或者高度变化的时?br />重画Q但是这样就会引起IE闪烁

解决ҎQ注册窗口类的时候不要用这两个风格Q如果窗口需要在改变大小的时候重画,那么可以在WM_SIZE的时?br />调用RedrawWindow?/p>

原因四:

界面上窗口很多,而且改变大小时很多窗口都要移动和改变大小Q如果用MoveWindow或者SetWindowPos两个API?br />改变H口的大和位置Q由于他们是{待H口重画完成后才q回Q所以过E很慢,q样视觉效果可能会闪烁?/p>

解决ҎQ?/p>

使用以下API来处理窗口移动,BeginDeferWindowPos, DeferWindowPosQEndDeferWindowPos
先调?BeginDeferWindowPos 讑֮需要移动的H口的个?br />使用DeferWindowPosQ来UdH口Q这个APIq不真的造成HUd
EndDeferWindowPos 一ơ性完成所有窗口的大小和位|的改变?/p>

有个地方要特别注意,要仔l计清楚要Ud多少个窗口,BeginDeferWindowPos讑֮
的个C定要和实际的个数一_否则在Win9x下,如果实际Ud的窗口数多于调用BeginDeferWindowPos
时设定的个数Q可能会造成pȝ崩溃。在Windows NTpd下不会有q样的问题?/p>

如果你在属性里讄?拖动H口昄H口内容的话Q屏q看h会闪许多。你可以通过api SystemParametersQ) Q把它去掉在你的应用E序里。这样在用户看来会好一炏V这只是我个人徏议?/p>

----------------------------

        1、将Invalidate()替换为InvalidateRect()

  Invalidate()会导致整个窗口的图象重画Q需要的旉比较长,而InvalidateRect()仅仅重画Rect区域内的内容Q所以所需旉会少一些。虫虫以前很懒,l常Z块区域的重d调用Invalidate()Q不愿意自己去计需要重ȝRectQ但是事实是Q如果你实需要改善闪烁的情况Q计一个Rect所用的旉比v重画那些不需要重ȝ内容所需要的旉要少得多?

  2、禁止系l搽除你的窗?br />
  pȝ在需要重ȝ口的时候会帮你用指定的背景色来搽除H口。可是,也许需要重ȝ区域也许非常。或者,在你重画q些东西之间q要l过大量的计才能开始。这个时候你可以止pȝ搽掉原来的图象。直C已经计算好了所有的数据Q自己把那些需要搽掉的部分用背景色覆盖掉(如:dc.FillRect(rect,&brush);rect是需要搽除的区域Qbrush是带背景色的刷子Q,再画上新的图形。要止pȝ搽除你的H口Q可以重载OnEraseBkgnd()函数Q让其直接返回pUE可以了。如

BOOL CMyWin::OnEraseBkgnd(CDC* pDC)
{
 return pUE;
 //return CWnd::OnEraseBkgnd(pDC);//把系l原来的q条语句注释掉?
}

  3、有效的q行搽除

  搽除背景的时候,不要该搽不该搽的地方都搽。比如,你在一个窗口上放了一个很大的Edit框,几乎占了整个H口Q那么你频繁的搽除整个窗口背景将DEdit不停重画形成剧烈的闪烁。事实上你可以CRgn创徏一个需要搽除的区域Q只搽除q一部分。如

GetClientRect(rectClient);
rgn1.CreateRectRgnIndirect(rectClient);
rgn2.CreateRectRgnIndirect(m_rectEdit);
if(rgn1.CombineRgn(&rgn1,&rgn2,RGN_XOR) == ERROR)//处理后的rgn1只包括了Edit框之外的客户区域Q这PEdit不会被我的背景覆盖而导致重甅R?
{
 ASSERT(FALSE);
 return ;
}
brush.CreateSolidBrush(m_clrBackgnd);
pDC->FillRgn(&rgn1,&brush);
brush.DeleteObject();

  注意Q在使用q个Ҏ的时候要同时使用Ҏ二。别忘了Q到时候又说虫虫的办法不灵?

  4、用MemoryDC先在内存里把囄好,再复制到屏幕?br />
  q对于一ơ画图过E很长的情况比较用。毕竟内存操作比较快Q而且复制到屏q又是一ơ性的Q至不会出现可以明昄Z个东东从左画到右的情c?

void CMyWin::OnPaint()
{
 CPaintDC dc1(this); // device context for painting
 dcMemory.CreateCompatibleDC(&dc1);
 CBitmap bmp;//q里的Bitmap是必ȝQ否则当心弄Z个大黑块哦?
 bmp.CreateCompatibleBitmap(&dc1,rectClient.Width(),rectClient.Height());
 dcMemory.SelectObject(&bmp);

 //接下来你x么d怎么d?
 //dcMemory.FillRect(rectClient,&brush);

 dc1.BitBlt(0,0,rectClient.Width(),rectClient.Height(),&dcMemory,0,0,SRCCOPY);
 dcMemory.DeleteDC();
 // Do not call CWnd::OnPaint() for painting messages
}

 争议

  上述Ҏ实有效Q但在有很多控g的情况下Q计一个窗口中需要擦除ƈ重绘的“空白区域”是一件很ȝ的事情。ؓ了方便这U方法的实际应用Q我写了一l宏来完成”计空白区域“的功能Q?br />

/************************************************************************/
/* MFC?br />/* 宏功? 界面h时仅h指定控g以外的空白区?可有效避免窗口闪?br />/* 使用? WM_ERASEBKGND 消息处理函数/************************************************************************/
#define ERASE_BKGND_BEGIN \
CRect bgRect;\
GetClientRect(&bgRect);\
CRgn bgRgn;\
bgRgn.CreateRectRgnIndirect(bgRect);
//#define ERASE_BKGND_BEGIN
// Marco parameter 'IDC' specifies the identifier of the control
#define ADD_NOERASE_CONTROL(IDC)\
{\
 CRect controlRect;\
 GetDlgItem(IDC)->GetWindowRect(&controlRect);\
 CRgn controlRgn;\
 controlRgn.CreateRectRgnIndirect(controlRect);\
 if(bgRgn.CombineRgn(&bgRgn, &controlRgn, RGN_XOR)==ERROR)\
  return false;\
}

// Marco parameter 'noEraseRect' specifies a screen coordinates based RECT,
// which needn't erase.
#define ADD_NOERASE_RECT(noEraseRect)\
{\
 CRgn noEraseRgn;\
 noEraseRgn.CreateRectRgnIndirect(noEraseRect);\
 if(bgRgn.CombineRgn(&bgRgn, &noEraseRgn, RGN_XOR)==ERROR)\
  return false;\
}

// Marco parameter 'pDC' is a kind of (CDC *) type.
// Marco parameter 'clBrushColor' specifies the color to brush the area.
#define ERASE_BKGND_END(pDC, clBrushColor)\
CBrush brush;\
brush.CreateSolidBrush(clBrushColor);\
CPoint saveOrg = (pDC)->GetWindowOrg();\
(pDC)->SetWindowOrg(bgRect.TopLeft());\
(pDC)->FillRgn(&bgRgn, &brush);\
(pDC)->SetWindowOrg(saveOrg);\
brush.DeleteObject();\
//#define ERASE_BKGND_END
/*************************************************/


/************************************************************************/
/* WTL?br />/* 宏功? 界面h时仅h指定控g以外的空白区?可有效避免窗口闪?br />/* 使用? WM_ERASEBKGND 消息处理函数
/************************************************************************/
#define ERASE_BKGND_BEGIN \
 CRect bgRect;\
 GetClientRect(&bgRect);\
 CRgn bgRgn;\
 bgRgn.CreateRectRgnIndirect(bgRect);
//#define ERASE_BKGND_BEGIN
// Marco parameter 'IDC' specifies the identifier of the control
#define ADD_NOERASE_CONTROL(IDC)\
{\
 CRect controlRect;\
 GetDlgItem(IDC)->GetWindowRect(&controlRect);\
 CRgn controlRgn;\
 controlRgn.CreateRectRgnIndirect(controlRect);\
 if(bgRgn.CombineRgn(&bgRgn, &controlRgn, RGN_XOR)==ERROR)\
  return false;\
}

// Marco parameter 'noEraseRect' specifies a screen coordinates based RECT,
// which needn't erase.
#define ADD_NOERASE_RECT(noEraseRect)\
{\
 CRgn noEraseRgn;\
 noEraseRgn.CreateRectRgnIndirect(noEraseRect);\
 if(bgRgn.CombineRgn(bgRgn.m_hRgn, noEraseRgn.m_hRgn, RGN_XOR)==ERROR)\
  return false;\
}

// Marco parameter 'pDC' is a kind of (CDC *) type.
// Marco parameter 'clBrushColor' specifies the color to brush the area.
#define ERASE_BKGND_END(pDC, clBrushColor)\
 CBrush brush;\
 brush.CreateSolidBrush(clBrushColor);\
 CPoint saveOrg;\
 (pDC)->GetWindowOrg(&saveOrg);\
 (pDC)->SetWindowOrg(bgRect.TopLeft());\
 (pDC)->FillRgn(bgRgn.m_hRgn, brush.m_hBrush);\
 (pDC)->SetWindowOrg(saveOrg);\
 brush.DeleteObject();\
//#define ERASE_BKGND_END
/*************************************************/


  说明Q?br />
  1)?ERASE_BKGND_BEGIN ?ERASE_BKGND_END(pDC, clBrushColor) 搭配使用?br />
  2)?ADD_NOERASE_CONTROL(IDC) ?ADD_NOERASE_RECT(noEraseRect) Ҏ需要放在上面两个宏的中_用来d不需要重l背景的区域(正是q些区域D了闪?Q用次C限。其中参数noEraseRect是一个屏q坐标系的RECTcd或CRectcd?br />
  使用举例1Q?br />
  在当前窗体的cM重写WM_ERASEBKGND消息处理函数如下Q?br />
BOOL CMyWnd::OnEraseBkgnd(CDC* pDC)
{
 ERASE_BKGND_BEGIN;
 ADD_NOERASE_RGN(IDC_BUTTON2);
 ADD_NOERASE_RGN(IDC_BUTTON1);
 ADD_NOERASE_RGN(IDC_LIST_STAT);
 ERASE_BKGND_END(pDC, GetSysColor(COLOR_3DFACE));
 return false;
}

  上面的IDC_BUTTON2QIDC_BUTTON1QIDC_LIST_STAT即窗体上的控件?br />
  你可以指定其他已存在的控件?br />
  q样Q窗口在擦除背景Ӟ只对上q控件以后的”空白区域“用系l色重绘Q有效避免了闪烁?br />
  备注Q?br />
  重蝲WM_ERASEBKGND消息处理函数OnEraseBkgnd的方法,选择View->ClassWizard->classinfo选项?message filter下拉?

  选择window,然后再选择message maps选项?在messages下拉框应该可以找到wm_erasebkgnd.双击d.

  使用举例2Q防止CListCtrl在拉动窗口时闪烁?br />
/* * No further full-erasing is required,
* to prevent screen flashing caused by background erase and view repaint.
* Only erase the blank area.
*/

BOOL CExListCtrl::OnEraseBkgnd(CDC* pDC) {
 //compute the holding-data-items area of this list control CRect rect;
 CPoint dataRgnTopLeftPoint;
 CPoint dataRgnBottomRightPoint;
 GetItemPosition(0 , &dataRgnTopLeftPoint);
 GetItemPosition(GetItemCount() , &dataRgnBottomRightPoint);
 if(!GetHeaderCtrl()->GetItemRect(GetHeaderCtrl()->GetItemCount()-1, rect)) return
 CListCtrl::OnEraseBkgnd(pDC);
 dataRgnBottomRightPoint.x = rect.right;
 rect.SetRect(dataRgnTopLeftPoint, (CPoint)(dataRgnBottomRightPoint - CPoint(2,2)));
 ClientToScreen(dataRgnRect);
 //compute and erase the blank area. Using the Marco. ERASE_BKGND_BEGIN;
 ADD_NOERASE_RECT(dataRgnRect);
 ERASE_BKGND_END(pDC, GetBkColor());
 return false;
}

  说明QCListCtrl在拉动的时候,会前以背景色重刷背景Q再在上面绘制有数据的ItemsQ?而没有数据的区域则保持背景色。因此,如果在BOOL CExListCtrl::OnEraseBkgnd(CDC* pDC) 函数中简单的return falseQ那么没有数据的区域显CZ正常?故D?中先计算出有数据的items的区域,q是不需要以背景重刷的区域?再用本文的宏,可以有效避免CListCtrl在拉动时候的闪烁?img src ="http://www.shnenglu.com/eday/aggbug/20106.html" width = "1" height = "1" />

独孤九剑 2007-03-19 00:14 发表评论
]]>
鼠标屏幕取词技术的原理和实?/title><link>http://www.shnenglu.com/eday/archive/2007/02/28/19065.html</link><dc:creator>独孤九剑</dc:creator><author>独孤九剑</author><pubDate>Wed, 28 Feb 2007 12:44:00 GMT</pubDate><guid>http://www.shnenglu.com/eday/archive/2007/02/28/19065.html</guid><description><![CDATA[“鼠标屏q取词”技术是在电子字怸得到q泛地应用的Q如四通利方和金山词霸{YӞq个技术看似简单,其实在WINDOWSpȝ中实现却是非常复杂的Qȝ来说有两U实现方式:<br />    W一U:采用截获寚w分GDI的API调用来实?如TextOut,TextOutA{?br />    W二U:Ҏ个设备上下文(DC)做一分Copy,q跟t所有修改上下文(DC)的操作。     ?br /> <br />   W二U方法更强大,但兼Ҏ不好,而第一U方法用的截获WindowsAPI的调用,q项技术的强大可能q远出了您的想象,毫不夸张的说Q利用WindowsAPI拦截技术,你可以改造整个操作系l,事实上很多外挂式Windows中文q_是q么实现的!而这Ҏ术也正是q篇文章的主题?br /><br />    截WindowsAPI的调用,具体的说来也可以分ؓ两种ҎQ?br />    W一U方法通过直接改写WinAPI 在内存中的映像,嵌入汇编代码Q之被调用时蟩转到指定的地址q行来截PW二U方法则改写IATQImport Address Table 输入地址表)Q重定向WinAPI函数的调用来实现对WinAPI的截莗?br /><br />    W一U方法的实现较ؓJ琐Q而且在Win95?8下面更有隑ֺQ这是因然微软说WIN16的API只是Z兼容性才保留下来Q程序员应该可能地调用32位的API,实际上根本就不是q样QWIN 9X内部的大部分32位APIl过变换调用了同名的16位APIQ也是说我们需要在拦截的函C嵌入16位汇~代码!<br /><br />    我们要介绍的是W二U拦截方法,q种Ҏ在Win95?8和NT下面q行都比较稳定,兼容性较好。由于需要用到关于Windows虚拟内存的管理、打破进E边界墙、向应用E序的进E空间中注入代码、PEQPortable ExecutableQ文件格式和IATQ输入地址表){较底层的知识,所以我们先Ҏ及到的这些知识大概地做一个介l,最后会l出拦截部分的关键代码?br /><br />      先说Windows虚拟内存的管理。Windows9Xl每一个进E分配了4GB的地址I间Q对于NT来说Q这个数字是2GBQ系l保留了2GB?4GB之间的地址I间止q程讉KQ而在Win9X中,2GB?GBq部分虚拟地址I间实际上是由所有的WIN32q程所׃n的,q部分地址I间加蝲了共享Win32 DLL、内存映文件和VXD、内存管理器和文件系l码QWin9X中这部分对于每一个进E都是可见的Q这也是Win9X操作pȝ不够健壮的原因。Win9X中ؓ16位操作系l保留了0?MB的地址I间Q而在4MB?GB之间也就是Win32q程U有的地址I间Q由?每个q程的地址I间都是相对独立的,也就是说Q如果程序想截获其它q程中的API调用Q就必须打破q程边界墙,向其它的q程中注入截获API调用的代码,q项工作我们交给钩子函数QSetWindowsHookExQ来完成Q关于如何创Z个包含系l钩子的动态链接库Q《电脑高手杂志》在W?期已l有q专题介l了Q这里就不赘qC。所有系l钩子的函数必须要在动态库里,q样的话Q当q程隐式或显式调用一个动态库里的函数Ӟpȝ会把q个动态库映射到这个进E的虚拟地址I间里,q得DLL成ؓq程的一部分Q以q个q程的n份执行,使用q个q程的堆栈,也就是说动态链接库中的代码被钩子函数注入了其它GUIq程的地址I间Q非GUIq程Q钩子函数就无能为力了)Q?br />当包含钩子的DLL注入其它q程后,可以取得映到q个q程虚拟内存里的各个模块QEXE和DLLQ的基地址Q?br />如:HMODULE hmodule=GetModuleHandle(“Mypro.exe?;<br /><br />在MFCE序?我们可以用AfxGetInstanceHandle()函数来得到模块的基地址。EXE和DLL被映到虚拟内存I间的什么地Ҏ由它们的基地址军_的。它们的基地址是在链接时由链接器决定的。当你新Z个Win32工程ӞVCQ+链接器用缺省的基地址0x00400000。可以通过链接器的BASE选项改变模块的基地址。EXE通常被映到虚拟内存?x00400000处,DLL也随之有不同的基地址Q通常被映到不同q程<br />的相同的虚拟地址I间处?br />pȝEXE和DLL原封不动映射到虚拟内存空间中Q它们在内存中的l构与磁盘上的静态文件结构是一L。即PE (Portable Executable) 文g格式。我们得Cq程模块的基地址以后Q就可以ҎPE文g的格式穷举这个模块的IMAGE_IMPORT_DESCRIPTOR数组Q看看进E空间中是否引入了我们需要截L函数所在的动态链接库Q比如需要截获“TextOutA”,必L查“Gdi32.dll”是否被引入了。说到这里,我们有必要介l一下PE文g的格式,如右图,q是PE文g格式的大致框图,最前面是文件头Q我们不必理会,从PE File Optional Header后面开始,是文g中各个段的说明,说明后面才是真正的段数据Q而实际上我们兛_的只有一个段Q那是?idata”段Q这个段中包含了所有的引入函数信息Q还有IATQImport Address TableQ的RVAQRelative Virtual AddressQ地址?br />说到q里Q截获WindowsAPI的整个原理就要真相大白了。实际上所有进E对l定的API函数的调用L通过PE文g的一个地Ҏ转移的,q就是一个该模块(可以是EXE或DLL)的?idata”段中的IAT输入地址表(Import Address TableQ。在那里有所有本模块调用的其它DLL的函数名及地址。对其它DLL的函数调用实际上只是跌{到输入地址表,p入地址表再跌{到DLL真正的函数入口? <p></p><p>具体来说Q我们将通过IMAGE_IMPORT_DESCRIPTOR数组来访问?idata”段中引入的DLL的信息,然后通过IMAGE_THUNK_DATA数组来针对一个被引入的DLL讉K该DLL中被引入的每个函数的信息Q找到我们需要截L函数的蟩转地址Q然后改成我们自q函数的地址……具体的做法在后面的关键代码中会有详l的讲解?br />   讲了q么多原理,现在让我们回到“鼠标屏q取词”的专题上来。除了API函数的截P要实现“鼠标屏q取词”,q需要做一些其它的工作Q简单的说来Q可以把一个完整的取词q程归纳成以下几个步骤:<br />1Q?安装鼠标钩子Q通过钩子函数获得鼠标消息?br />使用到的API函数QSetWindowsHookEx<br />2Q?得到鼠标的当前位|,向鼠标下的窗口发重画消息Q让它调用系l函数重ȝ口?br />     使用到的API函数QWindowFromPointQScreenToClientQInvalidateRect<br />3Q?截获对系l函数的调用Q取得参敎ͼ也就是我们要取的词?br />对于大多数的Windows应用E序来说Q如果要取词Q我们需要截L是“Gdi32.dll”中的“TextOutA”函数?br />我们先仿照TextOutA函数写一个自qMyTextOutA函数Q如Q?br />BOOL WINAPI MyTextOutA(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString,int cbString)<br />{<br />       // q里q行输出lpszString的处?br />           // 然后调用正版的TextOutA函数<br />}<br />把这个函数放在安装了钩子的动态连接库中,然后调用我们最后给出的HookImportFunction函数来截莯E?br />对TextOutA函数的调用,跌{到我们的MyTextOutA函数Q完成对输出字符串的捕捉。HookImportFunction?br />用法Q?br /> HOOKFUNCDESC hd;<br /> PROC         pOrigFuns;<br /> hd.szFunc="TextOutA";<br /> hd.pProc=(PROC)MyTextOutA;<br /> HookImportFunction (AfxGetInstanceHandle(),"gdi32.dll",&hd,pOrigFuns);<br /><br />下面l出了HookImportFunction的源代码Q相信详的注释一定不会让您觉得理解截获到底是怎么实现?br />很难QOk,Let’s GoQ?</p><p></p><p>///////////////////////////////////////////// Begin ///////////////////////////////////////////////////////////////<br />#include <crtdbg.h></p><p>// q里定义了一个生指针的?br />#define MakePtr(cast, ptr, AddValue) (cast)((DWORD)(ptr)+(DWORD)(AddValue))</p><p>// 定义了HOOKFUNCDESCl构,我们用这个结构作为参ClHookImportFunction函数<br />typedef struct tag_HOOKFUNCDESC<br />{<br />  LPCSTR szFunc; // The name of the function to hook.<br />  PROC pProc;    // The procedure to blast in.<br />} HOOKFUNCDESC , * LPHOOKFUNCDESC;</p><p>// q个函数监测当前pȝ是否是WindowNT<br />BOOL IsNT();</p><p>// q个函数得到hModule -- x们需要截L函数所在的DLL模块的引入描q符(import descriptor)<br />PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportModule);</p><p>// 我们的主函数<br />BOOL HookImportFunction(HMODULE hModule, LPCSTR szImportModule, <br />                         LPHOOKFUNCDESC paHookFunc, PROC* paOrigFuncs)<br />{<br />/////////////////////// 下面的代码检参数的有效?////////////////////////////<br /> _ASSERT(szImportModule);<br /> _ASSERT(!IsBadReadPtr(paHookFunc, sizeof(HOOKFUNCDESC)));<br />#ifdef _DEBUG<br /> if (paOrigFuncs) _ASSERT(!IsBadWritePtr(paOrigFuncs, sizeof(PROC)));<br /> _ASSERT(paHookFunc.szFunc);<br /> _ASSERT(*paHookFunc.szFunc != '\0');<br />        _ASSERT(!IsBadCodePtr(paHookFunc.pProc));<br />#endif<br /> if ((szImportModule == NULL) || (IsBadReadPtr(paHookFunc, sizeof(HOOKFUNCDESC))))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return FALSE;<br /> }<br />//////////////////////////////////////////////////////////////////////////////</p><p> // 监测当前模块是否是在2GB虚拟内存I间之上<br /> // q部分的地址内存是属于Win32q程׃n?br /> if (!IsNT() && ((DWORD)hModule >= 0x80000000))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_HANDLE, SLE_ERROR);<br />  return FALSE;<br /> }<br />     // 清零<br /> if (paOrigFuncs) memset(paOrigFuncs, NULL, sizeof(PROC)); </p><p> // 调用GetNamedImportDescriptor()函数,来得到hModule -- x们需?br /> // 截获的函数所在的DLL模块的引入描q符(import descriptor)<br /> PIMAGE_IMPORT_DESCRIPTOR pImportDesc = GetNamedImportDescriptor(hModule, szImportModule);<br /> if (pImportDesc == NULL)<br /> return FALSE; // 若ؓI?则模块未被当前进E所引入</p><p> //  从DLL模块中得到原始的THUNK信息,因ؓpImportDesc->FirstThunk数组中的原始信息已经<br /> //  在应用程序引入该DLL时覆盖上了所有的引入信息,所以我们需要通过取得pImportDesc->OriginalFirstThunk<br /> //  指针来访问引入函数名{信?br /> PIMAGE_THUNK_DATA pOrigThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, <br />                                               pImportDesc->OriginalFirstThunk);<br /><br /> </p><p></p><p> //  从pImportDesc->FirstThunk得到IMAGE_THUNK_DATA数组的指?׃q里在DLL被引入时已经填充?br /> //  所有的引入信息,所以真正的截获实际上正是在q里q行?br /> PIMAGE_THUNK_DATA pRealThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->FirstThunk);</p><p> //  IDIMAGE_THUNK_DATA数组,L我们需要截L函数,q是最关键的部?<br /> while (pOrigThunk->u1.Function)<br /> {<br />  // 只寻N些按函数名而不是序号引入的函数<br />  if (IMAGE_ORDINAL_FLAG != (pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG))<br />  {<br />   // 得到引入函数的函数名<br />   PIMAGE_IMPORT_BY_NAME pByName = MakePtr(PIMAGE_IMPORT_BY_NAME, hModule,<br />               pOrigThunk->u1.AddressOfData);</p><p>   // 如果函数名以NULL开?跌,l箋下一个函敊W?<br />   if ('\0' == pByName->Name[0])<br />    continue;</p><p>   // bDoHook用来查是否截h?br />   BOOL bDoHook = FALSE;</p><p>   // 查是否当前函数是我们需要截L函数<br />   if ((paHookFunc.szFunc[0] == pByName->Name[0]) &&<br />    (strcmpi(paHookFunc.szFunc, (char*)pByName->Name) == 0))<br />   {<br />    // 扑ֈ?<br />    if (paHookFunc.pProc)<br />    bDoHook = TRUE;<br />   }<br />   if (bDoHook)<br />   {<br />    // 我们已经扑ֈ了所要截L函数,那么开始动手吧<br />    // 首先要做的是改变q一块虚拟内存的内存保护状?让我们可以自由存?br />    MEMORY_BASIC_INFORMATION mbi_thunk;<br />    VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));<br />    _ASSERT(VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, <br />                        PAGE_READWRITE, &mbi_thunk.Protect));</p><p>    // 保存我们所要截L函数的正蟩转地址<br />    if (paOrigFuncs)<br />      paOrigFuncs = (PROC)pRealThunk->u1.Function;</p><p>    // IMAGE_THUNK_DATA数组中的函数跌{地址改写为我们自q函数地址!<br />    // 以后所有进E对q个pȝ函数的所有调用都成为对我们自己~写的函数的调用<br />    pRealThunk->u1.Function = (PDWORD)paHookFunc.pProc;</p><p>    // 操作完毕!这一块虚拟内存改回原来的保护状?br />    DWORD dwOldProtect;<br />    _ASSERT(VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, <br />                        mbi_thunk.Protect, &dwOldProtect));<br />    SetLastError(ERROR_SUCCESS);<br />    return TRUE;<br />   }<br /><br />  }<br />  // 讉KIMAGE_THUNK_DATA数组中的下一个元?br />  pOrigThunk++;<br />  pRealThunk++;<br /> }<br /> return TRUE;<br />} </p><p></p><p>// GetNamedImportDescriptor函数的实?br />PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportModule)<br />{<br /> // 参?br /> _ASSERT(szImportModule);<br /> _ASSERT(hModule);<br /> if ((szImportModule == NULL) || (hModule == NULL))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return NULL;<br /> }</p><p> // 得到Dos文g?br /> PIMAGE_<a target="_blank">DOS</a>_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;</p><p> // 是否MZ文g?br /> if (IsBadReadPtr(pDOSHeader, sizeof(IMAGE_DOS_HEADER)) || <br />  (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return NULL;<br /> }</p><p> // 取得PE文g?br /> PIMAGE_NT_HEADERS pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew);</p><p> // 是否PE映像文g<br /> if (IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) || <br />   (pNTHeader->Signature != IMAGE_NT_SIGNATURE))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return NULL;<br /> }</p><p> // 查PE文g的引入段(?.idata section)<br /> if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)<br />  return NULL;</p><p> // 得到引入D??.idata section)的指?br /> PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDOSHeader,<br />  pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);</p><p> // IDPIMAGE_IMPORT_DESCRIPTOR数组L我们需要截L函数所在的模块<br /> while (pImportDesc->Name)<br /> {<br />  PSTR szCurrMod = MakePtr(PSTR, pDOSHeader, pImportDesc->Name);<br />  if (stricmp(szCurrMod, szImportModule) == 0)<br />      break; // 扑ֈ!中断循环<br />  // 下一个元?br />  pImportDesc++;<br /> }</p><p> // 如果没有扑ֈ,说明我们L的模块没有被当前的进E所引入!<br /> if (pImportDesc->Name == NULL)<br />  return NULL;</p><p> // q回函数所扑ֈ的模块描q符(import descriptor)<br /> return pImportDesc;<br />}</p><p>// IsNT()函数的实?br />BOOL IsNT()<br />{<br /> OSVERSIONINFO stOSVI;<br /> memset(&stOSVI, NULL, sizeof(OSVERSIONINFO));<br /> stOSVI.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);<br /> BOOL bRet = GetVersionEx(&stOSVI);<br /> _ASSERT(TRUE == bRet);<br /> if (FALSE == bRet) return FALSE;<br /> return (VER_PLATFORM_WIN32_NT == stOSVI.dwPlatformId);<br />}<br />/////////////////////////////////////////////// End //////////////////////////////////////////////////////////////////////</p><p>   不知道在q篇文章问世之前Q有多少朋友试q去实现“鼠标屏q取词”这充满了挑战的技术,也只有尝试过的朋友才能体会到光的不易,其在探索API函数的截hQ手头的几篇资料没有一是涉及到关键代码的Q重要的地方都是一W代q,MSDN更是昑־苍白而无力,也不知道除了IMAGE_IMPORT_DESCRIPTOR和IMAGE_THUNK_DATAQ微软还隐藏了多秘密,好在着头皮q是把它l攻克了Q希望这文章对大家能有所帮助?br /><br /></p><img src ="http://www.shnenglu.com/eday/aggbug/19065.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/eday/" target="_blank">独孤九剑</a> 2007-02-28 20:44 <a href="http://www.shnenglu.com/eday/archive/2007/02/28/19065.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>截取pȝ API 调用http://www.shnenglu.com/eday/archive/2007/02/26/18987.html独孤九剑独孤九剑Mon, 26 Feb 2007 05:43:00 GMThttp://www.shnenglu.com/eday/archive/2007/02/26/18987.html阅读全文

独孤九剑 2007-02-26 13:43 发表评论
]]>
C++代码优化http://www.shnenglu.com/eday/archive/2007/02/17/18844.html独孤九剑独孤九剑Sat, 17 Feb 2007 14:05:00 GMThttp://www.shnenglu.com/eday/archive/2007/02/17/18844.html1.定点型变量和表达式是 float ?
    Z让编译器产生更好的代?比如说?DNow! 或SSE指o的代?Q必ȝ定Q点型变量和表辑ּ?float 型的。要特别注意的是Q以 "QF"Q??"Qf"Q?为后~Q比如:3.14fQ的点帔R才是 float 型,否则默认?double 型。ؓ了避?float 型参数自动{化ؓ doubleQ请在函数声明时使用 float?
2.使用32位的数据cd
  ~译器有很多U,但它们都包含的典型的32位类型是QintQsignedQsigned intQunsignedQunsigned intQlongQsigned longQlong intQsigned long intQunsigned longQunsigned long int。尽量?2位的数据cdQ因为它们比16位的数据甚至8位的数据更有效率?
3.明智使用有符h型变?
  在很多情况下Q你需要考虑整型变量是有W号q是无符L型的。比如,保存一个h的体重数据时不可能出现负敎ͼ所以不需要用有W号cd。但是,如果是要保存温度数据Q就必须使用到有W号的变量?
  在许多地方,考虑是否使用有符L变量是必要的。在一些情况下Q有W号的运比较快Q但在一些情况下却相反?
  比如Q整型到点转化Ӟ使用大于16位的有符h型比较快。因为x86构架中提供了从有W号整型转化到Q点型的指令,但没有提供从无符h型{化到点的指令。看看编译器产生的汇~代码:
  不好的代码:
~译?     ~译?
double xQ    mov [foo + 4], 0
unsigned int iQ   mov eax, i
x = iQ     mov [foo], eax
     flid qword ptr [foo]
     fstp qword ptr [x]
  上面的代码比较慢。不仅因为指令数目比较多Q而且׃指o不能配对造成的FLID指o被gq执行。最好用以下代码代替Q?
    推荐的代码:
~译前     编译后
double xQ    fild dword ptr
int iQ     fstp qword ptr [x]
x = iQ?
  在整数运中计算商和余数Ӟ使用无符L型比较快。以下这D典型的代码是编译器产生?2位整型数除以4的代码:
  不好的代码?
~译?     ~译?
int iQ     mov eax, i
i = i / 4Q?    cdq
     and edx, 3
     add eax, edx
     sar eax, 2
     mov i, eax
    推荐的代?br />~译?     ~译?
unsigned int iQ?   shr i, 2
i = i / 4Q?
 ȝQ?br /> 无符L型用于:除法和余?循环计数,数组下标
  有符L型用于:整型到Q点的转化
4.while VS. for
  在编E中Q我们常帔R要用到无限@环,常用的两U方法是while (1) ?for (Q;)。这两种Ҏ效果完全一P但那一U更好呢Q然我们看看它们~译后的代码Q?
~译?     ~译?
while (1)Q?    mov eax,1
     test eax,eax
     je foo+23h
     jmp foo+18h
~译?     ~译后?
for (Q;)Q?    jmp foo+23h
  一目了Ӟfor (Q;)指o,不占用寄存器Q而且没有判断跌{Q比while (1)好?
5.使用数组型代替指针型
  使用指针会ɾ~译器很难优化它。因为缺乏有效的指针代码优化的方法,~译器L假设指针可以讉K内存的Q意地方,包括分配l其他变量的储存I间。所以ؓ了编译器产生优化得更好的代码Q要避免在不必要的地方用指针。一个典型的例子是访问存攑֜数组中的数据。C++ 允许使用操作W?[] 或指针来讉K数组Q用数l型代码会让优化器减生不安全代码的可能性。比如,x[0] 和x[2] 不可能是同一个内存地址Q但 *p ?*q 可能。强烈徏议用数l型Q因样可能会有意料之外的性能提升?
    不好的代?br />typedef struct
{
  float x,y,z,wQ?br />} VERTEXQ?br />typedef struct

{
  float m[4][4]Q?br />} MATRIXQ?br />void XForm(float* res, const float* v, const float* m, int nNumVerts)
{
  float dpQ?br />  int iQ?br />   const VERTEX* vv = (VERTEX *)vQ?br />   for (i = 0Q?i <Q?nNumVertsQ?i++)
  {
    dp = vv->Qx * *m ++Q?br />    dp += vv->Qy * *m ++Q?br />    dp += vv->Qz * *m ++Q?br />    dp += vv->Qw * *m ++Q?br />    *res ++ = dpQ      // 写入转换了的 x
    dp = vv->Qx * *m ++Q?br />    dp += vv->Qy * *m ++Q?br />    dp += vv->Qz * *m ++Q?br />    dp += vv->Qw * *m ++Q?br />    *res ++ = dpQ     // 写入转换了的 y
    dp = vv->Qx * *m ++Q?br />    dp += vv->Qy * *m ++Q?br />    dp += vv->Qz * *m ++Q?br />    dp += vv->Qw * *m ++Q?br />    *res ++ = dpQ    // 写入转换了的 z
    dp = vv->Qx * *m ++Q?br />    dp += vv->Qy * *m ++Q?br />    dp += vv->Qz * *m ++Q?br />    dp += vv->Qw * *m ++Q?br />    *res ++ = dpQ    // 写入转换了的 w
    vv ++Q        // 下一个矢?br />    m -= 16Q?br />  }
}
    推荐的代?
typedef struct
{
  float x,y,z,wQ?br />} VERTEXQ?br />typedef struct
{
  float m[4][4]Q?br />} MATRIXQ?br />void XForm (float* res, const float* v, const float* m, int nNumVerts)
{
  int iQ?br />  const VERTEX* vv = (VERTEX*)vQ?br />  const MATRIX* mm = (MATRIX*)mQ?br />  VERTEX* rr = (VERTEX*)resQ?br />  for (i = 0Q?i <Q?nNumVertsQ?i++)
  {
    rr->Qx = vv->Qx * mm->Qm[0][0] + vv->Qy * mm->Qm[0][1]
        + vv->Qz * mm->Qm[0][2] + vv->Qw * mm->Qm[0][3]Q?br />    rr->Qy = vv->Qx * mm->Qm[1][0] + vv->Qy * mm->Qm[1][1]
        + vv->Qz * mm->Qm[1][2] + vv->Qw * mm->Qm[1][3]Q?br />    rr->Qz = vv->Qx * mm->Qm[2][0] + vv->Qy * mm->Qm[2][1]
        + vv->Qz * mm->Qm[2][2] + vv->Qw * mm->Qm[2][3]Q?br />    rr->Qw = vv->Qx * mm->Qm[3][0] + vv->Qy * mm->Qm[3][1]
        + vv->Qz * mm->Qm[3][2] + vv->Qw * mm->Qm[3][3]Q?br />  }
}
  注意: 源代码的转化是与~译器的代码发生器相l合的。从源代码层ơ很难控制生的机器码。依靠编译器和特D的源代码,有可能指针型代码~译成的机器码比同等条g下的数组型代码运行速度更快。明智的做法是在源代码{化后查性能是否真正提高了,再选择使用指针型还是数l型?
6.充分分解的循环
  要充分利用CPU的指令缓存,p充分分解的循环。特别是当@环体本n很小的时候,分解循环可以提高性能。BTW:很多~译器ƈ不能自动分解循环?
不好的代?推荐的代?
// 3D转化Q把矢量 V ?4x4 矩阵 M 怹
for (i = 0Q?i <Q?4Q?i ++)
{
  r = 0Q?br />  for (j = 0Q?j <Q?4Q?j ++)
  {
    r += M[j]*V[j]Q?br />  }
}
r[0] = M[0][0]*V[0] + M[1][0]*V[1] + M[2][0]*V[2] + M[3][0]*V[3]Q?br />r[1] = M[0][1]*V[0] + M[1][1]*V[1] + M[2][1]*V[2] + M[3][1]*V[3]Q?br />r[2] = M[0][2]*V[0] + M[1][2]*V[1] + M[2][2]*V[2] + M[3][2]*V[3]Q?br />r[3] = M[0][3]*V[0] + M[1][3]*V[1] + M[2][3]*V[2] + M[3][3]*v[3]Q?
7.避免没有必要的读写依?
  当数据保存到内存时存在读写依赖,x据必d正确写入后才能再ơ读取。虽然AMD Athlon{CPU有加速读写依赖gq的gQ允许在要保存的数据被写入内存前d出来Q但是,如果避免了读写依赖ƈ把数据保存在内部寄存器中Q速度会更快。在一D很长的又互怾赖的代码链中Q避免读写依赖显得尤光要。如果读写依赖发生在操作数组Ӟ许多~译器不能自动优化代码以避免d依赖。所以推荐程序员手动L除读写依赖,举例来说Q引q一个可以保存在寄存器中的时变量。这样可以有很大的性能提升。下面一D代码是一个例子:
    不好的代?br />float x[VECLEN], y[VECLEN], z[VECLEN]Q?br />......
for (unsigned int k = 1Q?k <Q?VECLENQ?k ++)
{
  x[k] = x[k-1] + y[k]Q?br />}
for (k = 1Q?k <Q?VECLENQ?k++)
{
  x[k] = z[k] * (y[k] - x[k-1])Q?br />}
   推荐的代?
float x[VECLEN], y[VECLEN], z[VECLEN]Q?br />......
float t(x[0])Q?br />for (unsigned int k = 1Q?k <Q?VECLENQ?k ++)
{
  t = t + y[k]Q?br />  x[k] = tQ?br />}
t = x[0]Q?br />for (k = 1Q?k <Q?VECLENQ?k ++)
{
  t = z[k] * (y[k] - t)Q?br />  x[k] = tQ?br />}
8.Switch 的用?
  Switch 可能转化成多U不同算法的代码。其中最常见的是跌{表和比较?树。推荐对case的g照发生的可能性进行排序,把最有可能的攑֜W一个,当switch用比较链的方式{化时Q这样可以提高性能。此外,在case中推荐用小的连l的整数Q因为在q种情况下,所有的~译器都可以把switch 转化成蟩转表?
    不好的代?br />int days_in_month, short_months, normal_months, long_monthsQ?br />......
switch (days_in_month)
{
  case 28:
  case 29:
    short_months ++Q?br />    breakQ?br />  case 30:
    normal_months ++Q?br />    breakQ?br />  case 31:
    long_months ++Q?br />    breakQ?br />  default:
    cout <Q?lt;Q?"Qmonth has fewer than 28 or more than 31 days"Q?<Q?lt;Q?endlQ?br />    breakQ?br />}
    推荐的代?
int days_in_month, short_months, normal_months, long_monthsQ?br />......
switch (days_in_month)
{
  case 31:
    long_months ++Q?br />    breakQ?br />  case 30:
    normal_months ++Q?br />    breakQ?br />  case 28:
  case 29:
    short_months ++Q?
    breakQ?br />  default:
    cout <Q?lt;Q?"Qmonth has fewer than 28 or more than 31 days"Q?<Q?lt;Q?endlQ?br />    breakQ?br />}
9.所有函数都应该有原型定?
  一般来_所有函数都应该有原型定义。原型定义可以传辄~译器更多的可能用于优化的信息?
  可能用常?const)。C++ 标准规定Q如果一个const声明的对象的地址不被获取Q允许编译器不对它分配储存空间。这样可以代码更有效率Q而且可以生成更好的代码?
10.提升循环的性能
  要提升@环的性能Q减多余的帔R计算非常有用Q比如,不随循环变化的计)?
  不好的代?在for()中包含不变的if()) 推荐的代?
for( i ... )
{
  if( CONSTANT0 )
  {
    DoWork0( i )Q?// 假设q里不改变CONSTANT0的?br />  }
  else
  {
    DoWork1( i )Q?// 假设q里不改变CONSTANT0的?br />  }
}
if( CONSTANT0 )
{
  for( i ... )
  {
    DoWork0( i )Q?br />  }
}
else
{
  for( i ... )
  {
    DoWork1( i )Q?br />  }
}
  如果已经知道if()的|q样可以避免重复计算。虽然不好的代码中的分支可以单地预测Q但是由于推荐的代码在进入@环前分支已经定Q就可以减少对分支预的依赖?  把本地函数声明ؓ静态的(static)
  如果一个函数在实现它的文g外未被用的话,把它声明为静态的(static)以强制用内部连接。否则,默认的情况下会把函数定义为外部连接。这样可能会影响某些~译器的优化——比如,自动内联?
11.考虑动态内存分?
  动态内存分配(C++中的"Qnew"Q)可能L为长的基本类型(四字寚wQ返回一个已l对齐的指针。但是如果不能保证对齐,使用以下代码来实现四字对齐。这D代码假设指针可以映到 long 型?
  例子
  double* p = (double*)new BYTE[sizeof(double) * number_of_doubles+7L]Q?br />    double* np = (double*)((long(p) + 7L) &Q??L)Q?
  现在Q你可以使用 np 代替 p 来访问数据。注意:释放储存I间时仍然应该用delete p?
12.使用昑ּ的ƈ行代?
  可能把长的有依赖的代码铑ֈ解成几个可以在流水线执行单元中ƈ行执行的没有依赖的代码链。因为QҎ作有很长的潜伏期Q所以不它被映成 x87 ?3DNow! 指oQ这都很重要。很多高U语aQ包括C++Qƈ不对产生的Q点表辑ּ重新排序Q因为那是一个相当复杂的q程。需要注意的是,重排序的代码和原来的代码在代C一致ƈ不等价于计算l果一_因ؓ点操作~Z_度。在一些情况下Q这些优化可能导致意料之外的l果。幸q的是,在大部分情况下,最后结果可能只有最不重要的位(x低位Q是错误的?
  不好的代?br />double a[100], sumQ?br />int iQ?br />sum = 0.0fQ?br />for (i=0Q?i<Q?00Q?i++)
  sum += aQ?
    推荐的代?
double a[100], sum1, sum2, sum3, sum4, sumQ?br />int iQ?br />sum1 = sum2 = sum3 = sum4 = 0.0Q?br />for (i = 0Q?i <Q?100Q?i += 4)
{
  sum1 += aQ?br />  sum2 += a[i+1]Q?br />  sum3 += a[i+2]Q?br />  sum4 += a[i+3]Q?br />}
sum = (sum4+sum3)+(sum1+sum2)Q?
  要注意的是:使用4 路分解是因ؓq样使用?阶段水UQ点加法,点加法的每一个阶D占用一个时钟周期,保证了最大的资源利用率?
13.提出公共子表辑ּ
  在某些情况下QC++~译器不能从点表达式中提出公共的子表达式,因ؓq意味着相当于对表达式重新排序。需要特别指出的是,~译器在提取公共子表辑ּ前不能按照代数的{h关系重新安排表达式。这ӞE序员要手动地提出公q子表辑ּQ在VC.net里有一“全局优化”选项可以完成此工作,但效果就不得而知了)?
推荐的代?
float a, b, c, d, e, fQ?br />...
e = b * c / dQ?br />f = b / d * aQ?br />float a, b, c, d, e, fQ?br />...
const float t(b / d)Q?br />e = c * tQ?br />f = a * tQ?
推荐的代?
float a, b, c, e, fQ?br />...
e = a / cQ?br />f = b / cQ?br />float a, b, c, e, fQ?br />...
const float t(1.0f / c)Q?br />e = a * tQ?br />f = b * tQ?
14.l构体成员的布局
  很多~译器有“ɾl构体字Q双字或四字寚w”的选项。但是,q是需要改善结构体成员的对齐,有些~译器可能分配给l构体成员空间的序与他们声明的不同。但是,有些~译器ƈ不提供这些功能,或者效果不好。所以,要在付出最代L情况下实现最好的l构体和l构体成员对齐,采取q些ҎQ?
  A按类型长度排?
  把结构体的成员按照它们的cd长度排序Q声明成员时把长的类型放在短的前面?
  把结构体填充成最长类型长度的整倍数
  把结构体填充成最长类型长度的整倍数。照q样Q如果结构体的第一个成员对齐了Q所有整个结构体自然也就寚w了。下面的例子演示了如何对l构体成员进行重新排序:
  不好的代码,普通顺?推荐的代码,新的序q手动填充了几个字节
struct
{
  char a[5]Q?br />  long kQ?br />  double xQ?br />} bazQ?br />struct
{
  double xQ?br />  long kQ?br />  char a[5]Q?br />char pad[7]Q?br />} bazQ?

  q个规则同样适用于类的成员的布局?
  B按数据类型的长度排序本地变量
  当编译器分配l本地变量空间时Q它们的序和它们在源代码中声明的顺序一P和上一条规则一P应该把长的变量放在短的变量前面。如果第一个变量对齐了Q其它变量就会连l的存放Q而且不用填充字节自然׃寚w。有些编译器在分配变量时不会自动改变变量序Q有些编译器不能产生4字节寚w的栈Q所?字节可能不对齐。下面这个例子演CZ本地变量声明的重新排序:
  不好的代码,普通顺?推荐的代码,改进的顺?
short ga, gu, giQ?br />long foo, barQ?br />double x, y, z[3]Q?br />char a, bQ?br />float bazQ?br />double z[3]Q?br />double x, yQ?br />long foo, barQ?br />float bazQ?br />short ga, gu, giQ?
14.避免不必要的整数除法
  整数除法是整数运中最慢的Q所以应该尽可能避免。一U可能减整数除法的地方是连除,q里除法可以׃法代ѝ这个替换的副作用是有可能在乘U时会溢出,所以只能在一定范围的除法中用?
  不好的代?推荐的代?
int i, j, k, mQ?br />m = i / j / kQ?br />int i, j, k, mQ?br />m = i / (j * k)Q?
15.把频J用的指针型参数拷贝到本地变量
  避免在函C频繁使用指针型参数指向的倹{因为编译器不知道指针之间是否存在冲H,所以指针型参数往往不能被编译器优化。这h数据不能被存攑֜寄存器中Q而且明显地占用了内存带宽。注意,很多~译器有“假设不冲突”优化开养I在VC里必L动添加编译器命o?Oa?OwQ,q允许编译器假设两个不同的指针L有不同的内容Q这样就不用把指针型参数保存到本地变量。否则,请在函数一开始把指针指向的数据保存到本地变量。如果需要的话,在函数结束前拯回去?  
    不好的代?
// 假设 q != r
void isqrt(unsigned long a, unsigned long* q, unsigned long* r)
{
  *q = aQ?br />  if (a >Q?0)
  {
    while (*q >Q?(*r = a / *q))
    {
      *q = (*q + *r) >Q?gt;Q?1Q?br />    }
  }
  *r = a - *q * *qQ?br />}
    推荐的代?br />// 假设 q != r
void isqrt(unsigned long a, unsigned long* q, unsigned long* r)
{
  unsigned long qq, rrQ?br />  qq = aQ?br />  if (a >Q?0)
  {
    while (qq >Q?(rr = a / qq))
    {
      qq = (qq + rr) >Q?gt;Q?1Q?br />    }
  }
  rr = a - qq * qqQ?br />  *q = qqQ?br />  *r = rrQ?br />}
16.赋g初始?br />先看看以下代码:
class CInt
{
  int m_iQ?
public:
  CInt(int a = 0):m_i(a) { cout <Q?lt;Q?"QCInt"Q?<Q?lt;Q?endlQ?}
  ~CInt() { cout <Q?lt;Q?"Q~CInt"Q?<Q?lt;Q?endlQ?}
  CInt operator + (const CInt&Q?a) { return CInt(m_i + a.GetInt())Q?}
  void SetInt(const int i)  { m_i = iQ?}
  int GetInt() const      { return m_iQ?}
}Q?br />    不好的代?
void main()
{
  CInt a, b, cQ?br />  a.SetInt(1)Q?br />  b.SetInt(2)Q?br />  c = a + bQ?br />}
    推荐的代?br />void main()
{
  CInt a(1), b(2)Q?br />  CInt c(a + b)Q?br />}
  q两D代码所作的事都一P但那一个更好呢Q看看输出结果就会发玎ͼ不好的代码输Z四个"QCInt"Q和四个"Q~CInt"Q,而推荐的代码只输Z个。也是_W二个例子比W一个例子少生成一ơ时对象。Why? h意,W一个中的c用的是先声明再赋值的ҎQ第二个用的是初始化的方法,它们有本质的区别。第一个例子的"Qc = a + b"Q先生成一个时对象用来保存a + b的|再把该时对象用位拷贝的Ҏlc赋|然后临时对象被销毁。这个时对象就是那个多出来的对象。第二个例子直接用拷贝构造函数的Ҏ对c初始化,不生时对象。所以,量在需要用一个对象时才声明,q用初始化的Ҏ赋初倹{?
17.量使用成员初始化列?
  在初始化cȝ成员Ӟ量使用成员初始化列表而不是传l的赋值方式?
  不好的代?
class CMyClass
{
  string strNameQ?
public:
  CMyClass(const string&Q?str)Q?br />}Q?
CMyClass::CMyClass(const string&Q?str)
{
  strName = strQ?br />}
    推荐的代?br />class CMyClass
{
  string strNameQ?br />  int iQ?br />public:
  CMyClass(const string&Q?str)Q?br />}Q?
CMyClass::CMyClass(const string&Qstr)
   :strName(str)
{

}
  不好的例子用的是赋值的方式。这PstrName会先被徏立(调用了string的默认构造函敎ͼQ再由参数str赋倹{而推荐的例子用的是成员初始化列表QstrName直接构造ؓstrQ少调用一ơ默认构造函敎ͼq少了一些安全隐患?/p>

独孤九剑 2007-02-17 22:05 发表评论
]]>
C代码优化http://www.shnenglu.com/eday/archive/2007/02/17/18843.html独孤九剑独孤九剑Sat, 17 Feb 2007 14:04:00 GMThttp://www.shnenglu.com/eday/archive/2007/02/17/18843.html阅读全文

独孤九剑 2007-02-17 22:04 发表评论
]]>
软g加密技术和注册机制http://www.shnenglu.com/eday/archive/2007/02/11/18667.html独孤九剑独孤九剑Sun, 11 Feb 2007 13:41:00 GMThttp://www.shnenglu.com/eday/archive/2007/02/11/18667.html  本文是一Y件加密技术的基础性文章,要介l了软g加密的一些基本常识和一些加密品,适用于国内Y件开发商或者个人共享Y件开发者阅d考?/p>

  1、加密技术概q?/strong>

  一个密码系l的安全性只在于密钥的保密性,而不在算法的保密性?/p>

  对纯数据的加密的是q样。对于你不愿意让他看到这些数据(数据的明文)的hQ用可靠的加密算法,只要破解者不知道被加密数据的密码Q他׃可解读这些数据?/p>

  但是QY件的加密不同于数据的加密Q它只能是“隐藏”。不你愿意不愿意让他(合法用户Q或 CrackerQ看见这些数据(软g的明文)QY件最l总要在机器上q行Q对机器Q它必L明文。既然机器可以“看见”这些明文,那么 CrackerQ通过一些技术,也可以看到这些明文?/p>

  于是Q从理论上,M软g加密技术都可以破解。只是破解的隑ֺ不同而已。有的要让最高明?Cracker 忙上几个月,有的可能不费吹灰之力Q就被破解了?/p>

  所以,反盗版的dQ技术上的反盗版Q而非行政上的反盗版)是增加 Cracker 的破解难度。让他们p在破解Y件上的成本,比他破解q个软g的获利还要高。这?Cracker 的破解变得毫无意义——谁会花比正版Y件更多的钱去买盗版YӞ

  2、密码学?/strong>

  2.1   概念

  Q?Q?发送者和接收?/strong>

  假设发送者想发送消息给接收者,且想安全地发送信息:Ҏ信偷听者不能阅d送的消息?/p>

  Q?Q?消息和加?/strong>

  消息被称为明文。用某种Ҏ伪装消息以隐藏它的内容的q程UCؓ加密Q加了密的消息称为密文,而把密文转变为明文的q程UCؓ解密?/p>

  明文用MQ消息)或PQ明文)表示Q它可能是比ҎQ文本文件、位图、数字化的语x或数字化的视频图像)。至于涉及到计算机,P是简单的二进制数据。明文可被传送或存储Q无论在哪种情况QM指待加密的消息?/p>

  密文用C表示Q它也是二进制数据,有时和M一样大Q有时稍大(通过压羃和加密的l合QC有可能比P些。然而,单单加密通常达不到这一点)。加密函数E作用于M得到密文CQ用数学表示为:

  EQMQ?C.

  相反圎ͼ解密函数D作用于C产生M

  DQCQ?M.

  先加密后再解密消息,原始的明文将恢复出来Q下面的{式必须成立Q?/p>

  DQEQMQ)=M

  Q?Q?鉴别、完整性和抗抵?/strong>

  除了提供机密性外Q密码学通常有其它的作用Q?

  QaQ?鉴别

  消息的接收者应该能够确认消息的来源Q入侵者不可能伪装成他人?/p>

  QbQ?完整性检?/p>

  消息的接收者应该能够验证在传送过E中消息没有被修改;入R者不可能用假消息代替合法消息?/p>

  QcQ?抗抵?/p>

  发送者事后不可能虚假地否认他发送的消息?/p>

  Q?Q?法和密?/strong>

  密码法也叫密码Q是用于加密和解密的数学函数。(通常情况下,有两个相关的函数Q一个用作加密,另一个用作解密)

  如果法的保密性是Z保持法的秘密,q种法UCؓ受限制的法。受限制的算法具有历史意义,但按现在的标准,它们的保密性已q远不够。大的或l常变换的用Ll不能用它们,因ؓ每有一个用L开q个l织Q其它的用户必L换另外不同的法。如果有人无意暴露了q个U密Q所有h都必L变他们的法?/p>

  更糟的是Q受限制的密码算法不可能q行质量控制或标准化。每个用Ll必L他们自己的唯一法。这Ll织不可能采用流行的g或Y件品。但H听者却可以买到q些行产品q学习算法,于是用户不得不自q写算法ƈ予以实现Q如果这个组l中没有好的密码学家Q那么他们就无法知道他们是否拥有安全的算法?/p>

  管有这些主要缺P受限制的法对低密的应用来说还是很行的,用户或者没有认识到或者不在乎他们pȝ中内在的问题?/p>

  C密码学用密钥解决了这个问题,密钥用K表示。K可以是很多数值里的Q意倹{密钥K的可能值的范围叫做密钥I间。加密和解密q算都用这个密钥(卌都依赖于密钥,q用K作ؓ下标表示Q,q样Q加/解密函数现在变成Q?/p>

  EKQMQ?C

  DKQCQ?M.

  DKQEKQMQ)=M.

  有些法使用不同的加密密钥和解密密钥Q也是说加密密钥K1与相应的解密密钥K2不同Q在q种情况下:

  EK1QMQ?C

  DK2QCQ?M

  DK2 QEK1QMQ)=M

  所有这些算法的安全性都Z密钥的安全性;而不是基于算法的l节的安全性。这意味着法可以公开Q也可以被分析,可以大量生使用法的品,即偷听者知道你的算法也没有关系Q如果他不知道你使用的具体密钥,他就不可能阅M的消息?/p>

  密码pȝq法、以及所有可能的明文、密文和密钥l成的?/p>

  Z密钥的算法通常有两c:对称法和公开密钥法。下面将分别介绍Q?/p>

  2.2   对称密码法

  对称法有时又叫传统密码法Q就是加密密钥能够从解密密钥中推出来,反过来也成立。在大多数对U算法中Q加/解密密钥是相同的。这些算法也叫秘密密钥算法或单密钥算法,它要求发送者和接收者在安全通信之前Q商定一个密钥。对U算法的安全性依赖于密钥Q泄漏密钥就意味着M人都能对消息q行?解密。只要通信需要保密,密钥必M密?/p>

  对称法的加密和解密表示为:

  EKQMQ?C

  DKQCQ?M

  对称法可分ZcR一ơ只Ҏ文中的单个比特(有时对字节)q算的算法称为序列算法或序列密码。另一cȝ法是Ҏ文的一l比特亚行运,q些比特l称为分l,相应的算法称为分l算法或分组密码。现代计机密码法的典型分l长度ؓ64比特——这个长度大到以防止分析破译,但又到以方便使用Q在计算机出现前Q算法普遍地每次只对明文的一个字W运,可认为是序列密码对字W序列的q算Q?/p>

  2.3   公开密码法

  公开密钥法Q也叫非对称法Q是q样设计的:用作加密的密钥不同于用作解密的密钥,而且解密密钥不能Ҏ加密密钥计算出来Q至在合理假定的长旉内)。之所以叫做公开密钥法Q是因ؓ加密密钥能够公开Q即陌生者能用加密密钥加密信息,但只有用相应的解密密钥才能解密信息。在q些pȝ中,加密密钥叫做公开密钥Q简U公钥)Q解密密钥叫做私人密钥(U私钥)。私人密钥有时也叫秘密密钥。ؓ了避免与对称法hQ此处不用秘密密钥这个名字?/p>

  用公开密钥K加密表示?/p>

  EKQMQ?C.

  虽然公开密钥和私人密钥是不同的,但用相应的私人密钥解密可表示为:

  DKQCQ?M

  有时消息用私人密钥加密而用公开密钥解密Q这用于数字{֐Q后面将详细介绍Q,管可能产生hQ但q些q算可分别表CZؓQ?/p>

  EKQMQ?C

  DKQCQ?M

  当前的公开密码法的速度Q比起对U密码算法,要慢的多Q这使得公开密码法在大数据量的加密中应用有限?/p>

  2.4   单向散列函数

  单向散列函数 HQMQ?作用于一个Q意长度的消息 MQ它q回一个固定长度的散列?hQ其?h 的长度ؓ m .

  输入ZQ意长度且输出为固定长度的函数有很多种Q但单向散列函数q有使其单向的其它特性:

  Q?Q?l定 M Q很Ҏ计算 h Q?/p>

  Q?Q?l定 h Q根?HQMQ?= h 计算 M 很难 Q?/p>

  Q?Q?l定 M Q要扑ֈ另一个消?M?q满?HQMQ?= HQM’) 很难?/p>

  在许多应用中Q仅有单向性是不够的,q需要称之ؓ“抗撞”的条gQ?/p>

  要找Z个随机的消息 M ?M‘,?HQMQ?= HQM’) 满很难?/p>

  ׃散列函数的这些特性,׃公开密码法的计速度往往很慢Q所以,在一些密码协议中Q它可以作ؓ一个消?M 的摘要,代替原始消息 MQ让发送者ؓ HQMQ?{֐而不是对 M {֐ .

  ?SHA 散列法用于数字{֐协议 DSA中?/p>

  2.5   数字{֐

  提到数字{֐q不开公开密码pȝ和散列技术?/p>

  有几U公钥算法能用作数字{֐。在一些算法中Q例如RSAQ公钥或者私钥都可用作加密。用你的U钥加密文gQ你拥有安全的数字{֐。在其它情况下,如DSAQ算法便区分开来了Q?数字{֐法不能用于加密。这U思想首先由Diffie和Hellman提出 .

  基本协议是简单的 Q?/p>

  Q?Q?A 用她的私钥对文g加密Q从而对文g{֐?/p>

  Q?Q?A 签名的文g传给B.

  Q?Q?B用A的公钥解密文Ӟ从而验证签名?/p>

  q个协议中,只需要证明A的公钥的是她的。如果B不能完成W(3Q步Q那么他知道{֐是无效的?/p>

  q个协议也满以下特征:

  Q?Q?{֐是可信的。当B用A的公钥验证信息时Q他知道是由A{֐的?/p>

  Q?Q?{֐是不可伪造的。只有A知道她的U钥?/p>

  Q?Q?{֐是不可重用的。签名是文g的函敎ͼq且不可能{换成另外的文件?/p>

  Q?Q?被签名的文g是不可改变的。如果文件有M改变Q文件就不可能用A的公钥验证?/p>

  Q?Q?{֐是不可抵赖的。B不用A的帮助就能验证A的签名?/p>

  在实际应用中Q因为公共密码算法的速度太慢Q签名者往往是对消息的散列签名而不是对消息本n{֐。这样做q不会降低签名的可信性?/p>

  3    当前行的一些Y件保护技?/strong>

  3.1   序列号保?/p>

  数学法一w是密码加密的核心Q但在一般的软g加密中,它似乎ƈ不太Zh们关心,因ؓ大多数时候Y件加密本w实现的都是一U编E的技巧。但q几q来随着序列号加密程序的普及Q数学算法在软g加密中的比重g是越来越大了?/p>

  看看在网l上大行光的序列号加密的工作原理。当用户从网l上下蝲某个shareware——共享Y件后Q一般都有用时间上的限Ӟ当过了共享Y件的试用期后Q你必须到这个Y件的公司L册后方能l箋使用。注册过E一般是用户把自qUh信息Q一般主要指名字Q连同信用卡L告诉lY件公司,软g公司会根据用L信息计算Z个序列码Q在用户得到q个序列码后Q按照注册需要的步骤在Y件中输入注册信息和注册码Q其注册信息的合法性由软g验证通过后,软g׃取消掉本w的各种限制Q这U加密实现v来比较简单,不需要额外的成本Q用戯C非常方便Q在互联|上的Y?0%都是以这U方式来保护的?/p>

  软g验证序列L合法性过E,其实是验证用户名和序列号之间的换算关系是否正确的过E。其验证最基本的有两种Q一U是按用戯入的姓名来生成注册码Q再同用戯入的注册码比较,公式表示如下Q?/p>

  序列?= FQ用户名Q?/p>

  但这U方法等于在用户软g中再C软g公司生成注册码的q程Q实际上是非怸安全的,不论其换过E多么复杂,解密者只需把你的换过E从E序中提取出来就可以~制一个通用的注册程序?/p>

  另外一U是通过注册码来验证用户名的正确性,公式表示如下Q?/p>

  用户名称 = F逆(序列P Q如ACDSEEQ?/p>

  q其实是软g公司注册码计过E的反算法,如果正向法与反向算法不是对U算法的话,对于解密者来_的确有些困难Q但q种法相当不好设计?/p>

  于是有h考虑C下的法Q?/p>

  F1Q用户名Uͼ = F2Q序列号Q?/p>

  F1、F2是两U完全不同的的算法,但用户名通过F1法计算出的特征字等于序列号通过F2法计算出的特征字,q种法在设计上比较单,保密性相对以上两U算法也要好的多。如果能够把F1、F2法设计成不可逆算法的话,保密性相当的好;可一旦解密者找到其中之一的反法的话Q这U算法就不安全了。一元算法的设计看来再如何努力也很难有太大的H破Q那么二元呢Q?/p>

  特定?= FQ用户名Q序列号Q?/p>

  q个法看上ȝ当不错,用户名称与序列号之间的关pM再那么清CQ但同时也失M用户名于序列L一一对应关系QY件开发者必自q护用户名UC序列号之间的唯一性,但这g不是难以办到的事Q徏个数据库可以了。当然也可以把用户名U和序列号分为几个部分来构造多元的法?/p>

  特定?= FQ用户名1Q用户名2Q?..序列?Q序列号2...Q?/p>

  现有的序列号加密法大多是Y件开发者自行设计的Q大部分相当单。而且有些法作者虽然下了很大的功夫Q效果却往往得不到它所希望的结果?/p>

  3.2   旉限制

  有些E序的试用版每次q行都有旉限制Q例如运?0分钟?0分钟停止工作,必须重新q行该程序才能正常工作。这些程序里面自然有个定时器来统计程序运行的旉?/p>

  q种Ҏ使用的较?/p>

  3.3   Key File 保护

  Key FileQ注册文Ӟ是一U利用文件来注册软g的保护方式。Key File一般是一个小文gQ可以是U文本文Ӟ也可以是包含不可昄字符的二q制文gQ其内容是一些加密过或未加密的数据,其中可能有用户名、注册码{信息。文件格式则pY件作者自己定义。试用版软g没有注册文gQ当用户向作者付Ҏ册之后,会收C者寄来的注册文gQ其中可能包含用L个h信息。用户只要将该文件放入指定的目录Q就可以让Y件成为正式版。该文g一般是攑֜软g的安装目录中或系l目录下。Y件每ơ启动时Q从该文件中d数据Q然后利用某U算法进行处理,Ҏ处理的结果判断是否ؓ正确的注册文Ӟ如果正确则以注册版模式来q行?/p>

  q种保护Ҏ使用也不多?/p>

  3.4   CD-check

  卛_盘保护技术。程序在启动时判断光׃的光盘上是否存在特定的文Ӟ如果不存在则认ؓ用户没有正版光盘Q拒l运行。在E序q行的过E当中一般不再检查光盘的存在与否。Windows下的具体实现一般是q样的:先用GetLogicalDriveStringsQ?Q或GetLogicalDrivesQ?Q得到系l中安装的所有驱动器的列表,然后再用GetDriveTypeQ?Q检查每一个驱动器Q如果是光驱则用CreateFileAQ?Q或FindFirstFileAQ?Q等函数查特定的文g存在与否Qƈ可能q一步地查文件的属性、大、内容等?/p>

  3.5   软g?/p>

  软g狗是一U智能型加密工具。它是一个安装在q口、串口等接口上的g电\Q同时有一套用于各种语言的接口Y件和工具软g。当被狗保护的Y件运行时Q程序向插在计算Z的Y件狗发出查询命oQY件狗q速计查询ƈl出响应Q正的响应保证软gl箋q行。如果没有Y件狗Q程序将不能q行Q复杂的软硬件技术结合在一起防止Y件盗版。真正有商业价值得软g一般都用Y件狗来保护?/p>

  qx常见的狗主要有“洋狗”(国外狗)和“土狗”(国狗)。这里“洋狗”主要指国的彩虹和以色列的HASPQ“土狗”主要有金天圎ͼ现在与美国彩虹合资,叫“彩虹天地”)、深思、尖矟뀂ȝ说来Q“洋狗”在软g接口、加壟뀁反跟踪{“Y”方面没有“土狗”好Q但在硬件上破解隑ֺ非常大;而“土狗”在软的斚w做的很好Q但在硬件上不如“洋狗”,E有单片机功力的人,都可以复制?/p>

  3.6   软盘加密

  通过在Y盘上格式化一些非标准道Q在q些道上写入一些数据,如Y件的解密密钥{等。这UY盘成为“钥匙盘”。Y件运行时用户Y盘插入,软gdq些道中的数据Q判断是否合法的“钥匙盘”?/p>

  软盘加密q有其它一些技术,如弱位加密等{?/p>

  随着q年来Y盘的没落Q这U方法基本上退Z历史舞台?/p>

  3.7   Y件与机器g信息l合

  用户得到Q买到或从网上下载)软g后,安装时Y件从用户的机器上取得该机器的一些硬件信息(如硬盘序列号、BOIS序列L{)Q然后把q些信息和用L序列受用户名{进行计,从而在一定程度上Y件和g部分l定。用户需要把q一序列LEmail、电话或邮寄{方法寄lY件提供商或开发商QY件开发商利用注册机(软gQ生该软g的注册号寄给用户卛_。Y件加密虽然加密强度比gҎ较弱Q但它具有非常廉L成本、方便的使用Ҏ{优炏V非帔R合做ؓ采用光盘QCDROMQ等方式发授软g的加密方案?/p>

  此种加密法的优?/p>

  ·    不同机器注册码不同。用戯得一个密码只能在一台机器上注册使用软g。不同于目前大多软g采用的注册方法,卛_要知道注册码Q可在Q何机器上安装注册?/p>

  ·    不需要Q何硬件或软盘

  ·    可以选择控制软gq行在什么机器、运行多长时间或ơ数{?/p>

  ·    可让软g在不注册前的功能为演CYӞ只能q行一D|间或部分功能。注册后q卛_为正式Y?/p>

  ·    采用特别技术,解密者很难找C生注册号码的规律

  ·    在用注册号产生软gQ注册机Q时可采用用密码、密钥盘、L数限制等Ҏ

  ·    方便易用Qhg廉?/p>

  q种加密q有以下特点

  1?注册加密的YӞ只能在一台机器上安装使用。把软g拯到其它机器上不能q行?/p>

  2?若用h在另一机器上安装运行,必须把Y件在q一机器上运行时的序列号Q寄lY件出版商换取注册密码。当然应再交一份Y件费用?/p>

  3?此加密方法特别适应在因特网上发布的软g及用光盘发布的Y件?/p>

  注释Q?/p>

  1、“加密技术概q”部分内容参考了大学教材“密码学基础”?/p>

  2、“当前流行的一些Y件保护技术”部分内容参考了“加密与解密--软g保护技术及完全解决Ҏ”一文?/p>

独孤九剑 2007-02-11 21:41 发表评论
]]>
C++资源之不完全导引http://www.shnenglu.com/eday/archive/2007/02/05/18382.html独孤九剑独孤九剑Mon, 05 Feb 2007 02:07:00 GMThttp://www.shnenglu.com/eday/archive/2007/02/05/18382.html  1Q前a

  无数ơ听到“我要开始学习C++!”的呐喊Q无数次听到“C++太复杂了Q我真的学不会”的无奈。Stan Lippman先生曑֜《C++ Primer》一书中指出“C++是最为难学的高E序设计语言之一”,Z常将“之一”去掉以表达自己对C++的敬畏。诚ӞC++E序设计语言对于学习者的有很多难以逾越的`沟,体系l构的庞大,应接不暇q不断扩充的Ҏ……除此之外,参考资料之多与冗杂使它的学习者望而却步,Ʋ求深入者苦不堪a。希望这一份不完全导引能够成ؓ您C++学习之\上的引\灯?/p>

  撰写本文的初衷ƈ不打带领大家体验古老的C++历史Q如果你想了解C++的历史与其前期发展中诸多技术的演变Q你应当d考Bjarne的《The Design and Evolution of C++》。当然也不打给大家一个无所不包的宝典(q不想Q其一是因水^有限Q其二无奈C++之博大精深)Q所l出的仅仅是一些我们认为对于想学习C++的广大读者来说最重要q且触手可及的开发与学习资源?/p>

  本文介绍q分析了一些编译器Q开发环境,库,量的书c以及参考网站,q且可能尝试着l出一个利用这些资源的导引Q望对如同我们一L初学者能够有所裨益?/p>

     2Q编译器

  在C++之外的Q何语a中,~译器都从来没有受到q如此之重视。因为C++是一门相当复杂的语言Q所以编译器也难于构建。直到最q我们才开始能够用上完全W合C++标准的编译器Q哦Q你可能会责怪那些编译器厂商不能早的提供符合标准的~译器,q只能怪他们各自维pȝ自n的一套别Z愿接受的标准Q。什么?你说q无关紧要?哦,不,你所需要的是和标准化C++高度兼容的编译环境。长q来?br />Q只有这L~译器对C++开发h员来说才是最有意义的工具Q尤其是对于E序设计语言的学习者。一x让代码具备可移植性,q让一门语a及其库的应用更ؓq泛。嗯Q是的,我们q里只打介l一些公认的优秀~译器?/p>

  2.1 Borland C++

  q个是Borland C++ Builder和Borland C++ Builder Xq两U开发环境的后台~译器。(哦,我之所以将之分ZU开发环境你应当能明白ؓ什么,正如Delphi7到Delphi8的{变,是革命性的两代。)Borland C++p牌开发工具厂商Borland們֊打造。该公司的编译器素以速度快,I间效率高著UͼBorland C++ pd~译器秉承了q个传统Q属于非怼质的~译器。标准化斚w早在5.5版本的编译器中对标准化C++的兼容就辑ֈ?2.73%。目前最新版本是Borland C++ Builder X中的6.0版本Q官方称100%W合ANSI/ISO的C++标准以及C99标准。嗯…这正是我前面所指的“完全符合C++标准的编译器”?/p>

  2.2 Visual C++

  q个正是我们熟知的Visual Studio ?Visual Studio.net 2002, 2003以及2005 Whidbey中带的C++~译器。由Microsoft公司研制。在Visual Studio 6.0中,因ؓ~译器有太多地方不能与后来出现的C++标准相吻合而饱受批评(x你在使用STL的时候编译时报出的那些o人厌恶的error和warning吧)。VC++6.0Ҏ准化C+
+的兼容只?3.43%。但是随着C++~译器设计大师Stanley Lippman以及诸多C++C达h的加盟,在Visual Studio.NET 2003中,Visual C++~译器已l成Z个非常成熟可*的C++~译器了。Dr.Dobb's Journal的评显CVisual C++7.1Ҏ准C++的兼Ҏ高?8.22%Q一度成为CBX之前兼容性最好的~译器。结合强大的Visual Studio.NET开发环境,是一个非怸错的选择。至于Whidbey时代的Visual C++,g微Y所最x的是C++/CLI……我们不惌论微软下一代的C++~译器对标准化兼容如何,但他实来适合.NET (其实你和我的感觉可能是一LQ微软不应当把标准C++q块肥肉丢给Borland,然而微软可能ƈ不这栯??/p>

  2.3 GNU C++

  著名的开源C++~译器。是cUnix操作pȝ下编写C++E序的首选。特Ҏ有非常好的移植性,你可以在非常q泛的^C使用它,同时也是~写跨^収ͼ嵌入式程序很好的选择。另外在W合标准q个斚w一直都非常好,GCC3.3大概能够辑ֈ96.15%。但是由于其跨^台的Ҏ,在代码尺寔R度{优化上略微差一炏V?/p>

  ZGNU C++的编译器有很多,比如Q?/p>

  (1) Mingw

  http://www.mingw.org/

  GCC的一个Windows的移植版本(Dev-C++的后収ͼ

  (2) Cygwin

  http://sources.redhat.com/cygwin/

  GCC的另外一个WindowsUL版本是Cygwin的一部分QCygwin是Windows下的一个Unix仿真环境。严格的说是模拟GNU的环境,q也是"Gnu's Not Unix"要表辄意思,噢,扯远了,qƈ不是我们在这里关心的实质内容?/p>

  (3) Djgpp

  http://www.delorie.com/djgpp/

  q是GCC的DOSUL版本?/p>

  (4) RSXNT

  http://www.mathematik.uni-bielefeld.de/~rainer/

  q是GCC的DOS和WindowsUL版本?/p>

  (5) Intel C++

  著名CPU刉厂商Intel出品的编译器QSpecial Design for Intel x86Q对于Intel x86l构的CPUl过特别的优化。在有些应用情况下,特别是数D等高性能应用Q仅仅采用Intel的编译器~译p大幅度的提高性能?/p>

  (6) Digital Mars C++

  |络上提供免费下载,Zortech/Symantec C++的承者,其前w在当年惨烈的C++四国战中也是主角之一?/p>

  3Q开发环?/p>

  开发环境对于程序员的作用不a而喻。选择自己朝夕相处的环境也不是Ҏ的事情,特别是在IDE如此丰富的情况下。下面就是我们推荐的一些常见的C++开发环境,q没有包括一些小型的Q罕见的IDE。其中Q何一N是功能丰富,可以用作日常开发用的。对于不同层面的开发者,请参见内文关于适用对象的描q?/p>

  3.1 Visual Studio 6.0

  q个虽然是Microsoft公司的老版本的开发环境,但是鉴于其后l版本VisualStudio.NET的庞大nw,以及初学者ƈ不那么高的功能要求,所以推荐这个开发环境给C++的初学者,供其学习C++的最基本的部分,比如C的那部分子集Q当然你别指望他能够支持最新的C99标准。在日常的开发中Q仍然有很多公司使用q个l典E_的环境,比如W者就看曾亲见有些公司其~译器替换ؓGCC做手机开发之用?/p>

  3.2 Visual Studio.NET 2003

  作ؓMicrosoft公司官方正式发布的最新版本开发环境,其中有太多激动h心的功能。结合其最新的C++~译器。对于机器配|比较好的开发h员来_使用q个开发环境将能满_大部分的要求。这里不打算单独说Visual Studio Whidbey,虽然Visual Studio .NET 2005 - WhidbeyC֌预览版已l推出,但暂不是很稳定,读者可以亲w去体验?/p>

  3.3 Borland C++ Builder 6

  q个q不是Borland的C++开发环境的最新版本。选择它的原因是它不是用Java写的IDEQ速度比较快。它有一个很完善的GUIH体设计器,和Delphiq一个VCL。由于这些特点,比较适合初学者上手。但是由于其GUI的中心位|,可能不利于对于C++语言的学习。而且其ؓ了支持VCLq个Object Pascal写的库也对C++q行了一些私有的扩充。得h们有一个不得不接受的事实:“Borland C++ Builder 6的高?br />几乎都是Delphi高手”?/p>

  3.4 Borland C++ Builder X

  正如前文所qͼ虽然版本号上和前面那个IDE非常相象Q但是其实它们是完全不同的两个集成开发环境。C++Builder更多的是一个和Delphi同步的C++版本的开发环境,C++BuilderX则是完全从C++的角度思考得出的一个功能丰富的IDE。其最大的特点是跨q_Q跨~译器,多种Framework的集成,q且有一个WxWindows为基的GUI设计器。尤其是采用了纯C++来重写了整个Framework,摒弃了以前o人无奈的版本。对于C++的开发来_从编译器Q到库,到功能集成都是非常理想的。可以预见,Borland C++ Builder X 2.0很值得C++爱好者期待。唯一令h隑֠之处是作Z个C++的开发工P其IDE是用Java写的Q在配置不够理想的机器上h重考虑再安装?/p>

  3.5 Emacs + GCC

  前面讲的大部分是Windows环境下的集成开发环境。Linux上的开发者更們֐于用Emacs来编辑C++的文Ӟ用Makefile来命令GCC做编译。虽然看上去比较松散Q但是这些东西综合v来还是一个开0发环境。如果你能够娴熟的用这L环境写程序,你的水^应该_指导我们来写q篇陋文了?/p>

  3.6 Dev C++

  GCC是一个很好的~译器。在Windows上的C++~译器一直和标准有着一D距ȝ时候,GCC是一个让Windows下开发者流口水的编译器。Dev-C++是能够让GCC跑在Windows下的工具Q作为集成开发环境,q提供了同专业IDE相媲的语法高亮Q代码提C,调试{功能。由于用Delphi开发,占用内存,速度很快Q比较适合轻量U的学习和用?/p>

  3.7 Eclipse + CDT

  Eclipse可是q来大名鼎鼎的开发工兗最C期的Jolt大奖颁l了q个杰出的神物。说其神奇是因ؓQ它本n是用Java写的Q但是拥有比一般Java写的E序快得多的速度。而且因ؓ其基于插件组装一切的原则Q得能够有CDTq样的插件把Eclipse变成一个C/C++的开发环境。如果你一直用Eclipse写Java的程序,不妨用它体验一下C++开发的乐趣?/p>

--------------------------------------------------------------------------------

  4Q工?/p>

  C++的辅助工L多,我们分门别类的ؓ大家作介l:

  4.1 文c?/p>

  (1) Doxygen

  参考站点:http://www.doxygen.org/

  Doxygen是一U适合C风格语言Q如C++、C、IDL、Java甚至包括C#和PHPQ的、开放源码的、基于命令行的文生器?/p>

  (2) C++2HTML

  参考站点:http://www.bedaux.net/cpp2html/

  把C++代码变成语法高亮的HTML

  (3) CodeColorizer

  参考站点:http://www.chami.com/colorizer/

  它能把好几种语言的源代码着色ؓHTML

  (4) Doc-O-Matic

  参考站点:http://www.doc-o-matic.com/

  Doc-O_MaticZ的C/C++QC++.netQDelphi/Pascal, VB.NETQC#和JavaE序或者组件生准的文。Doc-O-Matic使用源代码中的符号和注释以及外部的文档文件创Z行的文档样式一致的文?/p>

  (5) DocVizor

  参考站点:http://www.ucancode.net/Products/DocBuilder/Features.htm

  DocVizor满了面向对象Y件开发者的基本要求——它让我们能够看到C++工程中的cdơ结构。DocVizor快速地产生完整可供打印的类层次l构图,包括从第三方库中来的那些c,除此之外DocVizorq能从类信息中生HTML文g?/p>

  (6) SourcePublisher C++

  参考站点:http://www.scitools.com/sourcepublisher_c.html

  l源代码产生提供快速直观的HTML报表Q包括代码,cdơ结构,调用和被调用树,包含和被包含树。支持多U操作系l?/p>

  (7) Understand

  参考站点:http://www.scitools.com/ucpp.html

  分析M规模的C或者C++工程Q帮助我们更好的理解以及~写文?/p>

  4.2 代码c?/p>

  (1) CC-Rider

  参考站点:http://www.cc-rider.com/

  CC-Rider是用于C/C++E序强大的代码可视化工具Q通过交互式浏览、编辑及自动文g来促q程序的l持和发展?/p>

  (2) CodeInspect

  参考站点:http://www.yokasoft.com/

  一U新的C/C++代码分析工具。它查我们的源代码找出非标准的,可能的,以及普通的错误代码?/p>

  (3) CodeWizard

  参考站点:http://www.parasoft.com/

  先进的C/C++源代码分析工P使用过500个编码规范自动化地标明危险的Q但是编译器不能查到的代码结构?/p>

  (4) C++ Validation Test Suites

  参考站点:http://www.plumhall.com/suites.html

  一l用于测试编译器和库对于标准dE度的代码库?/p>

  (5) CppRefactory

  参考站点:http://cpptool.sourceforge.net/

  CPPRefactory是一个得开发者能够重构他们的C++代码的程序。目的是使得C++代码的重构能够尽可能的有效率和简单?/p>

  (6) Lzz

  参考站点:http://www.lazycplusplus.com/

  Lzz是一个自动化许多C++~程中的体力zȝ工具。它能够节省我们许多事gq且使得~码更加有乐。给Zpd的声明,Lzz会给我们创徏头文件和源文件?/p>

  (7) QA C++ Generation 2000

  参考站点:http://www.programmingresearch.com/solutions/qacpp.htm

  它关注面向对象的C++源代码,Ҏ关于设计Q效率,?性,可维护性的部分提出警告信息?/p>

  (8) s-mail project - Java to C++DOL

  参考站点:http://sadlocha.strefa.pl/s-mail/ja2dol.html

  把Java源代码翻译ؓ相应的C++源代码的命o行工兗?/p>

  (9) SNIP from Cleanscape Software International

  参考站点:http://www.cleanscape.net/stdprod/snip/index.html

  一个填q编码和设计之间沟壑的易于用的C++开发工P节省大量~辑和调试的事gQ它q得开发者能够指定设计模式作为对象模型,自动从对象模型中产生C++的类?/p>

  (10) SourceStyler C++

  参考站点:http://www.ochresoftware.com/

  对C/C++源代码提供完整的格式化和排版控制的工兗提供多?5个的格式化选项以及完全支持ANSI C++?/p>

  4.3 ~译c?/p>

  (1) Compilercache

  参考站点:http://www.erikyyy.de/compilercache/

  Compilercache是一个对你的C和C++~译器的装脚本。每ơ我们进行编译,装脚本Q把~译的结果放入缓存,一旦编译相同的东西Q结果将从缓存中取出而不是再ơ编译?/p>

  (2) Ccache

  参考站点:http://ccache.samba.org/

  Ccache是一个编译器~存。它使用h像C/C++~译器的~存预处理器Q编译速度通常能提高普通编译过E的5~10倍?/p>

  (3) Cmm (C++ with MultiMethods)

  参考站点:http://www.op59.net/cmm/cmm-0.28/users.html

  q是一UC++语言的扩展。读入Cmm源代码输出C++的源代码Q功能是对C++语言d了对multimethod的支持?/p>

  (4) The Frost Project

  参考站点:http://frost.flewid.de/

  Forst使得你能够在C++E序中像原生的C++Ҏ一样用multimethod以及虚函数参数。它是一个编译器的外壟?/p>

  4.4 试和调试类

  (1) CPPUnit

  CppUnit 是个Z LGPL 的开源项目,最初版本移植自 JUnitQ是一个非怼U的开源测试框架。CppUnit ?JUnit 一样主要思想来源于极限编E。主要功能就是对单元试q行理Qƈ可进行自动化试?br />

  (2) C++Test

  参考站点:http://www.parasoft.com/

  C++ Test是一个单元测试工P它自动化了C和C++c,函数或者组件的试?/p>


  (3) Cantata++

  参考站点:http://www.iplbath.com/products/tools/pt400.shtml

  设计的目的是Z满在合理的l济开销下用这个工具可以让开发工E师开展单元测试和集成试的需?

  (4) Purify

  参考站点:http://www-900.ibm.com/cn/software/rational/products/purif
yplus/index.shtml

  IBM Rational PurifyPlus是一套完整的q行时分析工P旨在提高应用E序的可*性和性能。PurifyPlus内存错误和泄漏、应用程序性能描述、代码覆盖分析等功能l合在一个单一、完整的工具包中?/p>

  (5) BoundsChecker

  BoundsChecker是一个C++q行旉误检和调试工具。它通过在Visual Studio内自动化调试q程加速开发ƈ且羃短上市的周期。BoundsChecker提供清楚Q详l的E序错误分析Q许多是对C++独有的ƈ且在staticQstack和heap内存中检和诊断错误Q以及发现内存和资源的泄漏。  

  (6) Insure++

  参考站点:http://www.parasoft.com/

  一个自动化的运行时E序试工具Q检查难以察觉的错误,如内存覆盖,内存泄漏Q内存分配错误,变量初始化错误,变量定义冲突Q指针错误,库错误,逻辑错误和算法错误等?/p>

  (7) GlowCode

  参考站点:http://www.glowcode.com/

  GlowCode包括内存泄漏查,code profilerQ函数调用跟t等功能。给C++开发者提供完整的错误诊断Q和q行时性能分析工具包?/p>

  (8) Stack Spy

  参考站点:http://www.imperioustech.com/

  它能捕捉stack corruption, stack over run, stack overflow{有x的错误?/p>

------------------------------------------------------------------------
 5Q库

  在C++中,库的C是非帔R的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的a论。现实中QC++的库门类J多Q解决的问题也是极其q泛Q库从轻量到重量的都有。不都是让人眼界大开Q亦或是望而生叹的思维C。由于库的数量非常庞大,而且限于W者水qI其中很多q不了解。所以文中所提的一些库都是比较著名的大型库?/p>

  5.1 标准?/p>

  标准库中提供了C++E序的基本设施。虽然C++标准库随着C++标准折腾了许多年Q直到标准的出台才正式定型,但是在标准库的实C却很令hƣ慰得看到多U实玎ͼq且已被实践证明为有工业U别强度的佳作?/p>

  (1) Dinkumware C++ Library

  参考站点:http://www.dinkumware.com/

  P.J. Plauger~写的高品质的标准库。P.J. Plauger博士是Dr. Dobb'sE序设计杰出奖的获得者。其~写的库长期被Microsoft采用Qƈ且最qBorland也取得了其OEM的licenseQ在其C/C++的品中采用Dinkumware的库?/p>

  (2) RogueWave Standard C++ Library

  参考站点:http://www.roguewave.com/

  q个库在Borland C++ Builder的早期版本中曄被采用,后来被其他的库给替换了。笔者不推荐使用?/p>

  (3) SGI STL

  参考站点:http://www.roguewave.com/

  SGI公司的C++标准模版库?/p>

  (4) STLport

  参考站点:http://www.stlport.org/

  SGI STL库的跨^台可UL版本?/p>

  5.2 “准”标准库 - Boost

  参考站点:http://www.boost.org/

  国内镜像Q?a >http://www.c-view.org/tech/lib/boost/index.htm

  Boost库是一个经q千锤百点{可UL、提供源代码的C++库,作ؓ标准库的后备Q是C++标准化进E的发动Z一?Boost库由C++标准委员会库工作l成员发P在C++C֌中媄响甚大,其成员已q?000人?Boost库ؓ我们带来了最新、最酗最实用的技术,是不折不扣的“准”标准库?/p>

  Boost中比较有名气的有q么几个库:

  Regex

  正则表达式库

  Spirit

  LL parser frameworkQ用C++代码直接表达EBNF

  Graph

  囄件和法

  Lambda

  在调用的地方定义短小匿名的函数对象,很实用的functional功能

  concept check

  查泛型编E中的concept

 

  Mpl

  用模板实现的元编E框?/p>

 

  Thread

  可移植的C++多线E库

 

  Python

  把C++cd函数映射到Python之中

  Pool

  内存池管?/p>

 

  smart_ptr

  5个智能指针,学习指针必读Q一份不错的参考是来自CUJ的文章:

  Smart Pointers in BoostQ哦Q这文章可以查刎ͼCUJ是提供在U浏览的。中文版见笔者在《Dr. Dobb's Journal软g研发杂志》第7辑上的译文?/p>

  BoostM来说是实用h值很高,质量很高的库。ƈ且由于其对跨q_的强调,Ҏ准C++的强调,是编写^台无养ICC++的开发者必备的工具。但是Boost中也有很多是实验性质的东西,在实际的开发中实用需要}慎。ƈ且很多Boost中的库功能堪U对语言功能的扩展,其构造用精巧的手法Q不要N然的p旉研读。Boost另外一面,比如Graphq样的库则是h工业强度Q结构良好,非常值得研读的精品代码,q且也可以放心的在品代码中多多利用?/p>

  5.3 GUI

  在众多C++的库中,GUI部分的库是比较J荣Q也比较引h注目的。在实际开发中QGUI库的选择也是非常重要的一件事情,下面我们lD一下可选择的GUI库,各自的特点以及相兛_L支持?/p>

  (1) MFC

  大名鼎鼎的微软基cdQMicrosoft Foundation ClassQ。大凡学qVC++的h都应该知道这个库。虽然从技术角度讲QMFC是不大漂亮的Q但是它构徏于Windows API 之上Q能够ɽE序员的工作更容?~程效率高,减少了大量在建立 Windows E序时必ȝ写的代码Q同时它q提供了所有一?C++ ~程的优点,例如l承和封装。MFC ~写的程序在各个版本的Windows操作pȝ上是可移植的Q例如,在Windows 3.1下编写的代码可以很容易地UL?Windows NT ?Windows 95 上。但是在最q发展以及官Ҏ持上日渐势微?/p>

  (2) QT

  参考网站:http://www.trolltech.com/

  Qt是Trolltech公司的一个多q_的C++囑Ş用户界面应用E序框架。它提供l应用程序开发者徏立艺术的图形用L面所需的所用功能。Qt是完全面向对象的很容易扩展,q且允许真正地组件编E。自?996q早些时候,Qtq入商业领域Q它已经成ؓ全世界范围内数千U成功的应用E序的基。Qt也是行的Linux桌面环境KDE 的基Q同时它q支持Windows、Macintosh、Unix/X11{多U^台?/p>

  (3) WxWindows

  参考网站:http://www.wxwindows.org/

  跨^台的GUI库。因为其cdơ极像MFCQ所以有文章介绍从MFC到WxWindows的代码移植以实现跨^台的功能。通过多年的开发也是一个日完善的GUI库,支持同样不弱于前面两个库。ƈ且是完全开放源代码的。新q的C++ Builder X的GUI设计器就是基于这个库的?/p>

  (4) Fox

  参考网站:http://www.fox-toolkit.org/

  开放源代码的GUI库。作者从自己亲n的开发经验中得出了一个理想的GUI库应该是什么样子的感受出发Q从而开始了对这个库的开发。有兴趣的可以尝试一下?/p>


  (5) WTL

  ZATL的一个库。因Z用了大量ATL的轻量手法Q模板等技术,在代码尺寸,以及速度优化斚w做得非常C。主要面向的使用体是开发COM轻量U供|络下蝲的可视化控g的开发者?/p>

  (6) GTK

  参考网站:http://gtkmm.sourceforge.net/

  GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnomeq样的杀手应用。而GTK是q个库的C++装版本?/p>

  5.4 |络通信

  (1) ACE

  参考网站:http://www.cs.wustl.edu/~schmidt/ACE.html

  C++库的代表Q超重量U的|络通信开发框架。ACE自适配通信环境QAdaptiveCommunication EnvironmentQ是可以自由使用、开放源代码的面向对象框Ӟ在其中实C许多用于q发通信软g的核心模式。ACE提供了一l丰富的可复用C++包装外观QWrapper FacadeQ和框架lgQ可跨越多种q_完成通用的通信软gdQ其中包括:事g多\分离和事件处理器分派、信号处理、服务初始化、进E间?br />信、共享内存管理、消息\由、分布式服务动态(重)配置、ƈ发执行和同步Q等{?/p>

  (2) StreamModule

  参考网站:http://www.omnifarious.org/StrMod/

  设计用于化编写分布式E序的库。尝试着使得~写处理异步行ؓ的程序更ҎQ而不是用同步的外壛_起异步的本质?/p>

  (3) SimpleSocket

  参考网站:http://home.hetnet.nl/~lcbokkers/simsock.htm

  q个cd让编写基于socket的客?服务器程序更加容易?/p>

  (4) A Stream Socket API for C++

  参考网站:http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.h
tml

  又一个对Socket的封装库?/p>

  5.5 XML

  (1) Xerces

  参考网站:http://xml.apache.org/xerces-c/

  Xerces-C++ 是一个非常健壮的XML解析器,它提供了验证Q以及SAX和DOM API。XML验证在文类型定?Document Type DefinitionQDTD)斚w有很好的支持Qƈ且在2001q?2月增加了支持W3C XML Schema 的基本完整的开放标准?/p>

  (2) XMLBooster

  参考网站:http://www.xmlbooster.com/

  q个库通过产生特制的parser的办法极大的提高了XML解析的速度Qƈ且能够生相应的GUIE序来修改这个parser。在DOM和SAX两大LXML解析办法之外提供了另外一个可行的解决Ҏ?/p>

  (3) Pull Parser

  参考网站:http://www.extreme.indiana.edu/xgws/xsoap/xpp/

  q个库采用pullҎ的parser。在每个SAX的parser底层都有一个pull的parserQ这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得试?br />
  (4) Xalan

  参考网站:http://xml.apache.org/xalan-c/

  Xalan是一个用于把XML文档转换为HTMLQ纯文本或者其他XMLcd文档的XSLT处理器?/p>

  (5) CMarkup

  参考网站:http://www.firstobject.com/xml.htm

  q是一U用EDOM的XML解析器。在很多思\上面非常灉|实用。值得大家在DOM和SAX之外L一点灵感?/p>

  (6) libxml++

  http://libxmlplusplus.sourceforge.net/

  libxml++是对著名的libxml XML解析器的C++装版本

  5.6 U学计算

  (1) Blitz++

  参考网站:http://www.oonumerics.org/blitz/

  Blitz++ 是一个高效率的数D函数库Q它的设计目的是希望建立一套既具像C++ 一h便,同时又比Fortran速度更快的数D环境。通常Q用C++所写出的数值程序,?Fortran?0%左右Q因此Blitz++正是要改掉这个缺炏V方法是利用C++的template技术,E序执行甚至可以比Fortran更快。Blitz++目前仍在发展中,对于常见的SVDQFFTsQQMRES{常见的U性代数方法ƈ不提供,不过使用者可以很Ҏ地利用Blitz++所提供的函数来构徏?/p>

  (2) POOMA

  参考网站:http://www.codesourcery.com/pooma/pooma

  POOMA是一个免费的高性能的C++库,用于处理q行式科学计。POOMA的面向对象设计方便了快速的E序开发,对ƈ行机器进行了优化以达到最高的效率Q方便在工业和研I环境中使用?/p>

  (3) MTL

  参考网站:http://www.osl.iu.edu/research/mtl/

  Matrix Template Library(MTL)是一个高性能的泛型组件库Q提供了各种格式矩阵的大量线性代数方面的功能。在某些应用使用高性能~译器的情况下,比如Intel的编译器Q从产生的汇~代码可以看出其与手写几乎没有两L效能?/p>

  (4) CGAL

  参考网站:http://www.cgal.org/

  Computational Geometry Algorithms Library的目的是把在计算几何斚w的大部分重要的解x案和Ҏ以C++库的形式提供l工业和学术界的用户?/p>

  5.7 游戏开?/p>

  (1) Audio/Video 3D C++ Programming Library

  参考网站:http://www.galacticasoftware.com/products/av/

  AV3D是一个跨q_Q高性能的C++库。主要的Ҏ是提供3D囑ŞQ声效支持(SB,以及S3MQ,控制接口Q键盘,鼠标和遥感)QXMS?/p>

  (2) KlayGE

  参考网站:http://home.g365.net/enginedev/

  国内游戏开发高手自qC++开发的游戏引擎。KlayGE是一个开放源代码、跨q_的游戏引擎,q用Python作脚本语a。KlayGE在LGPL协议下发行。感谢龚敏敏先生Z国游戏开发事业所做出的A献?/p>

  (3) OGRE

  参考网站:http://www.ogre3d.org/

  OGREQ面向对象的囑Ş渲染引擎Q是用C++开发的Q用灵zȝ面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基?Dg讑֤的应用程序或游戏。引擎中的类库对更底层的pȝ库(如:Direct3D和OpenGLQ的全部使用l节q行了抽象,q提供了Z现实世界对象的接口和其它cR?/p>

  5.8 U程

  (1) C++ Threads

  参考网站:http://threads.sourceforge.net/

  q个库的目标是给E序员提供易于用的c,q些c被l承以提供在Linux环境中很隄到的大量的线E方面的功能?/p>

  (2) ZThreads

  参考网站:http://zthread.sourceforge.net/

  一个先q的面向对象Q跨q_的C++U程和同步库?br />

  5.9 序列?/p>

  (1) s11n

  参考网站:http://s11n.net/

  一个基于STL的C++库,用于序列化PODQSTL容器以及用户定义的类型?/p>

  (2) Simple XML Persistence Library

  参考网站:http://sxp.sourceforge.net/

  q是一个把对象序列化ؓXML的轻量的C++库?/p>

  5.10 字符?/p>

  (1) C++ Str Library

  参考网站:http://www.utilitycode.com/str/

  操作字符串和字符的库Q支持Windows和支持gcc的多U^台。提供高度优化的代码Qƈ且支持多U程环境和UnicodeQ同时还有正则表辑ּ的支持?/p>

  (2) Common Text Transformation Library

  参考网站:http://cttl.sourceforge.net/

  q是一个解析和修改STL字符串的库。CTTL substringcd以用来比较,插入Q替换以及用EBNF的语法进行解析?/p>

  (3) GRETA

  参考网站:http://research.microsoft.com/projects/greta/

  q是由微软研I的研Ih员开发的处理正则表达式的库。在型匚w的情况下有非怼U的表现?/p>

  5.11 l合

  (1) P::Classes

  参考网站:http://pclasses.com/

  一个高度可UL的C++应用E序框架。当前关注类型和U程安全的signal/slot机制Qi/opȝ包括Z插g的网l协议透明的i/o架构Q基于插件的应用E序消息日志框架Q访问sql数据库的cȝ{?/p>

  (2) ACDK - Artefaktur Component Development Kit

  参考网站:http://acdk.sourceforge.net/

  q是一个^台无关的C++lg框架Q类gJava或?NET中的框架Q反机ӞU程QUnicodeQ废料收集,I/OQ网l,实用工具QXMLQ等{)Q以及对Java, Perl, Python, TCL, Lisp, COM ?CORBA的集成?/p>

  (3) dlib C++ library

  参考网站:http://www.cis.ohio-state.edu/~kingd/dlib/

  各种各样的类的一个综合。大整数QSocketQ线E,GUIQ容器类,以及览目录的API{等?/p>

  (4) Chilkat C++ Libraries

  参考网站:http://www.chilkatsoft.com/cpp_libraries.asp

  q是提供zipQe-mailQ编码,S/MIMEQXML{方面的库?/p>

  (5) C++ Portable Types Library (PTypes)

  参考网站:http://www.melikyan.com/ptypes/

  q是STL的比较简单的替代品,以及可移植的多线E和|络库?/p>

  (6) LFC

  参考网站:http://lfc.sourceforge.net/

  哦,q又是一个尝试提供一切的C++?/p>

  5.12 其他?/p>

  (1) Loki

  参考网站:http://www.moderncppdesign.com/

  哦,你可能抱怨我早该和Boost一起介l它Q一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。ƈ且尝试把cM设计模式q样思想层面的东襉K过库来提供。同时还提供了智能指针这h较实用的功能?/p>

  (2) ATL

  ATL(Active Template Library)

  是一l小巧、高效、灵zȝc,q些cMؓ创徏可互操作的COMlg提供了基本的设施?/p>

  (3) FC++: The Functional C++ Library

  q个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外L另一分的乐趣Q可以去看看函数式程序设计的世界。大师Peter Norvig?“Teach Yourself rogramming in Ten Years”一文中将函数式语a列ؓ臛_应当学习?cȝE语a之一?/p>

  (4) FACT!

  参考网站:http://www.kfa-juelich.de/zam/FACT/start/index.html

  另外一个实现函数式语言Ҏ的?/p>

  (5) Crypto++

  提供处理密码Q消息验证,单向hashQ公匙加密系l等功能的免费库?/p>

  q有很多非常Ȁ动h心或者是极其实用的C++库,限于我们的水q以及文章的幅不能包括q来。在对于q些已经包含q来的库的介l中Q由于ƈ不是每一个我们都使用q,所以难免有偏颇之处Q请读者见谅?/p>

--------------------------------------------------------------------------------

  6Q书c?/p>

  以前熊节先生曾撰文评论相对于JavaE序设计语言QC++的好书多如牛毛。荣耀先生在《程序员》杂志上撰文《C++E序设计之四书五l》也本领域内几乎所有的l典书籍作了全面的介l?M关于书的评论此时看来便是很多余的了。个人浅见,除非你打以C++作ؓ唯一兴趣或者生存之本,一般读者确实没有够的旉和必要将20余本书籍全部阅读。更有参考h值的是荣耀先生的另一文章:《至应该阅
ȝ九本C++著作》,可以从下面的地址览到此文:

  http://www.royaloo.com/articles/articles_2003/9CppBooks.htm

  下面几本书对于走在C++初学之\上的读者是我们最愿意推荐l大家的Q?/p>

  (1) 《C++ Primer?/p>

  哦,也许你会抱怨我们ؓ什么不先介lTCPL,但对于走在学习之路上的入门者,本书内容更ؓ全面Q更l易懂,我们U它为“C++的超U宝典”ƈ不过分。配有一本不错的习题解答《C++ Primer Answer Book》可以辅助你的学习之路?/p>

  (2) 《Essential C++?/p>

  如果说《C++ Primer》是C++领域的超U宝典,那么此书作ؓ掌握C++的大局观当之无愧。正如?NET大局观》一书能够让读者全?NETQ本书讲qCC++中最核心的全部主题。书虽不厚,内容_Q不׃ؓ《C++ Primer》读者茶余饭后的主题回顾之作?/p>

  (3) 《The C++ Programming Language?/p>

  BjarneZ带来的C++教程Q真正能够告诉你怎么用才叫真正的C++的唯一一本书。虽然如同“某某程序设计语a”这L书籍会给大家一个内容全揽,入门到精通的感觉Q但本书实不太适合初学者阅诅R如果你自认为是一名很有经验的C++E序员,那至也要反复咀嚼Bjarne先生所的若q内宏V?/p>

  (4) 《Effective C++》,《More Effective C++?/p>

  是的Q正如一些C++爱好者经总读过与没有读q上qC本作品来区分你是否是C++高手。我们也极力推崇q两本著作。在各种介绍C++专家l验的书c里面,q两本是最贴近语言本质Q看后最能够有脱胎换骨感觉的书,L书你需每日三省汝n?/p>

  技术书c仁者见仁,q多的评论反无太多意义,p者喜好选择最适合自己的书方ؓ上策?/p>

--------------------------------------------------------------------------------

  7Q资源网?/p>

  正如我们可以通过计算机历史上的重要h物了解计机史的发展QC++相关人物的网站也可以使我们得到最有h值的参考与借鉴Q下面的人物我们认ؓ没有介绍的必要,只因下面的h物在C++领域的地位众所周知Q我们只相关的资源q行|列以供读者学习,他们有的工作于贝实验室Q有的工作于知名~译器厂商,有的在不断推q语a的标准化Q有的ؓ读者撰写了多部千古奇作…?br />  (1) Bjarne Stroustrup
  http://www.research.att.com/~bs/

  (2) Stanley B. Lippman
  http://blogs.msdn.com/slippman/
  中文?http://www.zengyihome.net/slippman/index.htm

  (3) Scott Meyers
  http://www.aristeia.com/

  (4) David Musser
  http://www.cs.rpi.edu/~musser/

  (5) Bruce Eckel
  http://www.bruceeckel.com/

  (6) Nicolai M. Josuttis
  http://www.josuttis.com/

  (7) Herb Sutter
  http://www.gotw.ca/

  (8) Andrei Alexandrescu
  http://www.coderncppdesign.com/

  (9) 侯捷先生
  http://www.jjhou.com/

  (10) 孟岩先生
  先生J忙于工作,痴迷于技术,暂无个h主页Q关于先生的作品可以通过CSDN的专栏和侯先生的主页讉K到?/p>

  (11) 荣耀先生
  http://www.royaloo.com/

  (12) 潘爱民先?br />  http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm

  除了上述大师的主外Q以下的l合cC++学习参考站Ҏ我们非常愿意向大家推荐的Q?/p>

  (1) CodeProject
  http://www.codeproject.com/

  (2) CodeGuru
  http://www.codeguru.com/

  (3) Dr. Dobb's Journal
  http://www.ddj.com/

  (4) C/C++ Users Journal
  http://www.cuj.com/

  (5) Cl视?br />  http://www.c-view.org/

  (6) allaboutprogram
  http://www.allaboutprogram.com/
  其他资料

  (1) ISO IEC JTC1/SC22/WG21 - C++Q标准C++的权威参?br />  http://anubis.dkuug.dk/jtc1/sc22/wg21/

  (2) C++ FAQ LITE ?Frequently Asked Questions: 最为全面的C++FAQ
  http://www.sunistudio.com/cppfaq/index.html
  C/C++ 新闻l:
  你不妨尝试从q里提问和回{问题,很多不错的Q&A资源......

  (1) .alt.comp.lang.learn.c-c++
  q个单些Q如果你和我一h个菜?/p>

  (2) .comp.lang.c++.moderated
  嗯,q个昄水^高一?/p>

  (3) .comp.std.c++
  如果你需要讨论标准C++相关话题的话

--------------------------------------------------------------------------------

  8Q不得不写的l束?/p>

  l束的时候也是ȝ现状Q展望未来的时候。虽然C++从脱胎于C开始,一路艰隑֝L走过来,但是无论如何C++已经取得了工业基的地位。文章列丄大量相关资源是最好的证明Q而业界的大量用C++写成的品代码以及大量的C++职业工程师则是最直接的证明。同Ӟ我们可以看到各个高校的计机专业都开设有C++q门评Q网l上对于C++的学习讨Z从来都没有停q。但是,在Java?NET两大企业开发^台的围攻下,lh的感觉是C++来“不行”了?/p>

  C++在面向企业的软g开发中Q在开发便h等斚w的确要比Java和C#差很多,其中一个问题是C++语言本n比较复杂Q学习曲U比较陡峭,另外一个问题是C++标准化的旉太长Q׃很多的壮大机会,耗费了很多精力在厂商的之间的斗争上,而C++的标准库M个完善的E序开发框架还~少太多太多的内容,各个W三方的cd和框架又在一致性和完整性上没法和随q_提供的框架相提ƈ论。难道C++真的要退出历史舞CQ?/p>

  从C++目前的活跃程度,以及应用现状来说是完全能够肯定C++仍然是Y件工业的基础Q也不会退出历史舞台的。另外从BoostQLokiq些库中我们也能够看到C++的发展非常活跃,对于新技术新思维非常Ȁq,C++仍然q泛受到x。从ACE在高性能通信领域的应用,以及MTLq样的库在数D领域的表现Q我们可以看到C++在高性能应用场合下的不可替代的作用,而嵌入式pȝq样的内存受限开发^?br />Q比如Symbian OS上,C++已经发挥着q且发挥更大的作用。可以预见的是以后的软g无论上层的应用怎么变,它的底层核心都会是由C/C++q样的系l软g~写的,比如Java虚拟机,.NET Framwork。因为只有这LpȝUY件才能完全彻底的发挥机器的功能?/p>

  需要看到的是两个趋势,一个趋势是C++变得更加复杂Q更加学院派Q通过模板{有潜力的语法因素构造越来越_y的库成ؓ了现代C++的热点,虽然在利用库实现新的~程范式Q乃臌计模式等斚w很有开创意义,也确实生了一些能够便捷开发的工具Q但是更多的是把C++变得更加强大Q更加复杂,也更加难懂,g也更加学院派Q不得不说它正在向边~化道\发展。另一个趋势是C++在主的企业应用开
发中已经逐渐退ZQERPq样的企业Y件开发中基本上不会考虑C++Q除非需要考虑性能或者和遗留代码的集成这些因素。C++退守到pȝU别语言Q成Y件工业的基础是大势所。然而反思一下,真的是退守么Q自从STL出现Q无数的人风起云涌的开始支持C++,他们狂呼“我看到深夜消失了,目标软g工程的出现。我看到了可l护的代码。”是的,STL在可l护性下做得如此。但是又怎样呢?STL为C++铺^了现代Y件工E的道\Q而在上层应用E序软g开发领域这块场地早不单独属于C++,很多E序设计语言都做得很Q疯狂的支持者会毫不犹U地说我们应当支持C++,因ؓ它是世界上最的语言。而坦率地_你的腰杆真的那么么Q也许只是在逃避一些事实。C++是优U的,q不可否认,STL的出现让C++一度走上了最辉煌的时刻,然而现在看来……我的一位恩师曾aQ真正能够将STL应用得淋漓尽致的人很
保守地说国内也不过200人,或许不加入STL能够使C++向着它应当发展的方向发展的更好,而现在看来,C++也应当回首到真正属于他的那一片圣C…?/p>

独孤九剑 2007-02-05 10:07 发表评论
]]>
用Winsock实现语音全双工通信使用http://www.shnenglu.com/eday/archive/2007/01/31/18209.html独孤九剑独孤九剑Wed, 31 Jan 2007 03:47:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18209.html
  一、引a?br />
  Windows 95作ؓ微机的操作系l,已经完全融入了网l与通信功能Q不仅可以徏立纯Windows 95环境下的“对{网l”,而且支持多种协议Q如TCP/IP、IPX/SPX、NETBUI{。在TCP/IP协议l中QTPC是一U面向连接的协义Qؓ用户提供可靠的、全双工的字节流服务Q具有确认、流控制、多路复用和同步{功能,适于数据传输。UDP协议则是无连接的Q每个分l都携带完整的目的地址Q各分组在系l中独立传送。它不能保证分组的先后顺序,不进行分l出错的恢复与重传,因此不保证传输的可靠性,但是Q它提供高传输效率的数据报服务,适于实时的语韟뀁图像传输、广播消息等|络传输?br />
  Winsock接口E间通信提供了一U新的手D,它不但能用于同一机器中的q程之间通信Q而且支持|络通信功能。随着Windows 95的推出。Winsock已经被正式集成到了Windowspȝ中,同时包括?6位和32位的~程接口。而Winsock的开发工具也可以在Borland C++4.0、Visual C++2.0q些C~译器中扑ֈQ主要由一个名为winsock.h的头文g和动态连接库winsock.dll或wsodk32.dlll成Q这两种动态连接库分别用于Win16和Win32的应用程序。?br />
  本文针对话音的全双工传输要求Q采用UDP协议实现了实时网l通信。用VisualC++2.0~译环境Q其动态连接库名ؓwsock32.dll。?

  二、主要函数的使用要点?/b>

  通过建立双套接字Q可以很方便地实现全双工|络通信。?br />
  1.套接字徏立函敎ͼ?br />

SOCKET socket(int family,int type,int protocol)?

  对于UDP协议Q写为:?br />
SOCKRET s;?br />s=socket(AF_INET,SOCK_DGRAM,0);?br />或s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)?/td>

  Z建立两个套接字,必须实现地址的重复绑定,卻I当一个套接字已经l定到某本地地址后,Z让另一个套接字重复使用该地址Q必Mؓ调用bind()函数l定W二个套接字之前Q通过函数setsockopt()套接字设|SO_REUSEADDR套接字选项。通过函数getsockopt()可获得套接字选项讄状态。需要注意的是,两个套接字所对应的端口号不能相同。此外,q涉及到套接字缓冲区的设|问题,按规定,每个区的讄范围是:不小?12个字节,大大?k字节Q根据需要,文中选用?k字节。?br />
  2.套接字绑定函数?br />
int bind(SOCKET s,struct sockaddr_in*name,int namelen)?/td>

  s是刚才创建好的套接字Qname指向描述通讯对象的结构体的指针,namelen是该l构体的长度。该l构体中的分量包括:IP地址(对应name.sin_addr.s_addr)、端口号(name.sin_port)、地址cd(name.sin_familyQ一般都赋成AF_INETQ表C是internet地址)。?br />
  (1)IP地址的填写方法:在全双工通信中,要把用户名对应的点分表示法地址转换?2位长整数格式的IP地址Q用inet_addr()函数。?br />
  (2)端口h用于表示同一台计机不同的进E?应用E序)Q其分配Ҏ有两U:1)q程可以让系lؓ套接字自动分配一端口P只要在调用bind前将端口h定ؓ0卛_。由pȝ自动分配的端口号位于1024~5000之间Q?~1023之间的Q一TCP或UDP端口都是保留的,pȝ不允怓Q一q程使用保留端口Q除非其有效用户ID是零(用户)。?br />
  2)q程可ؓ套接字指定一特定端口。这对于需要给套接字分配一众所端口的服务器是很有用的。指定范围ؓ1024?5536之间。可L指定。?br />
  在本E序中,对两个套接字的端口号规定?000?001Q前者对应发送套接字Q后者对应接收套接字?br />
  端口可从一?6位无W号?u_shortcd?从主机字节顺序{换成|络字节序Q用htons()函数。?br />
  Ҏ以上两个函数Q可以给出双套接字徏立与l定的程序片断?br />
//讄有关的全局变量?br />SOCKET sr,ss;?br />HPSTR sockBufferS,sockBufferR;?br />HANDLE hSendData,hReceiveData;?br />DWROD dwDataSize=1024*4;?br />struct sockaddr_in therel.there2;?br />#DEFINE LOCAL_HOST_ADDR 200.200.200.201?br />#DEFINE REMOTE_HOST-ADDR 200.200.200.202?br />#DEFINE LOCAL_HOST_PORT 2000?br />#DEFINE LOCAL_HOST_PORT 2001?br />//套接字徏立函数?
BOOL make_skt(HWND hwnd)?br />{?br />struct sockaddr_in here,here1;?br />ss=socket(AF_INET,SOCK_DGRAM,0);?br />sr=socket(AF_INET,SOCK_DGRAM,0);?br />if((ss==INVALID_SOCKET)||(sr==INVALID_SOCKET))?br />{?br />MessageBox(hwnd,“套接字建立p|!”,“?MB_OK);?br />return(FALSE);?br />}?br />here.sin_family=AF_INET;?br />here.sin_addr.s_addr=inet_addr(LOCAL_HOST_ADDR);?br />here.sin_port=htons(LICAL_HOST_PORT);?br />//another socket?br />herel.sin_family=AF_INET;?br />herel.sin_addr.s_addr(LOCAL_HOST_ADDR);?br />herel.sin_port=htons(LOCAL_HOST_PORT1);?br />SocketBuffer();//套接字缓冲区的锁定设|?br />setsockopt(ss,SOL_SOCKET,SO_SNDBUF,(char FAR*)sockBufferS,dwDataSize);
if(bind(ss,(LPSOCKADDR)&here,sizeof(here)))
{
MessageBox(hwnd,“发送套接字l定p|!”,“”,MB_OK);
return(FALSE);
}
setsockopt(sr SQL_SOCKET,SO_RCVBUF|SO_REUSEADDR,(char FAR*)
sockBufferR,dwDataSize);
if(bind(sr,(LPSOCKADDR)&here1,sizeof(here1)))
{
MessageBox(hwnd,“接收套接字l定p|!”,“”,MB_OK);
return(FALSE);
}
return(TRUE);
}
//套接字缓冲区讄
void sockBuffer(void)
{
hSendData=GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize);
if(!hSendData)
{
MessageBox(hwnd,“发送套接字~冲区定位失?”,NULL,
MB_OK|MB_ICONEXCLAMATION);
return;
}
if((sockBufferS=GlobalLock(hSendData)==NULL)
{
MessageBox(hwnd,“发送套接字~冲区锁定失?”,NULL,
MB_OK|MB_ICONEXCLAMATION);
GlobalFree(hRecordData[0];
return;
}
hReceiveData=globalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize);
if(!hReceiveData)
{
MessageBox(hwnd,"“接收套接字~冲区定位|!”,NULL
MB_OK|MB_ICONEXCLAMATION);
return;
}
if((sockBufferT=Globallock(hReceiveData))=NULL)
MessageBox(hwnd,"发送套接字~冲区锁定失?”,NULL,
MB_OK|MB_ICONEXCLAMATION);
GlobalFree(hRecordData[0]);
return;
}
{

  3.数据发送与接收函数Q?br />
int sendto(SOCKET s.char*buf,int len,int flags,struct sockaddr_in to,int
tolen);?br />int recvfrom(SOCKET s.char*buf,int len,int flags,struct sockaddr_in
fron,int*fromlen)

  其中Q参数flags一般取0。?br />
  recvfrom()函数实际上是dsendto()函数发过来的一个数据包Q当d的数据字节少于规定接收的数目Ӟ把数据全部接收Qƈq回实际接收到的字节敎ͼ当读到的数据多于规定值时Q在数据报文方式下,多余的数据将被丢弃。而在方式下Q剩余的数据׃recvfrom()d。ؓ了发送和接收数据Q必d立数据发送缓冲区和数据接收缓冲区。规定:IP层的一个数据报最大不过64K(含数据报?。当~冲|得q多、过大时Q常因内存不够而导致套接字建立p|。在减小~冲区后Q该错误消失。经q实验,文中选用?K字节。?br />
  此外Q还应注意这两个函数中最后参数的写法Q给sendto()的最后参数是一个整数|而recvfrom()的则是指向一整数值的指针。?br />
  4.套接字关闭函敎ͼclosesocket(SOCKET s)?br />
  通讯l束Ӟ应关闭指定的套接字,以释与之相关的资源。?br />
  在关闭套接字Ӟ应先寚w定的各种~冲区加以释放。其E序片断为:?br />
void CloseSocket(void)?br />{?br />GlobalUnlock(hSendData);?br />GlobalFree(hSenddata);?br />GlobalUnlock(hReceiveData);?br />GlobalFree(hReceiveDava);?br />if(WSAAysncSelect(ss,hwnd,0,0)=SOCKET_ERROR)?br />{?br />MessageBos(hwnd,“发送套接字关闭p|!”,“”,MB_OK);?br />return;?br />}?br />if(WSAAysncSelect(sr,hwnd,0,0)==SOCKET_ERROR)?br />{?
MessageBox(hwnd,“接收套接字关闭p|!”,“”,MB_OK);?br />return;?br />}?br />WSACleanup();?br />closesockent(ss);?br />closesockent(sr);?br />return;?br />}?/td>

  三、Winsock的编E特点与异步选择机制?/b>

  1 d及其处理方式?br />
  在网l通讯中,׃|络拥挤或一ơ发送的数据量过大等原因Q经怼发生交换的数据在短时间内不能传送完Q收发数据的函数因此不能q回Q这U现象叫做阻塞。WinsockҎ可能d的函数提供了两种处理方式Q阻塞和非阻塞方式。在d方式下,收发数据的函数在被调用后一直要C送完毕或者出错才能返回。在d期间Q被ȝ函数不会断调用系l函数GetMessage()来保持消息@环的正常q行。对于非d方式Q函数被调用后立卌回,当传送完成后由Winsockl程序发一个事先约定好的消息。?br />
  在编E时Q应量使用非阻塞方式。因为在d方式下,用户可能会长旉的等待过E中试图关闭E序Q因为消息@环还在v作用Q所以程序的H口可能被关闭,q样当函CWinsock的动态连接库中返回时Q主E序已经从内存中删除Q这昄是极其危险的。?br />
  2 异步选择函数WSAAsyncSelect()的用?br />
  Winsock通过WSAAsyncSelect()自动地设|套接字处于非阻塞方式。用WindowsSockets实现Windows|络E序设计的关键就是它提供了对|络事gZ消息的异步存取,用于注册应用E序感兴的|络事g。它hWindows Sockets DLL在检到套接字上发生的网l事件时Q向H口发送一个消息。对UDP协议Q这些网l事件主要ؓQ?br />
  FD_READ 期望在套接字收到数据(卌准备?时接攉知Q?br />
  FD_WRITE 期望在套接字可发送数(卛_准备?时接攉知Q?br />
  FD_CLOSE 期望在套接字关闭时接电通知?br />
  消息变量wParam指示发生|络事g的套接字Q变?Param的低字节描述发生的网l事Ӟ高字包含错误码。如在窗口函数的消息循环中均加一个分支:?br />
int ok=sizeof(SOCKADDR);?br />case wMsg;?br />switch(1Param)?br />{?br />case FD_READ:?br />//套接字上L据?br />if(recvfrom(sr.lpPlayData[j],dwDataSize,0,(struct sockaddr FAR*)&there1,
?br />(int FAR*)&ok)==SOCKET_ERROR0?br />{?br />MessageBox)hwnd,“数据接收失?”,“”,MB_OK);?br />return(FALSE);?br />}?br />case FD_WRITE:?br />//套接字上写数据?br />}?br />breakQ?/td>

  在程序的~制中,应根据需要灵zdWSAAsyncSelect()函灵敏放在相应的消息循环之中Q其它说明可参见文献[1]。此外,应该指出的是Q以上程序片断中的消息框主要是ؓE序调试方便而设|的Q而在正式产品中不再出现。同Ӟ按照E序定w误设计,应徏立一个专门的定w处理函数。程序中可能出现的各U错误都由该函数进行处理,依据错误的危害程度不同,建立几种不同的处理措施。这P才能保证双方通话的顺利和可靠。?br />
  四、结论?/b>

  本文是多媒体|络传输目的重要内容之一Q目前,l合g全双工语韛_{设备,已经成功地实C话音的全双工的通信。有x个多媒体传输pȝ设计的内容,有另文叙述?



独孤九剑 2007-01-31 11:47 发表评论
]]>
用VC++6.0的Sockets API实现一个聊天室E序http://www.shnenglu.com/eday/archive/2007/01/31/18207.html独孤九剑独孤九剑Wed, 31 Jan 2007 03:34:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18207.html1.VC++|络~程及Windows Sockets API?/font>

  VC++对网l编E的支持有socket支持QWinInet支持QMAPI和ISAPI支持{。其中,Windows Sockets API是TCP/IP|络环境里,也是Internet上进行开发最为通用的API。最早美国加州大学Berkeley分校在UNIX下ؓTCP/IP协议开发了一个APIQ这个API是著名的Berkeley Socket接口(套接?。在桌面操作pȝq入Windows时代后,仍然l承了SocketҎ。在TCP/IP|络通信环境下,Socket数据传输是一U特D的I/OQ它也相当于一U文件描q符Q具有一个类g打开文g的函数调?socket()。可以这L解:Socket实际上是一个通信端点Q通过它,用户的SocketE序可以通过|络和其他的Socket应用E序通信。Socket存在于一?通信?(为描qC般的U程如何通过Socketq行通信而引入的一U抽象概?里,q且与另一个域的Socket交换数据。Socket有三cR第一U是SOCK_STREAM(式)Q提供面向连接的可靠的通信服务Q比如telnet,http。第二种是SOCK_DGRAM(数据?Q提供无q接不可靠的通信Q比如UDP。第三种是SOCK_RAW(原始)Q主要用于协议的开发和试Q支持通信底层操作Q比如对IP和ICMP的直接访问?br />
  2.Windows Socket机制分析

  2.1一些基本的Socketpȝ调用

  主要的系l调用包括:socket()-创徏SocketQbind()-创建的Socket与本地端口绑定;connect()与accept()-建立Socketq接Qlisten()-服务器监听是否有q接hQsend()-数据的可控缓冲发送;recv()-可控~冲接收Qclosesocket()-关闭Socket?br />
  2.2Windows Socket的启动与l止

  启动函数WSAStartup()建立与Windows Sockets DLL的连接,l止函数WSAClearup()l止使用该DLLQ这两个函数必须成对使用?br />
  2.3异步选择机制

  Windows是一个非抢占式的操作pȝQ而不采取UNIX的阻塞机制。当一个通信事g产生Ӟ操作pȝ要根据设|选择是否对该事g加以处理QWSAAsyncSelect()函数是用来选择pȝ所要处理的相应事g。当Socket收到讑֮的网l事件中的一个时Q会l程序窗口一个消息,q个消息里会指定产生|络事g的SocketQ发生的事gcd和错误码?br />
  2.4异步数据传输机制

  WSAAsyncSelect()讑֮了Socket上的d应通信事g后,每发生一个这L事g׃产生一个WM_SOCKET消息传给H口。而在H口的回调函C应该添加相应的数据传输处理代码?br />
  3.聊天室程序的设计说明

  3.1实现思想

  在Internet上的聊天室程序一般都是以服务器提供服务端q接响应Q用者通过客户端程序登录到服务器,可以与d在同一服务器上的用户交谈,q是一个面向连接的通信q程。因此,E序要在TCP/IP环境下,实现服务器端和客L两部分程序?br />
  3.2服务器端工作程

  服务器端通过socket()pȝ调用创徏一个Socket数组?卌定了接受q接客户的最大数?Q与指定的本地端口绑定bind()Q就可以在端口进行侦听listen()。如果有客户端连接请求,则在数组中选择一个空SocketQ将客户端地址赋给q个Socket。然后登录成功的客户可以在服务器上聊天了?br />
  3.3客户端工作流E?br />
  客户端程序相对简单,只需要徏立一个Socket与服务器端连接,成功后通过q个Socket来发送和接收数据可以了?br />
  4.核心代码分析

  限于幅Q这里仅l出与网l编E相关的核心代码Q其他的诸如聊天文字的服务器和客L昄读者可以自行添加?br />
  4.1服务器端代码

  开启服务器功能:

void OnServerOpen() //开启服务器功能
{
 WSADATA wsaData;
 int iErrorCode;
 char chInfo[64];
 if (WSAStartup(WINSOCK_VERSION, &wsaData)) //调用Windows Sockets DLL
  { MessageBeep(MB_ICONSTOP);
   MessageBox("Winsock无法初始?", AfxGetAppName(), MB_OK|MB_ICONSTOP);
   WSACleanup();
   return; }
 else
  WSACleanup();
  if (gethostname(chInfo, sizeof(chInfo)))
  { ReportWinsockErr("\n无法获取L!\n ");
   return; }
  CString csWinsockID = "\n==>>服务器功能开启在端口QNo. ";
  csWinsockID += itoa(m_pDoc->m_nServerPort, chInfo, 10);
  csWinsockID += "\n";
  PrintString(csWinsockID); //在程序视图显C提CZ息的函数Q读者可自行创徏
  m_pDoc->m_hServerSocket=socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
  //创徏服务器端SocketQ类型ؓSOCK_STREAMQ面向连接的通信
  if (m_pDoc->m_hServerSocket == INVALID_SOCKET)
  { ReportWinsockErr("无法创徏服务器socket!");
   return;}
  m_pDoc->m_sockServerAddr.sin_family = AF_INET;
  m_pDoc->m_sockServerAddr.sin_addr.s_addr = INADDR_ANY;
  m_pDoc->m_sockServerAddr.sin_port = htons(m_pDoc->m_nServerPort);
  if (bind(m_pDoc->m_hServerSocket, (LPSOCKADDR)&m_pDoc->m_sockServerAddr,   
     sizeof(m_pDoc->m_sockServerAddr)) == SOCKET_ERROR) //与选定的端口绑?br />   {ReportWinsockErr("无法l定服务器socket!");
    return;}
   iErrorCode=WSAAsyncSelect(m_pDoc->m_hServerSocket,m_hWnd,
   WM_SERVER_ACCEPT, FD_ACCEPT);
   //讑֮服务器相应的|络事g为FD_ACCEPTQ即q接hQ?br />   // 产生相应传递给H口的消息ؓWM_SERVER_ACCEPT
  if (iErrorCode == SOCKET_ERROR)
   { ReportWinsockErr("WSAAsyncSelect讑֮p|!");
    return;}
  if (listen(m_pDoc->m_hServerSocket, QUEUE_SIZE) == SOCKET_ERROR) //开始监听客戯接请?br />   {ReportWinsockErr("服务器socket监听p|!");
    m_pParentMenu->EnableMenuItem(ID_SERVER_OPEN, MF_ENABLED);
    return;}
  m_bServerIsOpen = TRUE; //监视服务器是否打开的变?br /> return;
}

  响应客户发送聊天文字到服务器:ON_MESSAGE(WM_CLIENT_READ, OnClientRead)

LRESULT OnClientRead(WPARAM wParam, LPARAM lParam)
{
 int iRead;
 int iBufferLength;
 int iEnd;
 int iRemainSpace;
 char chInBuffer[1024];
 int i;
 for(i=0;(i
  //MAXClient是服务器可响应连接的最大数?br />  {}
 if(i==MAXClient) return 0L;
  iBufferLength = iRemainSpace = sizeof(chInBuffer);
  iEnd = 0;
  iRemainSpace -= iEnd;
  iBytesRead = recv(m_aClientSocket[i], (LPSTR)(chInBuffer+iEnd), iSpaceRemaining, NO_FLAGS);   //用可控缓冲接收函数recv()来接收字W?br />  iEnd+=iRead;
 if (iBytesRead == SOCKET_ERROR)
  ReportWinsockErr("recv出错!");
  chInBuffer[iEnd] = '\0';
 if (lstrlen(chInBuffer) != 0)
  {PrintString(chInBuffer); //服务器端文字昄
   OnServerBroadcast(chInBuffer); //自己~写的函敎ͼ向所有连接的客户q播q个客户的聊天文?br />  }
 return(0L);
}

  对于客户断开q接Q会产生一个FD_CLOSE消息Q只ȝ应地用closesocket()关闭相应的Socket卛_Q这个处理比较简单?br />
  4.2客户端代?br />
  q接到服务器Q?br />
void OnSocketConnect()
{ WSADATA wsaData;
 DWORD dwIPAddr;
 SOCKADDR_IN sockAddr;
 if(WSAStartup(WINSOCK_VERSION,&wsaData)) //调用Windows Sockets DLL
 {MessageBox("Winsock无法初始?",NULL,MB_OK);
  return;
 }
 m_hSocket=socket(PF_INET,SOCK_STREAM,0); //创徏面向q接的socket
 sockAddr.sin_family=AF_INET; //使用TCP/IP协议
 sockAddr.sin_port=m_iPort; //客户端指定的IP地址
 sockAddr.sin_addr.S_un.S_addr=dwIPAddr;
 int nConnect=connect(m_hSocket,(LPSOCKADDR)&sockAddr,sizeof(sockAddr)); //hq接
 if(nConnect)
  ReportWinsockErr("q接p|!");
 else
  MessageBox("q接成功!",NULL,MB_OK);
  int iErrorCode=WSAAsyncSelect(m_hSocket,m_hWnd,WM_SOCKET_READ,FD_READ);
  //指定响应的事Ӟ为服务器发送来字符
 if(iErrorCode==SOCKET_ERROR)
 MessageBox("WSAAsyncSelect讑֮p|!");
}

  接收服务器端发送的字符也用可控缓冲接收函数recv()Q客L聊天的字W发送用数据可控缓冲发送函数send()Q这两个q程比较单,在此׃加赘qC?br />
  5.结

  通过聊天室程序的~写Q可以基本了解Windows Sockets API~程的基本过E和_要之处。本E序在VC++6.0下编译通过Q在使用windows 98/NT的局域网里运行良好?br />


独孤九剑 2007-01-31 11:34 发表评论
]]>
Windows Sockets API实现|络异步通讯http://www.shnenglu.com/eday/archive/2007/01/31/18206.html独孤九剑独孤九剑Wed, 31 Jan 2007 03:32:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18206.html摘要Q?/strong>本文对如何用面向连接的式套接字实现对|卡的编E以及如何实现异步网l通讯{问题进行了讨论与阐q?

  一?引言

  ?0q代初,国加利尼亚大学伯克利分校的研Ih员ؓTCP/IP|络通信开发了一个专门用于网l通讯开发的API。这个API是Socket接口Q套接字Q?-当今在TCP/IP|络最为通用的一UAPIQ也是在互联|上q行应用开发最为通用的一UAPI。在微Y联合其它几家公司共同制定了一套Windows下的|络~程接口Windows Sockets规范后,׃在其规范中引入了一些异步函敎ͼ增加了对|络事g异步选择机制Q因此更加符合Windows的消息驱动特性,使网l开发h员可以更加方便的q行高性能|络通讯E序的设计。本文接下来针对Windows Sockets APIq行面向q接的流式套接字~程以及对异步网l通讯的编E实现等问题展开讨论?br />
  二?面向q接的流式套接字~程模型的设?/b>

  本文在方案选择上采用了在网l编E中最常用的一U模?-客户?服务器模型。这U客?服务器模型是一U非对称式编E模式。该模式的基本思想是把集中在一L应用划分成ؓ功能不同的两个部分,分别在不同的计算Zq行Q通过它们之间的分工合作来实现一个完整的功能。对于这U模式而言其中一部分需要作为服务器Q用来响应ƈ为客h供固定的服务Q另一部分则作为客hE序用来向服务器提出h或要求某U服务?br />
  本文选取了基于TCP/IP的客h/服务器模型和面向q接的流式套接字。其通信原理为:服务器端和客L都必d立通信套接字,而且服务器端应先q入监听状态,然后客户端套接字发出q接hQ服务器端收到请求后Q徏立另一个套接字q行通信Q原来负责监听的套接字仍q行监听Q如果有其它客户发来q接hQ则再徏立一个套接字。默认状态下最多可同时接收5个客Lq接hQƈ与之建立通信关系。因此本E序的设计流E应当由服务器首先启动,然后在某一时刻启动客户机ƈ使其与服务器建立q接。服务器与客h开始都必须调用Windows Sockets API函数socket()建立一个套接字sockets,然后服务器方调用bind()套接字与一个本地网l地址捆扎在一P再调用listen()使套接字处于一U被动的准备接收状态,同时规定它的h队列长度。在此之后服务器可以通过调用accept()来接收客h的连接?br />
  相对于服务器Q客L的工作就昑־比较单了Q当客户端打开套接字之后,便可通过调用connect()和服务器建立q接。连接徏立之后,客户和服务器之间可以通过q接发送和接收资料。最后资料传送结束,双方调用closesocket()关闭套接字来l束q次通讯。整个通讯q程的具体流E框囑֏大致用下面的程图来表示Q?br />

        面向q接的流式套接字~程程C意?

三?软g设计要点以及异步通讯的实?br />
  Ҏ前面设计的程序流E,可将E序划分Z部分Q服务器端和客户端。而且整个实现q程可以大致用以下几个非常关键的Windows Sockets API函数其惯穿下来Q?br />
  服务器方Q?br />
socket()->bind()->listen->accept()->recv()/send()->closesocket()

  客户机方Q?br />
socket()->connect()->send()/recv()->closesocket()

  有鉴于以上几个函数在整个|络~程中的重要性,有必要结合程序实例对其做较深入的剖析。服务器端应用程序在使用套接字之前,首先必须拥有一个SocketQ系l调用socket()函数向应用程序提供创建套接字的手Dc该套接字实际上是在计算Z提供了一个通信埠,可以通过q个埠与M一个具有套接字接口的计机通信。应用程序在|络上传输、接收的信息都通过q个套接字接口来实现的。在应用开发中如同使用文g句柄一P可以对套接字句柄q行d操作Q?br />
sock=socket(AF_INET,SOCK_STREAM,0);

  函数的第一个参数用于指定地址族,在Windows下仅支持AF_INET(TCP/IP地址)Q第二个参数用于描述套接字的cdQ对于流式套接字提供有SOCK_STREAMQ最后一个参数指定套接字使用的协议,一般ؓ0。该函数的返回g存了新套接字的句柄,在程序退出前可以?closesocket(sock);函数来将光放。服务器方一旦获取了一个新的套接字后应通过bind()该套接字与本机上的一个端口相兌Q?br />
sockin.sin_family=AF_INET;
sockin.sin_addr.s_addr=0;
sockin.sin_port=htons(USERPORT);
bind(sock,(LPSOCKADDR)&sockin,sizeof(sockin)));

  该函数的W二个参数是一个指向包含有本机IP地址和端口信息的sockaddr_inl构cd的指针,其成员描qC本地端口号和本地L地址Q经qbind()服务器q程在网l上标识出来。需要注意的是由?024以内的埠号都是保留的埠号因此如无特别需要一般不能将sockin.sin_port的埠可|ؓ1024以内的倹{然后调用listen()函数开始侦听,再通过accept()调用{待接收q接以完成连接的建立Q?br />
//q接h队列长度?Q即只允许有一个请?若有多个h,
//则出现错误,l出错误代码WSAECONNREFUSED?br />listen(sock,1);
//开启线E避免主E序的阻?br />AfxBeginThread(Server,NULL);
…?br />UINT Server(LPVOID lpVoid)
{
…?br />int nLen=sizeof(SOCKADDR);
pView->newskt=accept(pView->sock,(LPSOCKADDR)& pView->sockin,(LPINT)& nLen);
…?
WSAAsyncSelect(pView->newskt,pView->m_hWnd,WM_SOCKET_MSG,FD_READ|FD_CLOSE);
return 1;
}

  q里之所以把accept()攑ֈ一个线E中L因ؓ在执行到该函数时如没有客戯接服务器的请求到来,服务器就会停在accept语句上等待连接请求的到来Q这势必会引L序的dQ虽然也可以通过讄套接字ؓ非阻塞方式在没有客L待时可以使acceptQ)函数调用立即q回Q但q种轮询套接字的方式会CPU处于忙等待方式,从而降低程序的q行效率大大费pȝ资源。考虑到这U情况,套接字讄为阻塞工作方式,qؓ其单独开辟一个子U程Q将光塞控制在子线E范围内而不会造成整个应用E序的阻塞。对于网l事件的响应昄要采取异步选择机制Q只有采取这U方式才可以在由|络Ҏ所引v的不可预知的|络事g发生时能马上在进E中做出及时的响应处理,而在没有|络事g到达时则可以处理其他事gQ这U效率是很高的,而且完全W合Windows所标榜的消息触发原则。前面那D代码中的WSAAsyncSelect()函数便是实现|络事g异步选择的核心函数?br />
  通过W四个参数注册应用程序感兴取的网l事Ӟ在这里通过FD_READ|FD_CLOSE指定了网l读和网l断开两种事gQ当q种事g发生时变会发出由W三个参数指定的自定义消息WM_SOCKET_MSGQ接收该消息的窗口通过W二个参数指定其句柄。在消息处理函数中可以通过Ҏ息参C字节q行判断而区别出发生的是何种|络事gQ?br />
void CNetServerView::OnSocket(WPARAM wParam,LPARAM lParam)
{
int iReadLen=0;
int message=lParam & 0x0000FFFF;
switch(message)
{
case FD_READ://M件发生。此时有字符到达Q需要进行接收处?br />char cDataBuffer[MTU*10];
//通过套接字接收信?br />iReadLen = recv(newskt,cDataBuffer,MTU*10,0);
//信息保存到文g
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://|络断开事g发生。此时客h关闭或退出?br />…?/q行相应的处?br />break;
default:
break;
}
}

  在这里需要实现对自定义消息WM_SOCKET_MSG的响应,需要在头文件和实现文g中分别添加其消息映射关系Q?br />
  头文Ӟ

//{{AFX_MSG(CNetServerView)
//}}AFX_MSG
void OnSocket(WPARAM wParam,LPARAM lParam);
DECLARE_MESSAGE_MAP()

  实现文gQ?br />
BEGIN_MESSAGE_MAP(CNetServerView, CView)
//{{AFX_MSG_MAP(CNetServerView)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SOCKET_MSG,OnSocket)
END_MESSAGE_MAP()

  在进行异步选择使用WSAAsyncSelect()函数Ӟ有以下几炚w要引L别的注意Q?br />
  1Q?q箋使用两次WSAAsyncSelect()函数Ӟ只有W二ơ设|的事g有效Q如Q?br />
WSAAsyncSelect(s,hwnd,wMsg1,FD_READ);
WSAAsyncSelect(s,hwnd,wMsg2,FD_CLOSE);

  q样只有当FD_CLOSE事g发生时才会发送wMsg2消息?br />
  2Q可以在讄q异步选择后通过再次调用WSAAsyncSelect(s,hwnd,0,0);的Ş式取消在套接字上所讄的异步事件?br />
  3QWindows Sockets DLL在一个网l事件发生后Q通常只会l相应的应用E序发送一个消息,而不能发送多个消息。但通过使用一些函数隐式地允许重发此事件的消息Q这样就可能再次接收到相应的消息?br />
  4Q在调用qclosesocket()函数关闭套接字之后不会再发生FD_CLOSE事g?br />
  以上基本完成了服务器方的E序设计Q下面对于客L的实现则要简单多了,在用socket()创徏完套接字之后只需通过调用connect()完成同服务器的连接即可,剩下的工作同服务器完全一P用send()/recv()发?接收收据Q用closesocket()关闭套接字:

sockin.sin_family=AF_INET; //地址?br />sockin.sin_addr.S_un.S_addr=IPaddr; //指定服务器的IP地址
sockin.sin_port=m_Port; //指定q接的端口号
int nConnect=connect(sock,(LPSOCKADDR)&sockin,sizeof(sockin));

  本文采取的是可靠的面向连接的式套接字。在数据发送上有write()、writev()和send(){三个函数可供选择Q其中前两种分别用于~冲发送和集中发送,而send()则ؓ可控~冲发送,q且q可以指定传输控制标志ؓMSG_OOBq行带外数据的发送或是ؓMSG_DONTROUTEd控制选项。在信宿地址的网l号部分指定数据发送需要经q的|络接口Q其可以不l过本地d机制直接发送出厅R这也是其同write()函数的真正区别所在。由于接收数据系l调用和发送数据系l调用是一一对应的,因此对于数据的接Ӟ在此不再赘述Q相应的三个接收函数分别为:read()、readv()和recv()。由于后者功能上的全面,本文在实C选择了send()-recv()函数对,在具体编E中应当视具体情늚不同灉|选择适当的发?接收函数寏V?br />
  结Q?/b>TCP/IP协议是目前各|络操作pȝ主要的通讯协议Q也?Internet的通讯协议Q本文通过Windows Sockets API实现了对ZTCP/IP协议的面向连接的式套接字网l通讯E序的设计,q过异步通讯和多U程{手D|高了E序的运行效率,避免了阻塞的发生?br />


独孤九剑 2007-01-31 11:32 发表评论
]]>
TCP/IP Winsock~程要点http://www.shnenglu.com/eday/archive/2007/01/31/18203.html独孤九剑独孤九剑Wed, 31 Jan 2007 02:53:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18203.html
  1、快速通信

  Winsock的Nagle法降低小数据报的发送速度Q而系l默认是使用Nagle法,使用

int setsockopt(

SOCKET s,

int level,

int optname,

const char FAR *optval,

int optlen

);函数关闭?

  例子Q?

SOCKET sConnect;

sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

int bNodelay = 1;

int err;

err = setsockopt(

sConnect,

IPPROTO_TCP,

TCP_NODELAY,

(char *)&bNodelay,

sizoeof(bNodelay));//不采用g时算?

if (err != NO_ERROR)

TRACE ("setsockopt failed for some reason\n");;

  2、SOCKET的SegMentSize和收发缓?

  TCPSegMentSize是发送接受时单个数据报的最大长度,pȝ默认?460Q收发缓冲大ؓ8192?

  在SOCK_STREAM方式下,如果单次发送数据超q?460Q系l将分成多个数据报传送,在对Ҏ受到的将是一个数据流Q应用程序需要增加断帧的判断。当然可以采用修Ҏ册表的方式改?460的大,但MicrcoSoft认ؓ1460是最x率的参数Q不修改?

  在工控系l中Q徏议关闭Nagle法Q每ơ发送数据小?460个字节(推荐1400Q,q样每次发送的是一个完整的数据报,减少ҎҎ据流的断帧处理?

  3、同步方式中减少断网时connect函数的阻塞时?

  同步方式中的断网时connect的阻塞时间ؓ20U左叻I可采用gethostbyaddr事先判断到服务主机的路径是否是通的Q或者先ping一下对方主机的IP地址?

  A、采用gethostbyaddrd旉不管成功与否?U左叟?

  例子Q?

LONG lPort=3024;

struct sockaddr_in ServerHostAddr;//服务L地址

ServerHostAddr.sin_family=AF_INET;

ServerHostAddr.sin_port=::htons(u_short(lPort));

ServerHostAddr.sin_addr.s_addr=::inet_addr("192.168.1.3");

HOSTENT* pResult=gethostbyaddr((const char *) &

(ServerHostAddr.sin_addr.s_addr),4,AF_INET);

if(NULL==pResult)

{

int nErrorCode=WSAGetLastError();

TRACE("gethostbyaddr errorcode=%d",nErrorCode);

}

else

{

TRACE("gethostbyaddr %s\n",pResult->h_name);;

}

  B、采用PING方式旉U?U左?

  暂略

4、同步方式中解决recvQsendd问题

  采用select函数解决Q在收发前先查读写可用状态?

  A、读

  例子Q?

TIMEVAL tv01 = {0, 1};//1ms钟gq?实际?-10毫秒

int nSelectRet;

int nErrorCode;

FD_SET fdr = {1, sConnect};

nSelectRet=::select(0, &fdr, NULL, NULL, &tv01);//查可ȝ?

if(SOCKET_ERROR==nSelectRet)

{

nErrorCode=WSAGetLastError();

TRACE("select read status errorcode=%d",nErrorCode);

::closesocket(sConnect);

goto 重新q接Q客hQ,或服务线E退出(服务方);

}

if(nSelectRet==0)//时发生Q无可读数据

{

l箋查读状态或向对方主动发?

}

else

{

L?

}

  B、写

TIMEVAL tv01 = {0, 1};//1ms钟gq?实际?-10毫秒

int nSelectRet;

int nErrorCode;

FD_SET fdw = {1, sConnect};

nSelectRet=::select(0, NULL, NULL,&fdw, &tv01);//查可写状?

if(SOCKET_ERROR==nSelectRet)

{

nErrorCode=WSAGetLastError();

TRACE("select write status errorcode=%d",nErrorCode);

::closesocket(sConnect);

//goto 重新q接Q客hQ,或服务线E退出(服务方);

}

if(nSelectRet==0)//时发生Q缓冲满或网l忙

{

//l箋查写状态或查读状?

}

else

{

//发?

}

  5、改变TCP收发~冲区大?

  pȝ默认?192Q利用如下方式可改变?

SOCKET sConnect;

sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

int nrcvbuf=1024*20;

int err=setsockopt(

sConnect,

SOL_SOCKET,

SO_SNDBUF,//写缓Ԍȝ冲ؓSO_RCVBUF

(char *)&nrcvbuf,

sizeof(nrcvbuf));

if (err != NO_ERROR)

{

TRACE("setsockopt Error!\n");

}

在设|缓冲时Q检查是否真正设|成功用

int getsockopt(

SOCKET s,

int level,

int optname,

char FAR *optval,

int FAR *optlen

);

  6、服务方同一端口多IP地址的bind和listen

  在可靠性要求高的应用中Q要求用双|和多网l通道Q再服务方很Ҏ实现Q用如下方式可徏立客户对本机所有IP地址在端?024下的h服务?

SOCKET hServerSocket_DS=INVALID_SOCKET;

struct sockaddr_in HostAddr_DS;//服务器主机地址

LONG lPort=3024;

HostAddr_DS.sin_family=AF_INET;

HostAddr_DS.sin_port=::htons(u_short(lPort));

HostAddr_DS.sin_addr.s_addr=htonl(INADDR_ANY);

hServerSocket_DS=::socket( AF_INET, SOCK_STREAM,IPPROTO_TCP);

if(hServerSocket_DS==INVALID_SOCKET)

{

AfxMessageBox("建立数据服务器SOCKET p|!");

return FALSE;

}

if(SOCKET_ERROR==::bind(hServerSocket_DS,(struct

sockaddr *)(&(HostAddr_DS)),sizeof(SOCKADDR)))

{

int nErrorCode=WSAGetLastError ();

TRACE("bind error=%d\n",nErrorCode);

AfxMessageBox("Socket Bind 错误!");

return FALSE;

}

if(SOCKET_ERROR==::listen(hServerSocket_DS,10))//10个客?

{

AfxMessageBox("Socket listen 错误!");

return FALSE;

}

AfxBeginThread(ServerThreadProc,NULL,THREAD_PRIORITY_NORMAL);

  在客h要复杂一些,q接断后Q重联不成功则应换下一个IP地址q接。也可采用同时连接好后备用的方式?

  7、用TCP/IP Winsock实现变种Client/Server

  传统的Client/Server为客户问、服务答Q收发是成对出现的。而变U的Client/Server是指在连接时有客户和服务之分Q徏立好通信q接后,不再有严格的客户和服务之分,M斚w可主动发送,需要或不需要回{看应用而言Q这U方式在工控行业很有用,比如RTDB作ؓI/O Server的客P但I/O Server也可d向RTDB发送开关状态变位、随即事件等信息。在很大E度上减了|络通信负荷、提高了效率?

  采用1-6的TCP/IP~程要点Q在Client和Server方均已接收优先,适当控制时序p实现?br />

独孤九剑 2007-01-31 10:53 发表评论
]]>
ZVisual C++的Winsock API研究http://www.shnenglu.com/eday/archive/2007/01/31/18202.html独孤九剑独孤九剑Wed, 31 Jan 2007 02:51:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18202.html
  微Y为VC定义了WinsockcdCAsyncSocketcdz于CAsyncSocket 的CSocketc,它们单易用,读者朋友当然可以用这些类来实现自q|络E序Q但是ؓ了更好的了解Winsock API~程技术,我们q里探讨怎样使用底层的API函数实现单的 Winsock |络应用E式设计Q分别说明如何在Server端和Client端操作SocketQ实现基于TCP/IP的数据传送,最后给出相关的源代码?br />
  在VC中进行WINSOCK的API~程开发的时候,需要在目中用下面三个文Ӟ否则会出现编译错误?br />
  1QWINSOCK.H: q是WINSOCK API的头文gQ需要包含在目中?br />
  2QWSOCK32.LIB: WINSOCK APIq接库文件。在使用中,一定要把它作ؓ目的非~省的连接库包含到项目文件中厅R?

  3QWINSOCK.DLL: WINSOCK的动态连接库Q位于WINDOWS的安装目录下?br />
  一、服务器端操?socketQ套接字Q?/b>

  1)在初始化阶段调用WSAStartup()

  此函数在应用E序中初始化Windows Sockets DLL Q只有此函数调用成功后,应用E序才可以再调用其他Windows Sockets DLL中的API函数。在E式中调用该函数的Ş式如下:WSAStartup((WORD)((1<<8|1)Q(LPWSADATAQ?amp;WSAData)Q其?1<<8|1)表示我们用的是WinSocket1.1版本QWSAata用来存储pȝ传回的关于WinSocket的资料?br />
  2)建立Socket

  初始化WinSock的动态连接库后,需要在服务器端建立一个监听的SocketQؓ此可以调用Socket()函数用来建立q个监听的SocketQƈ定义此Socket所使用的通信协议。此函数调用成功q回Socket对象Q失败则q回INVALID_SOCKET(调用WSAGetLastError()可得知原因,所有WinSocket 的函数都可以使用q个函数来获取失败的原因)?br />
SOCKET PASCAL FAR socket( int af, int type, int protocol )
参数: af:目前只提?PF_INET(AF_INET)Q?br />typeQSocket 的类?(SOCK_STREAM、SOCK_DGRAM)Q?br />protocolQ通讯协定(如果使用者不指定则设?)Q?br />
如果要徏立的是遵从TCP/IP协议的socketQ第二个参数type应ؓSOCK_STREAMQ如为UDPQ数据报Q的socketQ应为SOCK_DGRAM?br />
  3)l定端口

  接下来要为服务器端定义的q个监听的Socket指定一个地址及端口(PortQ,q样客户端才知道待会要连接哪一个地址的哪个端口,为此我们要调用bind()函数Q该函数调用成功q回0Q否则返回SOCKET_ERROR?br />int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name,int namelen );

?敎ͼ sQSocket对象名;
nameQSocket的地址|q个地址必须是执行这个程式所在机器的IP地址Q?br />namelenQname的长度;

  如果使用者不在意地址或端口的|那么可以讑֮地址为INADDR_ANYQ及Port?QWindows Sockets 会自动将其设定适当之地址及Port (1024 ?5000之间的?。此后可以调用getsockname()函数来获知其被设定的倹{?br />
  4Q监?br />
  当服务器端的Socket对象l定完成之后,服务器端必须建立一个监听的队列来接收客L的连接请求。listen()函数使服务器端的Socket q入监听状态,q设定可以徏立的最大连接数(目前最大值限制ؓ 5, 最gؓ1)。该函数调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR listen( SOCKET s, int backlog );
?敎ͼ sQ需要徏立监听的SocketQ?br />backlogQ最大连接个敎ͼ

  服务器端的Socket调用完listenQ)后,如果此时客户端调用connectQ)函数提出q接甌的话QServer 端必d调用accept() 函数Q这h务器端和客户端才正式完成通信E序的连接动作。ؓ了知道什么时候客L提出q接要求Q从而服务器端的Socket在恰当的时候调用accept()函数完成q接的徏立,我们p使用WSAAsyncSelectQ)函数Q让pȝd来通知我们有客L提出q接h了。该函数调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd,unsigned int wMsg, long lEvent );
参数Q?sQSocket 对象Q?br />hWnd Q接收消息的H口句柄Q?br />wMsgQ传l窗口的消息Q?br />lEventQ被注册的网l事Ӟ也即是应用程序向H口发送消息的|\事gQ该gؓ下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的组合,各个值的具体含意为FD_READQ希望在套接字S收到数据时收到消息;FD_WRITEQ希望在套接字S上可以发送数据时收到消息QFD_ACCEPTQ希望在套接字S上收到连接请求时收到消息QFD_CONNECTQ希望在套接字S上连接成功时收到消息QFD_CLOSEQ希望在套接字S上连接关闭时收到消息QFD_OOBQ希望在套接字S上收到带外数据时收到消息?

  具体应用ӞwMsg应是在应用程序中定义的消息名Uͼ而消息结构中的lParam则ؓ以上各种|络事g名称。所以,可以在窗口处理自定义消息函数中用以下结构来响应Socket的不同事Ӟ  

switch(lParam) 
  {case FD_READ:
    …  
  break;
case FD_WRITE?br />    ?br />  break;
    ?br />}

  5Q服务器端接受客L的连接请?br />
  当Client提出q接hӞServer 端hwnd视窗会收到Winsock Stack送来我们自定义的一个消息,q时Q我们可以分析lParamQ然后调用相关的函数来处理此事g。ؓ了服务器端接受客户端的q接hQ就要用accept() 函数Q该函数新徏一Socket与客L的Socket盔R,原先监听之Socketl箋q入监听状态,{待他h的连接要求。该函数调用成功q回一个新产生的Socket对象Q否则返回INVALID_SOCKET?br />
SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen );
参数QsQSocket的识别码Q?br />addrQ存放来q接的客L的地址Q?br />addrlenQaddr的长?/td>

  6Q结?socket q接

  l束服务器和客户端的通信q接是很单的Q这一q程可以由服务器或客h的Q一端启动,只要调用closesocket()可以了Q而要关闭Server端监听状态的socketQ同样也是利用此函数。另外,与程序启动时调用WSAStartup()憨数相对应,E式l束前,需要调?WSACleanup() 来通知Winsock Stack释放Socket所占用的资源。这两个函数都是调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR closesocket( SOCKET s );
?敎ͼsQSocket 的识别码Q?br />int PASCAL FAR WSACleanup( void );
?敎ͼ ?/td>

二、客LSocket的操?br />
  1Q徏立客L的Socket

  客户端应用程序首先也是调用WSAStartup() 函数来与Winsock的动态连接库建立关系Q然后同栯用socket() 来徏立一个TCP或UDP socketQ相同协定的 sockets 才能盔R,TCP ?TCPQUDP ?UDPQ。与服务器端的socket 不同的是Q客L的socket 可以调用 bind() 函数Q由自己来指定IP地址及portLQ但是也可以不调?bind()Q而由 Winsock来自动设定IP地址及portL?br />
  2Q提接申?br />
  客户端的Socket使用connect()函数来提Z服务器端的Socket建立q接的申P函数调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR *name, int namelen );
?敎ͼsQSocket 的识别码Q?br />nameQSocket惌q接的对方地址Q?br />namelenQname的长?/td>

  三、数据的传?/b>

  虽然ZTCP/IPq接协议Q流套接字)的服务是设计客户?服务器应用程序时的主标准,但有些服务也是可以通过无连接协议(数据报套接字Q提供的。先介绍一下TCP socket 与UDP socket 在传送数据时的特性:Stream (TCP) Socket 提供双向、可靠、有ơ序、不重复的资料传送。Datagram (UDP) Socket 虽然提供双向的通信Q但没有可靠、有ơ序、不重复的保证,所以UDP传送数据可能会收到无次序、重复的资料Q甚臌料在传输q程中出现遗漏。由于UDP Socket 在传送资料时Qƈ不保证资料能完整地送达ҎQ所以绝大多数应用程序都是采用TCP处理SocketQ以保证资料的正性。一般情况下TCP Socket 的数据发送和接收是调用send() 及recv() q两个函数来达成Q?UDP Socket则是用sendto() 及recvfrom() q两个函敎ͼq两个函数调用成功发挥发送或接收的资料的长度Q否则返回SOCKET_ERROR?br />
int PASCAL FAR send( SOCKET s, const char FAR *buf,int len, int flags );
参数QsQSocket 的识别码
bufQ存放要传送的资料的暂存区
len bufQ的长度
flagsQ此函数被调用的方式

  对于Datagram Socket而言Q若?datagram 的大超q限Ӟ则将不会送出M资料Qƈ会传回错误倹{对Stream Socket aQBlocking 模式下,若是传送系l内的储存空间不够存放这些要传送的资料Qsend()会被block住,直到资料送完为止Q如果该Socket被设定ؓ Non-Blocking 模式Q那么将视目前的output bufferI间有多,送出多少资料Qƈ不会?block 住。flags 的值可设ؓ 0 ?MSG_DONTROUTE?MSG_OOB 的组合?br />
int PASCAL FAR recv( SOCKET s, char FAR *buf, int len, int flags );
参数QsQSocket 的识别码
bufQ存放接收到的资料的暂存?br />len bufQ的长度
flagsQ此函数被调用的方式

  对Stream Socket aQ我们可以接收到目前input buffer内有效的资料Q但其数量不过len的大?br />
  四、自定义的CMySocketcȝ实现代码Q?/font>

  Ҏ上面的知识,我自定义了一个简单的CMySocketc,下面是我定义的该cȝ部分实现代码Q?br />
//////////////////////////////////////
CMySocket::CMySocket() : file://cȝ构造函?br />{
 WSADATA wsaD;
 memset( m_LastError, 0, ERR_MAXLENGTH );
 // m_LastError是类内字W串变量,初始化用来存放最后错误说明的字符Ԍ
 // 初始化类内sockaddr_inl构变量Q前者存攑֮L地址Q后者对应于服务器端地址;
 memset( &m_sockaddr, 0, sizeof( m_sockaddr ) );
 memset( &m_rsockaddr, 0, sizeof( m_rsockaddr ) );
 int result = WSAStartup((WORD)((1<<8|1)Q?&wsaD);//初始化WinSocket动态连接库;
 if( result != 0 ) // 初始化失败;
 { set_LastError( "WSAStartup failed!", WSAGetLastError() );
  return;
 }
}

//////////////////////////////
CMySocket::~CMySocket() { WSACleanup(); }//cȝ析构函数Q?br />////////////////////////////////////////////////////
int CMySocket::Create( void )
 {// m_hSocket是类内Socket对象Q创Z个基于TCP/IP的Socket变量QƈDl该变量Q?br />  if ( (m_hSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP )) == INVALID_SOCKET )
  {
   set_LastError( "socket() failed", WSAGetLastError() );
   return ERR_WSAERROR;
  }
  return ERR_SUCCESS;
 }
///////////////////////////////////////////////
int CMySocket::Close( void )//关闭Socket对象Q?br />{
 if ( closesocket( m_hSocket ) == SOCKET_ERROR )
 {
  set_LastError( "closesocket() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 file://重置sockaddr_in l构变量Q?br /> memset( &m_sockaddr, 0, sizeof( sockaddr_in ) );
 memset( &m_rsockaddr, 0, sizeof( sockaddr_in ) );
 return ERR_SUCCESS;
}
/////////////////////////////////////////
int CMySocket::Connect( char* strRemote, unsigned int iPort )//定义q接函数Q?br />{
 if( strlen( strRemote ) == 0 || iPort == 0 )
  return ERR_BADPARAM;
 hostent *hostEnt = NULL;
 long lIPAddress = 0;
 hostEnt = gethostbyname( strRemote );//Ҏ计算机名得到该计机的相兛_容;
 if( hostEnt != NULL )
 {
  lIPAddress = ((in_addr*)hostEnt->h_addr)->s_addr;
  m_sockaddr.sin_addr.s_addr = lIPAddress;
 }
 else
 {
  m_sockaddr.sin_addr.s_addr = inet_addr( strRemote );
 }
 m_sockaddr.sin_family = AF_INET;
 m_sockaddr.sin_port = htons( iPort );
 if( connect( m_hSocket, (SOCKADDR*)&m_sockaddr, sizeof( m_sockaddr ) ) == SOCKET_ERROR )
 {
  set_LastError( "connect() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
///////////////////////////////////////////////////////
int CMySocket::Bind( char* strIP, unsigned int iPort )//l定函数Q?br />{
 if( strlen( strIP ) == 0 || iPort == 0 )
  return ERR_BADPARAM;
 memset( &m_sockaddr,0, sizeof( m_sockaddr ) );
 m_sockaddr.sin_family = AF_INET;
 m_sockaddr.sin_addr.s_addr = inet_addr( strIP );
 m_sockaddr.sin_port = htons( iPort );
 if ( bind( m_hSocket, (SOCKADDR*)&m_sockaddr, sizeof( m_sockaddr ) ) == SOCKET_ERROR )
 {
  set_LastError( "bind() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
//////////////////////////////////////////
int CMySocket::Accept( SOCKET s )//建立q接函数QS为监听Socket对象名;
{
 int Len = sizeof( m_rsockaddr );
 memset( &m_rsockaddr, 0, sizeof( m_rsockaddr ) );
 if( ( m_hSocket = accept( s, (SOCKADDR*)&m_rsockaddr, &Len ) ) == INVALID_SOCKET )
 {
  set_LastError( "accept() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
/////////////////////////////////////////////////////
int CMySocket::asyncSelect( HWND hWnd, unsigned int wMsg, long lEvent )
file://事g选择函数Q?br />{
 if( !IsWindow( hWnd ) || wMsg == 0 || lEvent == 0 )
  return ERR_BADPARAM;
 if( WSAAsyncSelect( m_hSocket, hWnd, wMsg, lEvent ) == SOCKET_ERROR )
 {
  set_LastError( "WSAAsyncSelect() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
////////////////////////////////////////////////////
int CMySocket::Listen( int iQueuedConnections )//监听函数Q?br />{
 if( iQueuedConnections == 0 )
  return ERR_BADPARAM;
 if( listen( m_hSocket, iQueuedConnections ) == SOCKET_ERROR )
 {
  set_LastError( "listen() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
////////////////////////////////////////////////////
int CMySocket::Send( char* strData, int iLen )//数据发送函敎ͼ
{
 if( strData == NULL || iLen == 0 )
  return ERR_BADPARAM;
 if( send( m_hSocket, strData, iLen, 0 ) == SOCKET_ERROR )
 {
  set_LastError( "send() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
/////////////////////////////////////////////////////
int CMySocket::Receive( char* strData, int iLen )//数据接收函数Q?br />{
 if( strData == NULL )
  return ERR_BADPARAM;
 int len = 0;
 int ret = 0;
 ret = recv( m_hSocket, strData, iLen, 0 );
 if ( ret == SOCKET_ERROR )
 {
  set_LastError( "recv() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ret;
}
void CMySocket::set_LastError( char* newError, int errNum )
file://WinSock API操作错误字符串设|函敎ͼ
{
 memset( m_LastError, 0, ERR_MAXLENGTH );
 memcpy( m_LastError, newError, strlen( newError ) );
 m_LastError[strlen(newError)+1] = '\0';
}

  有了上述cȝ定义Q就可以在网l程序的服务器和客户端分别定义CMySocket对象Q徏立连接,传送数据了。例如,Z在服务器和客L发送数据,需要在服务器端定义两个CMySocket对象ServerSocket1和ServerSocket2Q分别用于监听和q接Q客L定义一个CMySocket对象ClientSocketQ用于发送或接收数据Q如果徏立的q接数大于一Q可以在服务器端再定义CMySocket对象Q但要注意连接数不要大于五?br />
  ׃Socket API函数q有许多Q如获取q端服务器、本地客h的IP地址、主机名{等Q读者可以再此基上对CMySocket补充完善Q实现更多的功能?br />

独孤九剑 2007-01-31 10:51 发表评论
]]>
Windows Socket1.1 E序设计http://www.shnenglu.com/eday/archive/2007/01/30/18190.html独孤九剑独孤九剑Tue, 30 Jan 2007 14:07:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/30/18190.html一、简?br />
  Windows Sockets 是从 Berkeley Sockets 扩展而来的,其在l承 Berkeley Sockets 的基上,又进行了新的扩充。这些扩充主要是提供了一些异步函敎ͼq增加了W合WINDOWS消息驱动Ҏ的|络事g异步选择机制?br />
  Windows Sockets׃部分l成Q开发组件和q行lg?br />
  开发组ӞWindows Sockets 实现文、应用程序接?API)引入库和一些头文g?br />
  q行lgQWindows Sockets 应用E序接口的动态链接库(WINSOCK.DLL)?

  二、主要扩充说?/b>

  1、异步选择机制Q?br />
  Windows Sockets 的异步选择函数提供了消息机制的|络事g选择Q当使用它登记网l事件发生时Q应用程序相应窗口函数将收到一个消息,消息中指CZ发生的网l事Ӟ以及与事件相关的一些信息?br />
  Windows Sockets 提供了一个异步选择函数 WSAAsyncSelect()Q用它来注册应用E序感兴的|络事gQ当q些事g发生Ӟ应用E序相应的窗口函数将收到一个消息?br />
  函数l构如下Q?/p>
int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);

  参数说明Q?br />
   hWndQ窗口句?br />
   wMsgQ需要发送的消息

   lEventQ事Ӟ以下Z件的内容Q?br />

|含义Q?/td>
FD_READ期望在套接字上收到数据(卌准备好)时接到通知
FD_WRITE期望在套接字上可发送数据(卛_准备好)时接到通知
FD_OOB期望在套接字上有带外数据到达时接到通知
FD_ACCEPT期望在套接字上有外来q接时接到通知
FD_CONNECT期望在套接字q接建立完成时接到通知
FD_CLOSE期望在套接字关闭时接到通知

  例如Q我们要在套接字d备好或写准备好时接到通知Q语句如下:

rc=WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);

  如果我们需要注销对套接字|络事g的消息发送,只要?lEvent 讄?

  2、异步请求函?br />
  ?Berkeley Sockets 中请求服务是d的,WINDOWS SICKETS 除了支持q一cd数外Q还增加了相应的异步h函数(WSAAsyncGetXByY();)?

  3、阻塞处理方?br />
  Windows Sockets Z实现当一个应用程序的套接字调用处于阻塞时Q能够放弃CPU让其它应用程序运行,它在调用处于d时便q入一个叫“HOOK”的例程Q此例程负责接收和分配WINDOWS消息Q得其它应用程序仍然能够接收到自己的消息ƈ取得控制权?br />
  WINDOWS 是非抢先的多d环境Q即若一个程序不d攑ּ其控制权Q别的程序就不能执行。因此在设计Windows Sockets E序Ӟ管pȝ支持d操作Q但q是反对E序员用该操作。但׃ SUN 公司下的 Berkeley Sockets 的套接字默认操作是阻塞的QWINDOWS 作ؓUL?SOCKETS 也不可避免对q个操作支持?br />
  在Windows Sockets 实现中,对于不能立即完成的阻塞操作做如下处理QDLL初始化→循环操作。在循环中,它发送Q?WINDOWS 消息Qƈ查这?Windows Sockets 调用是否完成Q在必要Ӟ它可以放弃CPU让其它应用程序执行(当然使用线E的CPU׃会有q个ȝ了^_^Q。我们可以调?WSACancelBlockingCall() 函数取消此阻塞操作?br />
  ?Windows Sockets 中,有一个默认的d处理例程 BlockingHook() 单地获取q发?WINDOWS 消息。如果要对复杂程序进行处理,Windows Sockets 中还?WSASetBlockingHook() 提供用户安装自己的阻塞处理例E能力;与该函数相对应的则是 SWAUnhookBlockingHook()Q它用于删除先前安装的Q何阻塞处理例E,q新安装默认的处理例程。请注意Q设计自qd处理例程Ӟ除了函数 WSACancelBlockingHook() 之外Q它不能使用其它?Windows Sockets API 函数。在处理例程中调?WSACancelBlockingHook()函数取消处于阻塞的操作Q它结束阻塞@环?/p>

  4、出错处?br />
  Windows Sockets Z和以后多U程环境QWINDOWS/UNIXQ兼容,它提供了两个出错处理函数来获取和讄当前U程的最q错误号。(WSAGetLastEror()和WSASetLastError()Q?/p>

  5、启动与l止

  使用函数 WSAStartup() ?WSACleanup() 启动和终止套接字?br />
三、Windows Sockets|络E序设计核心

  我们l于可以开始真正的 Windows Sockets |络E序设计了。不q我们还是先看一看每?Windows Sockets |络E序都要涉及的内宏V让我们一步步慢慢走?/p>

  1、启动与l止

  在所?Windows Sockets 函数中,只有启动函数 WSAStartup() 和终止函?WSACleanup() 是必M用的?br />
  启动函数必须是第一个用的函数Q而且它允许指?Windows Sockets API 的版本,q获?SOCKETS的特定的一些技术细节。本l构如下Q?br />

int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

  其中 wVersionRequested 保证 SOCKETS 可正常运行的 DLL 版本Q如果不支持Q则q回错误信息?br />我们看一下下面这D代码,看一下如何进?WSAStartup() 的调?br />

WORD wVersionRequested;// 定义版本信息变量
WSADATA wsaData;//定义数据信息变量
int err;//定义错误号变?br />wVersionRequested = MAKEWORD(1,1);//l版本信息赋?br />err = WSAStartup(wVersionRequested, &wsaData);//l错误信息赋?br />if(err!=0)
{
return;//告诉用户找不到合适的版本
}
//认 Windows Sockets DLL 支持 1.1 版本
//DLL 版本可以高于 1.1
//pȝq回的版本号始终是最低要求的 1.1Q即应用E序与DLL 中可支持的最低版本号
if(LOBYTE(wsaData.wVersion)!= 1|| HIBYTE(wsaData.wVersion)!=1)
{
WSACleanup();//告诉用户找不到合适的版本
return;
}
//Windows Sockets DLL 被进E接受,可以q入下一步操?/td>

  关闭函数使用ӞM打开q已q接?SOCK_STREAM 套接字被复位Q但那些已由 closesocket() 函数关闭的但仍有未发送数据的套接字不受媄响,未发送的数据仍将被发送。程序运行时可能会多ơ调?WSAStartuo() 函数Q但必须保证每次调用时的 wVersionRequested 的值是相同的?/p>

  2、异步请求服?br />
  Windows Sockets 除支?Berkeley Sockets 中同步请求,q增加了了一cd步请求服务函?WSAAsyncGerXByY()。该函数是阻塞请求函数的异步版本。应用程序调用它Ӟ?Windows Sockets DLL 初始化这一操作q返回调用者,此函数返回一个异步句柄,用来标识q个操作。当l果存储在调用者提供的~冲区,q且发送一个消息到应用E序相应H口。常用结构如下:

HANDLE taskHnd;
char hostname="rs6000";
taskHnd = WSAAsyncBetHostByName(hWnd,wMsg,hostname,buf,buflen);

  需要注意的是,׃ Windows 的内存对像可以设|ؓ可移动和可丢弃,因此在操作内存对象是Q必M?WIindows Sockets DLL 对象是可用的?

  3、异步数据传?br />
  使用 send() ?sendto() 函数来发送数据,使用 recv() 或recvfrom() 来接收数据。Windows Sockets 不鼓q户用阻塞方式传输数据,因ؓ那样可能会阻塞整?Windows 环境。下面我们看一个异步数据传输实例:

  假设套接?s 在连接徏立后Q已l用了函数 WSAAsyncSelect() 在其上注册了|络事g FD_READ ?FD_WRITEQƈ?wMsg gؓ UM_SOCKQ那么我们可以在 Windows 消息循环中增加如下的分支语句Q?/p>

case UM_SOCK:
switch(lParam)
{
case FD_READ:
len = recv(wParam,lpBuffer,length,0);
break;
case FD_WRITE:
while(send(wParam,lpBuffer,len,0)!=SOCKET_ERROR)
break;
}
break;

  4、出错处?br />
  Windows 提供了一个函数来获取最q的错误?WSAGetLastError()Q推荐的~写方式如下Q?

len = send (s,lpBuffer,len,0);
of((len==SOCKET_ERROR)&&(WSAGetLastError()==WSAWOULDBLOCK)){...}


独孤九剑 2007-01-30 22:07 发表评论
]]>Winsocket~程之套接字原理http://www.shnenglu.com/eday/archive/2007/01/30/18189.html独孤九剑独孤九剑Tue, 30 Jan 2007 14:04:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/30/18189.html一、客h/服务器模?br />
  在TCP/IP|络中两个进E间的相互作用的L模式是客h/服务器模?Client/Server model)。该模式的徏立基于以下两点:1、非对等作用Q?、通信完全是异步的。客h/服务器模式在操作q程中采取的是主动请C方式:

  首先服务器方要先启动QƈҎL提供相应服务Q(q程如下Q?br />
  1、打开一通信通道q告知本C机,它愿意在某一个公认地址上接收客戯求?br />
  2、等待客戯求到达该端口?br />
  3、接收到重复服务hQ处理该hq发送应{信受?br />
  4、返回第二步Q等待另一客户h

  5、关闭服务器?br />
  客户方:

  1、打开一通信通道Qƈq接到服务器所在主机的特定端口?br />
  2、向服务器发送服务请求报文,{待q接收应{;l箋提出h…?br />
  3、请求结束后关闭通信通道q终止?/p>

  二、基本套接字

  Z更好说明套接字编E原理,l出几个基本的套接字Q在以后的篇q中会给出更详细的用说明?br />
  1、创建套接字——socket()

  功能Q用前创徏一个新的套接字

  格式QSOCKET PASCAL FAR socket(int af,int type,int procotol);

  参数Qaf: 通信发生的区?br />
  type: 要徏立的套接字类?br />
  procotol: 使用的特定协?/p>

  2、指定本地地址——bind()

  功能Q将套接字地址与所创徏的套接字可pv来?br />
  格式Qint PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);

  参数Qs: 是由socket()调用q回的ƈ且未作连接的套接字描q符Q套接字P?br />
  其它Q没有错误,bind()q回0Q否则SOCKET_ERROR

  地址l构说明Q?br />
struct sockaddr_in
{
short sin_family;//AF_INET
u_short sin_port;//16位端口号Q网l字节顺?br />struct in_addr sin_addr;//32位IP地址Q网l字节顺?br />char sin_zero[8];//保留
}

  3、徏立套接字q接——connect()和accept()

  功能Q共同完成连接工?br />
  格式Qint PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);

  SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);

  参数Q同?/p>

  4、监听连接——listen()

  功能Q用于面向连接服务器Q表明它愿意接收q接?br />
  格式Qint PASCAL FAR listen(SOCKET s, int backlog);

  5、数据传输——send()与recv()

  功能Q数据的发送与接收

  格式Qint PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);

  int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);

  参数Qbuf:指向存有传输数据的缓冲区的指针?

  6、多路复用——select()

  功能Q用来检一个或多个套接字状态?br />
  格式Qint PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds,
fd_set FAR * exceptfds,const struct timeval FAR * timeout);

  参数Qreadfds:指向要做L的指针

     writefds:指向要做写检的指针

     exceptfds:指向要检是否出错的指针

     timeout:最大等待时?/p>

  7、关闭套接字——closesocket()

  功能Q关闭套接字s

  格式QBOOL PASCAL FAR closesocket(SOCKET s);

三、典型过E图

  2.1 面向q接的套接字的系l调用时序图



  2.2 无连接协议的套接字调用时序图



   2.3 面向q接的应用程序流E图




独孤九剑 2007-01-30 22:04 发表评论
]]>Winsocket~程之TCP/IP体系l构http://www.shnenglu.com/eday/archive/2007/01/30/18188.html独孤九剑独孤九剑Tue, 30 Jan 2007 13:59:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/30/18188.html一、TCP/IP 体系l构与特?/strong>

  1、TCP/IP体系l构

  TCP/IP协议实际上就是在物理|上的一l完整的|络协议。其中TCP是提供传输层服务Q而IP则是提供|络层服务。TCP/IP包括以下协议Q(l构如图1.1Q?/p>


(?.1)

  IPQ?|间协议(Internet Protocol) 负责L间数据的路由和网l上数据的存储。同时ؓICMPQTCPQ   UDP提供分组发送服务。用戯E通常不需要涉及这一层?br />
  ARPQ?地址解析协议(Address Resolution Protocol)
   此协议将|络地址映射到硬件地址?br />
  RARPQ?反向地址解析协议(Reverse Address Resolution Protocol)
   此协议将g地址映射到网l地址

  ICMPQ?|间报文控制协议(Internet Control Message Protocol)
   此协议处理信兛_L的差错和传送控制?br />
  TCPQ?传送控制协?Transmission Control Protocol)
   q是一U提供给用户q程的可靠的全双工字节流面向q接的协议。它要ؓ用户q程提供虚电路服务,qؓ数据可靠传输建立查。(注:大多数网l用L序用TCPQ?br />
  UDPQ?用户数据报协?User Datagram Protocol)
   q是提供l用戯E的无连接协议,用于传送数据而不执行正确性检查?br />
  FTPQ?文g传输协议(File Transfer Protocol)
   允许用户以文件操作的方式Q文件的增、删、改、查、传送等Q与另一L怺通信?br />
  SMTPQ?单邮件传送协?Simple Mail Transfer Protocol)
   SMTP协议为系l之间传送电子邮件?br />
  TELNETQ终端协?Telnet Terminal Procotol)
   允许用户以虚l端方式讉Kq程L

  HTTPQ?文本传输协?Hypertext Transfer Procotol)
  
  TFTP: 单文件传输协?Trivial File Transfer Protocol)

  2、TCP/IP特点

  TCP/IP协议的核心部分是传输层协?TCP、UDP)Q网l层协议(IP)和物理接口层Q这三层通常是在操作pȝ内核中实现。因此用户一般不涉及。编E时Q编E界面有两种形式Q一、是由内核心直接提供的系l调用;二、用以库函数方式提供的各种函数。前者ؓ核内实现Q后者ؓ核外实现。用h务要通过核外的应用程序才能实玎ͼ所以要使用套接?socket)来实现?br />
  ?.2是TCP/IP协议核心与应用程序关pd?br />

(?.2)

  二、专用术?/b>

  1、套接字

  套接字是|络的基本构件。它是可以被命名和寻址的通信端点Q用中的每一个套接字都有其类型和一个与之相q听q程。套接字存在通信区域Q通信区域又称地址)中。套接字只与同一区域中的套接字交换数据(跨区域时Q需要执行某和{换进E才能实玎ͼ。WINDOWS 中的套接字只支持一个域——网际域。套接字hcd?br />
  WINDOWS SOCKET 1.1 版本支持两种套接字:套接字(SOCK_STREAM)和数据报套接?SOCK_DGRAM)

  2、WINDOWS SOCKETS 实现

  一个WINDOWS SOCKETS 实现是指实现了WINDOWS SOCKETS规范所描述的全部功能的一套Y件。一般通过DLL文g来实?/p>

  3、阻塞处理例E?br />
  d处理例程(blocking hook,d钩子)是WINDOWS SOCKETS实现Z支持d套接字函数调用而提供的一U机制?/p>

  4、多址q播QmulticastQ多点传送或l播Q?br />
  是一U一对多的传输方式,传输发v者通过一ơ传输就信息传送到一l接收者,与单点传?br />(unicast)和广?Broadcast)相对应?/p>

独孤九剑 2007-01-30 21:59 发表评论
]]>
þۺϾþۺϾþ| þ99ƷþֻоƷ| 91Ʒ91þۺ| þþƷAV| þþþþþþþþ| þþžƷ| þ99ƷۺϹҳ| þþþ޾Ʒ| ɫ88þþþø߳ۺӰԺ | þavСݲ| ݺ88ۺϾþþþۺ| Ӱ7777þþƷˬ| ŷþþҹһĻ | Ʒŷ޺ձþ| Ʒþù鶹99վ | 2021ƷۺϾþ| 99þþƷҹһ| þþƷƵ| Ʒþþ21p| ݺɫݺɫۺϾþ| պƷþĻ| 뾫ƷþþӰ| avԾþþþa| 99þþþƷ| Ʒþþþþ| ŷսպ91ۺһþþ| þ| þۺϸþúݺ97ɫ| þ99Ʒþþþþþò| þ99Ʒþþþþþþþ| ɫۺϾþþƷĻҳ| þþƷĻ23ҳ| þþҹƷ| ۲˾þþƷٸAV| þŮˬŮˬ| ŷպƷþþѹۿ| ھƷžžþþƷ| Ůþþ| 鶹avþavʢav| 97Ʒ˾þô߽app | ҹ91þø|