??xml version="1.0" encoding="utf-8" standalone="yes"?>四虎国产精品免费久久久,亚洲国产精品一区二区久久,7777久久亚洲中文字幕http://www.shnenglu.com/zgysx/category/3385.html抒写快乐心情zh-cnFri, 15 Aug 2008 06:14:35 GMTFri, 15 Aug 2008 06:14:35 GMT60解析IPhttp://www.shnenglu.com/zgysx/archive/2008/08/14/58858.htmlwarriorwarriorThu, 14 Aug 2008 09:52:00 GMThttp://www.shnenglu.com/zgysx/archive/2008/08/14/58858.htmlhttp://www.shnenglu.com/zgysx/comments/58858.htmlhttp://www.shnenglu.com/zgysx/archive/2008/08/14/58858.html#Feedback0http://www.shnenglu.com/zgysx/comments/commentRss/58858.htmlhttp://www.shnenglu.com/zgysx/services/trackbacks/58858.html 1 //析取IP地址Q如果说是合法的IP则返回true,否则q回 false
 2 bool GetProxyIP( std::wstring& strIP )
 3 {
 4     strIP.clear();
 5     LPCWSTR pszIP = GetEditBoxText( IDC_DLG_LOGIN_SETTING_EDIT_ADDRESS );
 6     if( pszIP == NULL || !wcscmp( pszIP, TEXT("") ) )
 7         return false;
 8 
 9     std::wstring strTemp = pszIP;
10     std::wstring::size_type nPos1, nPos2;
11     nPos1 = nPos2 = 0;
12     int nIP[4= {0};
13     std::wstring::size_type i = 0;
14     for( ; (i < 4&& (nPos2 != std::wstring::npos ); ++i )
15     {
16         if( nPos2 == 0 )
17             nPos1 = nPos2;
18         else
19             nPos1 = nPos2 + 1;
20 
21         nPos2 = strTemp.find( TEXT('.'), nPos1);
22         std::wstring::size_type nCount = std::wstring::npos;
23         if( nPos2 != std::wstring::npos )
24             nCount = nPos2 - nPos1;
25 
26         std::wstring strSub = strTemp.substr(nPos1, nCount);
27         if( strSub == TEXT("") )
28             return false;
29 
30         int nValue = _wtoi( strSub.c_str() );
31         if( nValue < 0 || nValue > 255 )
32             return false;
33         nIP[i] = nValue;
34     }
35 
36     if( i < 4 )
37         return false;
38     wchar_t szIP[100= {0};
39     wsprintf( szIP, TEXT("%d.%d.%d.%d"), nIP[0], nIP[1], nIP[2], nIP[3] );
40     strIP = szIP;
41     return true;
42 }


warrior 2008-08-14 17:52 发表评论
]]>
[转]URL~码http://www.shnenglu.com/zgysx/archive/2007/01/04/17217.htmlwarriorwarriorThu, 04 Jan 2007 05:46:00 GMThttp://www.shnenglu.com/zgysx/archive/2007/01/04/17217.htmlhttp://www.shnenglu.com/zgysx/comments/17217.htmlhttp://www.shnenglu.com/zgysx/archive/2007/01/04/17217.html#Feedback3http://www.shnenglu.com/zgysx/comments/commentRss/17217.htmlhttp://www.shnenglu.com/zgysx/services/trackbacks/17217.html URL~码
作者: Chandrasekhar Vuppalapati
译Qeastvc

下蝲源代?/a>

本文的目的是设计一个完成URL~码的C++cR在我曾l的目中,我需要从VC++ 6.0应用E序中POST数据Q而这些数据需要进行URL~码。我在MSDN中查找能Ҏ提供的字W串生成URL~码的相关类或APIQ但我没有找刎ͼ因此我必设计一个自qURLEncode C++cR?/p>

URLEncoder.exe是一个用URLEncodecȝMFC对话框程序?/p>

如何处理

一些特D字W在Internet上传送是件棘手的事情, lURL~码Ҏ处理Q可以所有字W安全地从Internet传送?/p>

例如Q回车的ASCII值是13Q在发送FORM数据时候这p为是一行数据的l束?/p>

通常Q所有应用程序采用HTTP或HTTPS协议在客L和服务器端传送数据。服务器端从客户端接收数据有两种基本ҎQ?/p>

1、数据可以从HTTP头传送(COOKIES或作为FORM数据发送)
2、可以包含在URL中的查询部分

当数据包含在URLQ它必须遵@URL语法q行~码。在WEB服务器端Q数据自动解码。考虑一下下面的URLQ哪个数据是作ؓ查询参数?/p>

例如Qhttp://WebSite/ResourceName?Data=Data

WebSite是URL名称
ResourceName可以是ASP或Servlet名称
Data是需要发送的数据。如果MIMEcd是Content-Type: application/x-www-form-urlencodedQ则要求q行~码?/p>

RFC 1738

RFC 1738指明了统一资源定位(URLs)中的字符应该是US-ASCII字符集的子集。这是受HTML的限Ӟ另一斚wQ允许在文档中用所有ISO- 8859-1(ISO-Latin)字符集。这意味着在HTML FORM里POST的数据(或作为查询字串的一部分Q,所有HTML~码必须被编码?/p>

ISO-8859-1 (ISO-Latin)字符?/p>

在下表中Q包含了完整的ISO-8859-1 (ISO-Latin)字符集,表格提供了每个字W范_10q制Q,描述Q实际|十六q制|HTMLl果。某个范围中的字W是否安全?br />

Character range(decimal) Type Values Safe/Unsafe
0-31 ASCII Control Characters These characters are not printable Unsafe
32-47 Reserved Characters '' ''!?#$%&''()*+,-./ Unsafe
48-57 ASCII Characters and Numbers 0-9 Safe
58-64 Reserved Characters :;<=>?@ Unsafe
65-90 ASCII Characters A-Z Safe
91-96 Reserved Characters [\]^_` Unsafe
97-122 ASCII Characters a-z Safe
123-126 Reserved Characters {|}~ Unsafe
127 Control Characters '' '' Unsafe
128-255 Non-ASCII Characters '' '' Unsafe

所有不安全的ASCII字符都需要编码,例如Q范?32-47, 58-64, 91-96, 123-126)?br />下表描述了这些字Wؓ什么不安全?

Character Unsafe Reason Character Encode
"<" Delimiters around URLs in free text %3C
> Delimiters around URLs in free text %3E
. Delimits URLs in some systems %22
# It is used in the World Wide Web and in other systems to delimit a URL from a fragment/anchor identifier that might follow it. %23
{ Gateways and other transport agents are known to sometimes modify such characters %7B
} Gateways and other transport agents are known to sometimes modify such characters %7D
| Gateways and other transport agents are known to sometimes modify such characters %7C
\ Gateways and other transport agents are known to sometimes modify such characters %5C
^ Gateways and other transport agents are known to sometimes modify such characters %5E
~ Gateways and other transport agents are known to sometimes modify such characters %7E
[ Gateways and other transport agents are known to sometimes modify such characters %5B
] Gateways and other transport agents are known to sometimes modify such characters %5D
` Gateways and other transport agents are known to sometimes modify such characters %60
+ Indicates a space (spaces cannot be used in a URL) %20
/ Separates directories and subdirectories %2F
? Separates the actual URL and the parameters %3F
& Separator between parameters specified in the URL %26

如何实现

字符的URL~码是将字符转换??6q制q在前面加上''%''前缀。例如,US-ASCII字符集中I格?0q制
?2?6q制?0Q因此,URL~码?20?/p>

URLEncode: URLEncode是一个C++c,来实现字W串的URL~码。CURLEncodecd含如下函敎ͼ
isUnsafeString
decToHex
convert
URLEncode

URLEncode()函数完成~码q程QURLEncode查每个字W,看是否安全。如果不安全用%16q制D行{换ƈd
到原始字W串中?/p>

代码片断 :

class CURLEncode
{
private:
  static CString csUnsafeString;
  CString (char num, int radix);
  bool isUnsafe(char compareChar);
  CString convert(char val);

public:
  CURLEncode() { };
  virtual ~CURLEncode() { };
  CString (CString vData);
};

bool CURLEncode::isUnsafe(char compareChar)
{
  bool bcharfound = false;
  char tmpsafeChar;
  int m_strLen = 0;

  m_strLen = csUnsafeString.GetLength();
  for(int ichar_pos = 0; ichar_pos < m_strLen ;ichar_pos++)
  {
    tmpsafeChar = csUnsafeString.GetAt(ichar_pos);
    if(tmpsafeChar == compareChar)
    {
      bcharfound = true;
      break;
    }
  }
  int char_ascii_value = 0;
  //char_ascii_value = __toascii(compareChar);
  char_ascii_value = (int) compareChar;

  if(bcharfound == false &&  char_ascii_value > 32 &&
                             char_ascii_value < 123)
  {
    return false;
  }
  // found no unsafe chars, return false
  else
  {
    return true;
  }

  return true;
}

CString CURLEncode::decToHex(char num, int radix)
{
  int temp=0;
  CString csTmp;
  int num_char;

num_char = (int) num;
  if (num_char < 0)
    num_char = 256 + num_char;

  while (num_char >= radix)
    {
    temp = num_char % radix;
    num_char = (int)floor(num_char / radix);
    csTmp = hexVals[temp];
    }

  csTmp += hexVals[num_char];

  if(csTmp.GetLength() < 2)
  {
    csTmp += ''0'';
  }

  CString strdecToHex(csTmp);
  // Reverse the String
  strdecToHex.MakeReverse();

  return strdecToHex;
}

CString CURLEncode::convert(char val)
{
  CString csRet;
  csRet += "%";
  csRet += decToHex(val, 16);
  return  csRet;
}

参考:

URL~码:
http://www.blooberry.com/indexdot/html/topics/urlencoding.htm.

RFC 1866: The HTML 2.0 规范 (U文?. 附录包含了字W表: http://www.rfc-editor.org/rfc/rfc1866.txt.

Web HTML 2.0 版本(RFC 1866) : http://www.w3.org/MarkUp/html-spec/html-spec_13.html.

The HTML 3.2 (Wilbur) : http://www.w3.org/MarkUp/Wilbur/.

The HTML 4.0 : http://www.w3.org/TR/REC-html40/.

W3C HTML 国际化区? http://www.w3.org/International/O-HTML.html.

warrior 2007-01-04 13:46 发表评论
]]>
[转]用完成端口开发大响应规模的Winsock应用E序http://www.shnenglu.com/zgysx/archive/2007/01/04/17216.htmlwarriorwarriorThu, 04 Jan 2007 05:42:00 GMThttp://www.shnenglu.com/zgysx/archive/2007/01/04/17216.htmlhttp://www.shnenglu.com/zgysx/comments/17216.htmlhttp://www.shnenglu.com/zgysx/archive/2007/01/04/17216.html#Feedback0http://www.shnenglu.com/zgysx/comments/commentRss/17216.htmlhttp://www.shnenglu.com/zgysx/services/trackbacks/17216.html 用完成端口开发大响应规模的Winsock应用E序

原文出处Q?a target="_blank">http://msdn.microsoft.com/msdnmag/issues/1000/Winsock/

通常要开发网l应用程序ƈ不是一件轻杄事情Q不q,实际上只要掌握几个关键的原则也就可以了——创建和q接一个套接字Q尝试进行连接,然后收发数据。真正难的是要写Z个可以接U_则一个,多则数千个连接的|络应用E序。本文将讨论如何通过Winsock2在Windows NT ?Windows 2000上开发高扩展能力的Winsock应用E序。文章主要的焦点在客h/服务器模型的服务器这一方,当然Q其中的许多要点Ҏ型的双方都适用?

API与响应规?/strong>

通过Win32的重叠I/O机制Q应用程序可以提请一I/O操作Q重叠的操作h在后台完成,而同一旉提请操作的线E去做其他的事情。等重叠操作完成后线E收到有关的通知。这U机制对那些耗时的操作而言特别有用。不q,像Windows 3.1上的WSAAsyncSelect()及Unix下的select()那样的函数虽然易于用,但是它们不能满响应规模的需要。而完成端口机制是针对操作pȝ内部q行了优化,在Windows NT ?Windows 2000上,使用了完成端口的重叠I/O机制才能够真正扩大系l的响应规模?/p>

完成端口

一个完成端口其实就是一个通知队列Q由操作pȝ把已l完成的重叠I/Oh的通知攑օ其中。当某项I/O操作一旦完成,某个可以对该操作l果q行处理的工作者线E就会收C则通知。而套接字在被创徏后,可以在Q何时候与某个完成端口q行兌?/p>

通常情况下,我们会在应用E序中创Z定数量的工作者线E来处理q些通知。线E数量取决于应用E序的特定需要。理想的情况是,U程数量{于处理器的数量Q不q这也要求Q何线E都不应该执行诸如同步读写、等待事仉知{阻塞型的操作,以免U程d。每个线E都分C定的CPU旉Q在此期间该U程可以q行Q然后另一个线E将分到一个时间片q开始执行。如果某个线E执行了d型的操作Q操作系l将剥夺其未使用的剩余时间片q让其它U程开始执行。也是_前一个线E没有充分用其旉片,当发生这L情况Ӟ应用E序应该准备其它U程来充分利用这些时间片?/p>

完成端口的用分Z步。首先创建完成端口,如以下代码所C:

HANDLE    hIocp;
hIocp = CreateIoCompletionPort(
    INVALID_HANDLE_VALUE,
    NULL,
    (ULONG_PTR)0,
    0);
if (hIocp == NULL) {
    // Error
}
完成端口创徏后,要把用该完成端口的套接字与之兌h。方法是再次调用CreateIoCompletionPort ()函数Q第一个参数FileHandle设ؓ套接字的句柄Q第二个参数ExistingCompletionPort 设ؓ刚刚创徏的那个完成端口的句柄?br />以下代码创徏了一个套接字Qƈ把它和前面创建的完成端口兌hQ?
SOCKET    s;

s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
    // Error
if (CreateIoCompletionPort((HANDLE)s,
                           hIocp,
                           (ULONG_PTR)0,
                           0) == NULL)
{
// Error
}
...
}

q时完成了套接字与完成端口的关联操作。在q个套接字上q行的Q何重叠操作都通过完成端口发出完成通知。注意,CreateIoCompletionPort()函数中的W三个参数用来设|一个与该套接字相关的“完成键(completion key)?译者注Q完成键可以是Q何数据类?。每当完成通知到来Ӟ应用E序可以d相应的完成键Q因此,完成键可用来l套接字传递一些背景信息?

在创Z完成端口、将一个或多个套接字与之相兌之后Q我们就要创q个U程来处理完成通知。这些线E不断@环调用GetQueuedCompletionStatus ()函数q返回完成通知?/p>

下面Q我们先来看看应用程序如何跟t这些重叠操作。当应用E序调用一个重叠操作函数时Q要把指向一个overlappedl构的指针包括在其参C。当操作完成后,我们可以通过GetQueuedCompletionStatus()函数中拿回这个指针。不q,单是Ҏq个指针所指向的overlapped l构Q应用程序ƈ不能分LI竟完成的是哪个操作。要实现Ҏ作的跟踪Q你可以自己定义一个OVERLAPPEDl构Q在其中加入所需的跟t信息?/p>

无论何时调用重叠操作函数ӞL会通过其lpOverlapped参数传递一个OVERLAPPEDPLUSl构(例如WSASend?WSARecv{函?。这允怽为每一个重叠调用操作设|某些操作状态信息,当操作结束后Q你可以通过 GetQueuedCompletionStatus()函数获得你自定义l构的指针。注意OVERLAPPED字段不要求一定是q个扩展后的l构的第一个字Dc当得到了指向OVERLAPPEDl构的指针以后,可以用CONTAINING_RECORD宏取出其中指向扩展结构的指针?/p>

OVERLAPPED l构的定义如下:

typedef struct _OVERLAPPEDPLUS {
    OVERLAPPED        ol;
    SOCKET            s, sclient;
    int               OpCode;
    WSABUF            wbuf;
    DWORD             dwBytes, dwFlags;
    // 其它有用的信?
} OVERLAPPEDPLUS;

#define OP_READ     0
#define OP_WRITE    1
#define OP_ACCEPT   2

下面让我们来看看工作者线E的情况?br />
工作U程WorkerThread代码Q?
DWORD WINAPI WorkerThread(LPVOID lpParam)
{    
    ULONG_PTR       *PerHandleKey;
    OVERLAPPED      *Overlap;
    OVERLAPPEDPLUS  *OverlapPlus,
                    *newolp;
    DWORD           dwBytesXfered;

    while (1)
    {
        ret = GetQueuedCompletionStatus(
            hIocp,
            &dwBytesXfered,
            (PULONG_PTR)&PerHandleKey,
            &Overlap,
            INFINITE);
        if (ret == 0)
        {
            // Operation failed
            continue;
        }
        OverlapPlus = CONTAINING_RECORD(Overlap, OVERLAPPEDPLUS, ol);
    
    switch (OverlapPlus->OpCode)
    {
    case OP_ACCEPT:
        // Client socket is contained in OverlapPlus.sclient
        // Add client to completion port
            CreateIoCompletionPort(
                (HANDLE)OverlapPlus->sclient,
                hIocp,
                (ULONG_PTR)0,
                0);

        //  Need a new OVERLAPPEDPLUS structure
        //  for the newly accepted socket. Perhaps
        //  keep a look aside list of free structures.
        newolp = AllocateOverlappedPlus();
        if (!newolp)
        {
            // Error
        }
        newolp->s = OverlapPlus->sclient;
        newolp->OpCode = OP_READ;

        // This function prepares the data to be sent
        PrepareSendBuffer(&newolp->wbuf);
  
        ret = WSASend(
                newolp->s,
                &newolp->wbuf,
                1,
                &newolp->dwBytes,
                0,
                &newolp.ol,
                NULL);
        
        if (ret == SOCKET_ERROR)
        {
            if (WSAGetLastError() != WSA_IO_PENDING)
            {
            // Error
            }
        }

        // Put structure in look aside list for later use
        FreeOverlappedPlus(OverlapPlus);

        // Signal accept thread to issue another AcceptEx
        SetEvent(hAcceptThread);
        break;

    case OP_READ:
        // Process the data read    
        // ...

        // Repost the read if necessary, reusing the same
        // receive buffer as before
        memset(&OverlapPlus->ol, 0, sizeof(OVERLAPPED));
        ret = WSARecv(
              OverlapPlus->s,
              &OverlapPlus->wbuf,
              1,
              &OverlapPlus->dwBytes,
              &OverlapPlus->dwFlags,
              &OverlapPlus->ol,
              NULL);

        if (ret == SOCKET_ERROR)
        {
            if (WSAGetLastError() != WSA_IO_PENDING)
            {
                // Error
            }
        }
        break;

    case OP_WRITE:
        // Process the data sent, etc.
        break;
    } // switch
    } // while
}  // WorkerThread

其中每句柄键(PerHandleKey)变量的内容,是在把完成端口与套接字进行关联时所讄的完成键参数QOverlap参数q回的是一个指向发出重叠操作时所使用的那个OVERLAPPEDPLUSl构的指针?

要记住,如果重叠操作调用p|?也就是说Q返回值是SOCKET_ERRORQƈ且错误原因不是WSA_IO_PENDING)Q那么完成端口将不会收到M完成通知。如果重叠操作调用成功,或者发生原因是WSA_IO_PENDING的错误时Q完成端口将L能够收到完成通知?br />
Windows NT和Windows 2000的套接字架构

对于开发大响应规模的Winsock应用E序而言Q对Windows NT和Windows 2000的套接字架构有基本的了解是很有帮助的。下图是Windows 2000中的Winsock架构Q?br />

与其它类型操作系l不同,Windows NT和Windows 2000的传输协议没有一U风格像套接字那L、可以和应用E序直接交谈的界面,而是采用了一U更为底层的APIQ叫做传输驱动程序界?Transport Driver Interface,TDI)。Winsock的核心模式驱动程序负责连接和~冲区管理,以便向应用程序提供套接字仿真(在AFD.SYS文g中实?Q同时负责与底层传输驱动E序对话?/p>

谁来负责理~冲区?

正如上面所说的Q应用程序通过Winsock来和传输协议驱动E序交谈Q而AFD.SYS负责为应用程序进行缓冲区理。也是_当应用程序调?send()或WSASend()函数来发送数据时QAFD.SYS把数据拯q它自己的内部缓冲区(取决于SO_SNDBUF讑֮?Q然后send ()或WSASend()函数立即q回。也可以q么_AFD.SYS在后台负责把数据发送出厅R不q,如果应用E序要求发出的数据超q了 SO_SNDBUF讑֮的缓冲区大小Q那么WSASend()函数会阻塞,直至所有数据发送完毕?/p>

从远E客L接收数据的情况也cM。只要不用从应用E序那里接收大量的数据,而且没有出SO_RCVBUF讑֮的|AFD.SYS把数据先拷贝到其内部缓冲区中。当应用E序调用recv()或WSARecv()函数Ӟ数据从内部~冲拯到应用程序提供的~冲区?/p>

多数情况下,q样的架构运行良好,特别在是应用E序采用传统的套接字下非重叠的send()和receive()模式~写的时候。不q程序员要小心的是,管可以通过setsockopt()q个API来把SO_SNDBUF和SO_RCVBUF选项D?(关闭内部~冲?Q但是程序员必须十分清楚?AFD.SYS的内部缓冲区x会造成什么后果,避免收发数据时有关的~冲区拷贝可能引Lpȝ崩溃?/p>

举例来说Q一个应用程序通过讑֮SO_SNDBUF?把缓冲区关闭Q然后发Z个阻塞send()调用。在q样的情况下Q系l内怼把应用程序的~冲区锁定,直到接收方确认收C整个~冲区后send()调用才返回。似乎这是一U判定你的数据是否已lؓҎ全部收到的简z的ҎQ实际上却ƈ非如此。想想看Q即使远端TCP 通知数据已经收到Q其实也Ҏ不代表数据已l成功送给客户端应用程序,比如Ҏ可能发生资源不的情况,DAFD.SYS不能把数据拷贝给应用E序。另一个更要紧的问题是Q在每个U程中每ơ只能进行一ơ发送调用,效率极其低下?/p>

把SO_RCVBUF设ؓ0Q关闭AFD.SYS的接收缓冲区也不能让性能得到提升Q这只会q接收到的数据在比Winsock更低的层ơ进行缓Ԍ当你发出receive调用Ӟ同样要进行缓冲区拯Q因此你本来想避免缓冲区拯的阴谋不会得逞?/p>

现在我们应该清楚了,关闭~冲区对于多数应用程序而言q不是什么好L。只要要应用E序注意随时在某个连接上保持几个WSARecvs重叠调用Q那么通常没有必要关闭接收~冲区。如果AFD.SYSL有由应用E序提供的缓冲区可用Q那么它没有必要用内部缓冲区?/p>

高性能的服务器应用E序可以关闭发送缓冲区Q同时不会损失性能。不q,q样的应用程序必d分小心,保证它L发出多个重叠发送调用,而不是等待某个重叠发送结束了才发Z一个。如果应用程序是按一个发完再发下一个的序来操作,那浪Ҏ两次发送中间的I档旉QM是要保证传输驱动E序在发送完一个缓冲区后,立刻可以转向另一个缓冲区?/p>

资源的限制条?/strong>

在设计Q何服务器应用E序Ӟ其强健性是主要的目标。也是_

你的应用E序要能够应对Q何突发的问题Q例如ƈ发客戯求数辑ֈ峰倹{可用内存时出C뀁以及其它短旉的现象。这p求程序的设计者注意Windows NT?000pȝ下的资源限制条g的问题,从容地处理突发性事件?/p>

你可以直接控制的、最基本的资源就是网l带宽。通常Q用用h据报协议(UDP)的应用程序都可能会比较注意带宽方面的限制Q以最大限度地减少包的丢失。然而,在用TCPq接Ӟ服务器必d分小心地控制好,防止|络带宽q蝲过一定的旉Q否则将需要重发大量的包或造成大量q接中断。关于带宽管理的Ҏ应根据不同的应用E序而定Q这出了本文讨论的范围?/p>

虚拟内存的用也必须很小心地理。通过谨慎地申请和释放内存Q或者应用lookaside lists(一U高速缓?技术来重新使用已分配的内存Q将有助于控制服务器应用E序的内存开销(原文为“让服务器应用程序留下的脚印一点?Q避免操作系l频J地应用程序申L物理内存交换到虚拟内存中(原文为“让操作pȝ能够L把更多的应用E序地址I间更多C留在内存中?。你也可以通过 SetWorkingSetSize()q个Win32 API让操作系l分配给你的应用E序更多的物理内存?/p>

在用Winsock时还可能到另外两个非直接的资源不情况。一个是被锁定的内存面的极限。如果你把AFD.SYS的缓冲关闭,当应用程序收发数据时Q应用程序缓冲区的所有页面将被锁定到物理内存中。这是因为内栔R动程序需要访问这些内存,在此期间q些面不能交换出去。如果操作系l需要给其它应用E序分配一些可分页的物理内存,而又没有_的内存时׃发生问题。我们的目标是要防止写出一个病态的、锁定所有物理内存、让pȝ崩溃的程序。也是_你的E序锁定内存Ӟ不要出pȝ规定的内存分|限?/p>

在Windows NT?000pȝ上,所有应用程序d可以锁定的内存大U是物理内存?/8(不过q只是一个大概的估计Q不是你计算内存的依?。如果你的应用程序不注意q一点,当你的发出太多的重叠收发调用Q而且I/O没来得及完成Ӟ可能偶发生ERROR_INSUFFICIENT_RESOURCES的错误。在q种情况下你要避免过度锁定内存。同时要注意Q系l会锁定包含你的~冲区所在的整个内存面Q因此缓冲区靠近边界时是有代h?译者理解,~冲区如果正好超q页面边界,那怕是1个字节,出的这个字节所在的面也会被锁??/p>

另外一个限制是你的E序可能会遇到系l未分页池资源不的情况。所谓未分页池是一块永q不被交换出ȝ内存区域Q这块内存用来存储一些供各种内核lg讉K的数据,其中有的内核lg是不能访问那些被交换出去的页面空间的。Windows NT?000的驱动程序能够从q个特定的未分页池分配内存?/p>

当应用程序创Z个套接字(或者是cM的打开某个文g)Ӟ内核会从未分|中分配一定数量的内存Q而且在绑定、连接套接字Ӟ内核又会从未分页池中再分配一些内存。当你注意观察这U行为时你将发现Q如果你发出某些I/Oh?例如收发数据)Q你会从未分|里再分配多一些内?比如要追t某个待决的 I/O操作Q你可能需要给q个操作d一个自定义l构Q如前文所提及?。最后这可能会造成一定的问题Q操作系l会限制未分内存的用量?/p>

在Windows NT?000q两U操作系l上Q给每个q接分配的未分页内存的具体数量是不同的,未来版本的Windows很可能也不同。ؓ了应用E序的生命期更长Q你׃应该计算Ҏ分页池内存的具体需求量?/p>

你的E序必须防止消耗到未分|的极限。当pȝ中未分页池剩余空间太时Q某些与你的应用E序毫无关系的内栔R动就会发疯,甚至造成pȝ崩溃Q特别是当系l中有第三方讑֤或驱动程序时Q更Ҏ发生q样的惨?而且无法预测)。同时你q要CQ同一台电脑上q可能运行有其它同样消耗未分页池的其它应用E序Q因此在设计你的应用E序Ӟ对资源量的预估要特别保守和}慎?/p>

处理资源不的问题是十分复杂的,因ؓ发生上述情况时你不会收到特别的错误代码,通常你只能收C般性的WSAENOBUFS或者ERROR_INSUFFICIENT_RESOURCES 错误。要处理q些错误Q首先,把你的应用程序工作配|调整到合理的最大?译者注Q所谓工作配|,是指应用E序各部分运行中所需的内存用量,请参?http://msdn.microsoft.com/msdnmag/issues/1000/Bugslayer/Bugslayer1000.asp Q关于内存优化,译者另有译?Q如果错误l出玎ͼ那么注意查是否是|络带宽不的问题。之后,L认你没有同时发出太多的收发调用。最后,如果q是收到资源不的错误,那就很可能是遇到了未分页内存池不的问题了。要释放未分内存池I间Q请关闭应用E序中相当部分的q接Q等待系l自行渡q和修正q个瞬时的错误?br />
接受q接h

服务器要做的最普通的事情之一是接受来自客户端的q接h。在套接字上使用重叠I/O接受q接的惟一API是AcceptEx()函数。有的是,通常的同步接受函数accept()的返回值是一个新的套接字Q而AcceptEx()函数则需要另外一个套接字作ؓ它的参数之一。这是因?AcceptEx()是一个重叠操作,所以你需要事先创Z个套接字(但不要绑定或q接?Qƈ把这个套接字通过参数传给AcceptEx()。以下是一段典型的用AcceptEx()的伪代码Q?

do {
    -{待上一?AcceptEx 完成
    -创徏一个新套接字ƈ与完成端口进行关?
    -讄背景l构{等
    -发出一?AcceptEx h
}while(TRUE);
作ؓ一个高响应能力的服务器Q它必须发出_的AcceptEx调用Q守候着Q一旦出现客Lq接hqd应。至于发出多个AcceptEx才够Q就取决于你的服务器E序所期待的通信交通类型。比如,如果q入q接率高的情?因ؓq接持箋旉较短Q或者出C通高?Q那么所需要守候的 AcceptEx当然要比那些偶尔q入的客Lq接的情况要多。聪明的做法是,由应用程序来分析交通状况,q调整AcceptEx守候的数量Q而不是固定在某个数量上?

对于Windows2000QWinsock提供了一些机Ӟ帮助你判定AcceptEx的数量是否够。这是Q在创徏监听套接字时创徏一个事Ӟ通过WSAEventSelect()q个APIq注册FD_ACCEPT事g通知来把套接字和q个事g兌h。一旦系l收C个连接请求,如果pȝ中没有AcceptEx()正在{待接受q接Q那么上面的事g收C个信受通过q个事gQ你可以判断你有没有发够的 AcceptEx()Q或者检出一个非正常的客戯?下文q?。这U机制对Windows NT 4.0不适用?/p>

使用AcceptEx()的一大好处是Q你可以通过一ơ调用就完成接受客户端连接请求和接受数据(通过传送lpOutputBuffer参数)两g事情。也是_如果客户端在发出q接的同时传输数据,你的AcceptEx()调用在连接创建ƈ接收了客L数据后就可以立刻q回。这样可能是很有用的Q但是也可能会引发问题,因ؓAcceptEx()必须{全部客L数据都收C才返回。具体来_如果你在发出AcceptEx()调用的同时传递了lpOutputBuffer参数Q那么AcceptEx()不再是一原子型的操作,而是分成了两步:接受客户q接Q等待接收数据。当~少一U机制来通知你的应用E序所发生的这U情况:“连接已l徏立了Q正在等待客L数据”,q将意味着有可能出现客L只发接请求,但是不发送数据。如果你的服务器收到太多q种cd的连接时Q它拒l连接更多的合法客户端请求。这是黑客q行“拒l服务”攻ȝ常见手法?/p>

要预防此cL击,接受q接的线E应该不时地通过调用getsockopt()函数(选项参数?SO_CONNECT_TIME)来检查AcceptEx()里守候的套接字。getsockopt()函数的选项值将被设|ؓ套接字被q接的时_或者设|ؓ-1(代表套接字尚未徏立连?。这ӞWSAEventSelect()的特性就可以很好地利用来做这U检查。如果发现连接已l徏立,但是很久都没有收到数据的情况Q那么就应该l止q接Q方法就是关闭作为参数提供给AcceptEx()的那个套接字。注意,在多数非紧急情况下Q如果套接字已经传递给AcceptEx()q开始守候,但还未徏立连接,那么你的应用E序不应该关闭它们。这是因为即使关闭了q些套接字,Z提高pȝ性能的考虑Q在q接q入之前Q或者监听套接字自n被关闭之前,相应的内核模式的数据l构也不会被q净地清除?/p>

发出AcceptEx()调用的线E,g与那个进行完成端口关联操作、处理其它I/O完成通知的线E是同一个,但是Q别忘记U程里应该尽力避免执行阻塞型的操作。Winsock2分层l构的一个副作用是调用socket()或WSASocket() API的上层架构可能很重要(译者不太明白原文意思,抱歉)。每个AcceptEx()调用都需要创Z个新套接字,所以最好有一个独立的U程专门调用AcceptEx()Q而不参与其它I/O处理。你也可以利用这个线E来执行其它dQ比如事件记录?/p>

有关AcceptEx()的最后一个注意事:要实现这些APIQƈ不需要其它提供商提供的Winsock2实现。这一点对微YҎ的其它API也同样适用Q比如TransmitFile()和GetAcceptExSockAddrs()Q以及其它可能会被加入到新版Windows的API. 在Windows NT?000上,q些API是在微Y的底层提供者DLL(mswsock.dll)中实现的Q可通过与mswsock.lib~译q接q行调用Q或者通过WSAIoctl() (选项参数为SIO_GET_EXTENSION_FUNCTION_POINTER)动态获得函数的指针?/p>

如果在没有事先获得函数指针的情况下直接调用函?也就是说Q编译时静态连接mswsock.libQ在E序中直接调用函?Q那么性能很受媄响。因?AcceptEx()被置于Winsock2架构之外Q每ơ调用时它都被迫通过WSAIoctl()取得函数指针。要避免q种性能损失Q需要用这?API的应用程序应该通过调用WSAIoctl()直接从底层的提供者那里取得函数的指针?br />
参见下图套接字架构:



TransmitFile ?TransmitPackets

Winsock 提供两个专门为文件和内存数据传输q行了优化的函数。其中TransmitFile()q个API函数在Windows NT 4.0 ?Windows 2000上都可以使用Q而TransmitPackets()则将在未来版本的Windows中实现?/p>

TransmitFile ()用来把文件内定w过Winsockq行传输。通常发送文件的做法是,先调用CreateFile()打开一个文Ӟ然后不断循环调用ReadFile () 和WSASend ()直至数据发送完毕。但是这U方法很没有效率Q因为每ơ调用ReadFile() ?WSASend ()都会涉及一ơ从用户模式到内核模式的转换。如果换成TransmitFile()Q那么只需要给它一个已打开文g的句柄和要发送的字节敎ͼ而所涉及的模式{换操作将只在调用CreateFile()打开文g时发生一ơ,然后TransmitFile()时再发生一ơ。这h率就高多了?/p>

TransmitPackets()比TransmitFile()更进一步,它允许用户只调用一ơ就可以发送指定的多个文g和内存缓冲区。函数原型如下:

BOOL TransmitPackets(
  SOCKET hSocket,                             
  LPTRANSMIT_PACKET_ELEMENT lpPacketArray,
  DWORD nElementCount,                
  DWORD nSendSize,                
  LPOVERLAPPED lpOverlapped,                  
  DWORD dwFlags                               
); 
其中QlpPacketArray是一个结构的数组Q其中的每个元素既可以是一个文件句柄或者内存缓冲区Q该l构定义如下Q?br />
typedef struct _TRANSMIT_PACKETS_ELEMENT { 
    DWORD dwElFlags; 
    DWORD cLength; 
    union {
        struct {
            LARGE_INTEGER     nFileOffset;
            HANDLE            hFile;
            };
            PVOID             pBuffer;
    };
} TRANSMIT_FILE_BUFFERS;
其中各字D|自描q型?self explanatory)?br />dwElFlags字段Q指定当前元素是一个文件句柄还是内存缓冲区(分别通过帔RTF_ELEMENT_FILE 和TF_ELEMENT_MEMORY指定)Q?br />cLength字段Q指定将从数据源发送的字节?如果是文Ӟq个字段gؓ0表示发送整个文?Q?br />l构中的无名联合体:包含文g句柄的内存缓冲区(以及可能的偏U量)?

使用q两个API的另一个好处,是可以通过指定TF_REUSE_SOCKET和TF_DISCONNECT标志来重用套接字句柄。每当API完成数据的传输工作后Q就会在传输层别断开q接Q这栯个套接字又可以重新提供lAcceptEx()使用。采用这U优化的Ҏ~程Q将减轻那个专门做接受操作的U程创徏套接字的压力(前文q及)?/p>

q两个API也都有一个共同的qQWindows NT Workstation ?Windows 2000 专业版中Q函数每ơ只能处理两个调用请求,只有在Windows NT、Windows 2000服务器版、Windows 2000高服务器版?Windows 2000 Data Center中才获得完全支持?/p>

攑֜一L?/strong>

以上各节中,我们讨论了开发高性能的、大响应规模的应用程序所需的函数、方法和可能遇到的资源瓶颈问题。这些对你意味着什么呢Q其实,q取决于你如何构造你的服务器和客L。当你能够在服务器和客户端设计上q行更好地控制时Q那么你能够避开瓉问题?/p>

来看一个示范的环境。我们要设计一个服务器来响应客L的连接、发送请求、接收数据以及断开q接。那么,服务器将需要创Z个监听套接字Q把它与某个完成端口q行兌Qؓ每颗CPU创徏一个工作线E。再创徏一个线E专门用来发出AcceptEx()。我们知道客L会在发出q接h后立M送数据,所以如果我们准备好接收~冲Z使事情变得更为容易。当Ӟ不要忘记不时地轮询AcceptEx()调用中用的套接?使用SO_CONNECT_TIME选项参数)来确保没有恶意超时的q接?/p>

该设计中有一个重要的问题要考虑Q我们应该允许多个AcceptEx()q行守候。这是因为,每发Z个AcceptEx()时我们都同时需要ؓ它提供一个接收缓冲区Q那么内存中会出现很多被锁定的面(前文说过了,每个重叠操作都会消耗一部分未分页内存池,同时q会锁定所有涉及的~冲?。这个问题很隑֛{,没有一个确切的{案。最好的Ҏ是把q个值做成可以调整的Q通过反复做性能试Q你可以得出在典型应用环境中最佳的倹{?/p>

好了Q当你测清楚后Q下面就是发送数据的问题了,考虑的重Ҏ你希望服务器同时处理多少个ƈ发的q接。通常情况下,服务器应该限制ƈ发连接的数量以及{候处理的发送调用。因为ƈ发连接数量越多,所消耗的未分内存池也越多;{候处理的发送调用越多,被锁定的内存面也越?心别超q了极限)。这同样也需要反复测试才知道{案?/p>

对于上述环境Q通常不需要关闭单个套接字的缓冲区Q因为只在AcceptEx()中有一ơ接收数据的操作Q而要保证l每个到来的q接提供接收~冲区ƈ不是太难的事情。但是,如果客户Z服务器交互的方式变一变,客户机在发送了一ơ数据之后,q需要发送更多的数据Q在q种情况下关闭接收缓冲就不太妙了Q除非你惛_法保证在每个q接上都发出了重叠接收调用来接收更多的数据?/p>

l论

开发大响应规模的Winsock服务器ƈ不是很可怕,其实也就是设|一个监听套接字、接受连接请求和q行重叠收发调用。通过讄合理的进行守候的重叠调用的数量,防止出现未分内存池被耗尽Q这才是最主要的挑战。按照我们前面讨论的一些原则,你就可以开发出大响应规模的服务器应用程序?/p>

warrior 2007-01-04 13:42 发表评论
]]>
讄revc()的等待超时时?/title><link>http://www.shnenglu.com/zgysx/archive/2006/12/11/16276.html</link><dc:creator>warrior</dc:creator><author>warrior</author><pubDate>Mon, 11 Dec 2006 08:39:00 GMT</pubDate><guid>http://www.shnenglu.com/zgysx/archive/2006/12/11/16276.html</guid><wfw:comment>http://www.shnenglu.com/zgysx/comments/16276.html</wfw:comment><comments>http://www.shnenglu.com/zgysx/archive/2006/12/11/16276.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zgysx/comments/commentRss/16276.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zgysx/services/trackbacks/16276.html</trackback:ping><description><![CDATA[ <p> </p> <p>#include <stdio.h><br />#include <winsock2.h></p> <p> <br />#define TCP</p> <p>#pragma comment(lib,"ws2_32.lib")</p> <p>int main( int argc ,char* argv[])<br />{<br /> WSAData wsaData;</p> <p> WSAStartup(WINSOCK_VERSION,&wsaData);<br /> DWORD begin,finish;</p> <p> struct timeval tv;<br /> tv.tv_sec = 5000;<br /> tv.tv_usec = 0;<br /> int optlen = sizeof(struct timeval);</p> <p> <br /> //<br /> SOCKET sock = socket(PF_INET,SOCK_STREAM,0);</p> <p> struct sockaddr_in to;<br /> int len = sizeof(struct sockaddr_in);<br /> memset(&to,0,len);</p> <p> to.sin_addr.s_addr = inet_addr("202.108.9.39");;<br /> to.sin_port = htons(80);<br /> to.sin_family = AF_INET;</p> <p> if ( connect(sock,(struct sockaddr*)&to,len) == SOCKET_ERROR )<br /> {<br />  closesocket(sock);<br />  return 0;<br /> }</p> <p> <font color="#ff3333">//讄时gؓtv<br /> if ( setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(char*)&tv,optlen) ==<br />  SOCKET_ERROR)<br /></font> {<br />  closesocket(sock);<br />  return 0;<br /> }</p> <p> char buf[100];<br /> if ( recv(sock,buf,100,0) == SOCKET_ERROR )<br /> {<br />  printf("d");<br /> }</p> <p> return 0;<br />}</p> <img src ="http://www.shnenglu.com/zgysx/aggbug/16276.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zgysx/" target="_blank">warrior</a> 2006-12-11 16:39 <a href="http://www.shnenglu.com/zgysx/archive/2006/12/11/16276.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用MFC提供的HttpcM载和上传文ghttp://www.shnenglu.com/zgysx/archive/2006/09/28/13088.htmlwarriorwarriorThu, 28 Sep 2006 03:34:00 GMThttp://www.shnenglu.com/zgysx/archive/2006/09/28/13088.htmlhttp://www.shnenglu.com/zgysx/comments/13088.htmlhttp://www.shnenglu.com/zgysx/archive/2006/09/28/13088.html#Feedback5http://www.shnenglu.com/zgysx/comments/commentRss/13088.htmlhttp://www.shnenglu.com/zgysx/services/trackbacks/13088.html1、下载文?br />Download(const CString& strFileURLInServer, //待下载文件的URL
const CString & strFileLocalFullPath)//存放到本地的路径
{
 ASSERT(strFileURLInServer != "");
 ASSERT(strFileLocalFullPath != "");
 CInternetSession session;
 CHttpConnection* pHttpConnection = NULL;
 CHttpFile* pHttpFile = NULL;
 CString strServer, strObject;
 INTERNET_PORT wPort;

 DWORD dwType;
 const int nTimeOut = 2000;
 session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, nTimeOut); //重试之间的等待g?br /> session.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 1);   //重试ơ数
 char* pszBuffer = NULL;  

 try
 {
  AfxParseURL(strFileURLInServer, dwType, strServer, strObject, wPort);
  pHttpConnection = session.GetHttpConnection(strServer, wPort);
  pHttpFile = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject);
  if(pHttpFile->SendRequest() == FALSE)
   return false;
  DWORD dwStateCode;

  pHttpFile->QueryInfoStatusCode(dwStateCode);
  if(dwStateCode == HTTP_STATUS_OK)
  {
    HANDLE hFile = CreateFile(strFileLocalFullPath, GENERIC_WRITE,
         FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
         NULL);  //创徏本地文g
   if(hFile == INVALID_HANDLE_VALUE)
   {
    pHttpFile->Close();
    pHttpConnection->Close();
    session.Close();
    return false;
   }
 
   char szInfoBuffer[1000];  //q回消息
   DWORD dwFileSize = 0;   //文g长度
   DWORD dwInfoBufferSize = sizeof(szInfoBuffer);
   BOOL bResult = FALSE;
   bResult = pHttpFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH,
           (void*)szInfoBuffer,&dwInfoBufferSize,NULL);

   dwFileSize = atoi(szInfoBuffer);
   const int BUFFER_LENGTH = 1024 * 10;
   pszBuffer = new char[BUFFER_LENGTH];  //d文g的缓?br />   DWORD dwWrite, dwTotalWrite;
   dwWrite = dwTotalWrite = 0;
   UINT nRead = pHttpFile->Read(pszBuffer, BUFFER_LENGTH); //d服务器上数据

   while(nRead > 0)
   {
    WriteFile(hFile, pszBuffer, nRead, &dwWrite, NULL);  //写到本地文g
    dwTotalWrite += dwWrite;
    nRead = pHttpFile->Read(pszBuffer, BUFFER_LENGTH);
   }

   delete[]pszBuffer;
   pszBuffer = NULL;
   CloseHandle(hFile);
  }
  else
  {
   delete[]pszBuffer;
   pszBuffer = NULL;
   if(pHttpFile != NULL)
   {
    pHttpFile->Close();
    delete pHttpFile;
    pHttpFile = NULL;
   }
   if(pHttpConnection != NULL)
   {
    pHttpConnection->Close();
    delete pHttpConnection;
    pHttpConnection = NULL;
   }
   session.Close();
    return false;
  }
 }
 catch(...)
 {
  delete[]pszBuffer;
  pszBuffer = NULL;
  if(pHttpFile != NULL)
  {
   pHttpFile->Close();
   delete pHttpFile;
   pHttpFile = NULL;
  }
  if(pHttpConnection != NULL)
  {
   pHttpConnection->Close();
   delete pHttpConnection;
   pHttpConnection = NULL;
  }
  session.Close();
  return false;
 }

 if(pHttpFile != NULL)
  pHttpFile->Close();
 if(pHttpConnection != NULL)
  pHttpConnection->Close();
 session.Close();
 return true;
}

2、上传文?br />UploadFile(LPCTSTR strURL, //负责接收上传操作的页面的URL
LPCTSTR strLocalFileName)  //待上传的本地文g路径
{
 ASSERT(strURL != NULL && strLocalFileName != NULL);

 BOOL bResult = FALSE;
 DWORD dwType = 0;
 CString strServer;
 CString strObject;
 INTERNET_PORT wPort = 0;
 DWORD dwFileLength = 0;
 char * pFileBuff = NULL;

 CHttpConnection * pHC = NULL;
 CHttpFile * pHF = NULL;
 CInternetSession cis;

 bResult =  AfxParseURL(strURL, dwType, strServer, strObject, wPort);
 if(!bResult)
  return FALSE;
 CFile file;
 try
 {
  if(!file.Open(strLocalFileName, CFile::shareDenyNone | CFile::modeRead))
   return FALSE;
  dwFileLength = file.GetLength();
  if(dwFileLength <= 0)
   return FALSE;
  pFileBuff = new char[dwFileLength];
  memset(pFileBuff, 0, sizeof(char) * dwFileLength);
  file.Read(pFileBuff, dwFileLength);

  const int nTimeOut = 5000;
  cis.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, nTimeOut); //联接时讄
  cis.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 1);  //重试1?br />  pHC = cis.GetHttpConnection(strServer, wPort);  //取得一个Http联接

  pHF = pHC->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject);
  if(!pHF->SendRequest(NULL, 0, pFileBuff, dwFileLength))
  {
   delete[]pFileBuff;
   pFileBuff = NULL;
   pHF->Close();
   pHC->Close();
   cis.Close();
   return FALSE;
  }
  DWORD dwStateCode = 0;
  pHF->QueryInfoStatusCode(dwStateCode);

  if(dwStateCode == HTTP_STATUS_OK)
   bResult = TRUE;
 }

 catch(CInternetException * pEx)
 {
  char sz[256] = "";
  pEx->GetErrorMessage(sz, 25);
  CString str;
  str.Format("InternetException occur!\r\n%s", sz);
  AfxMessageBox(str);
 }
 catch(CFileException& fe)
 {
  CString str;
  str.Format("FileException occur!\r\n%d", fe.m_lOsError);
  AfxMessageBox(str);
 }
 catch(...)
 {
  DWORD dwError = GetLastError();
  CString str;
  str.Format("Unknow Exception occur!\r\n%d", dwError);
  AfxMessageBox(str);
 }

 delete[]pFileBuff;
 pFileBuff = NULL;
 file.Close();
 pHF->Close();
 pHC->Close();
 cis.Close();
 return bResult;
}



warrior 2006-09-28 11:34 发表评论
]]>
99ξþþŷƷվ | þ߳ˮ| þӰԺҹƬ| ޹㽶ˬAVƬþ| ޹Ʒ˾þ| þþƷAV| ձŷþþþѲ| ˾þþƷһ| ƷþþþþþþþĻ| þþƷۺ| ˾þþƷһ| ŷ츾BBBþþ| Ӱһþҹײ | þþƷAɫ| ޹Ʒľþþ| AëƬþ| ӰɫۺϾþ| Ʒ99þþƷ| 18ƾþþAAAƬ| þþƷƷ| ˾þAV| þˬˬƬAV | ƷѾþ| AVݺɫۺϾþ| ھƷþþþþþþõӰ | þһձɫۺϾþ| ɫþ| ھƷ˾þþþӰԺ԰| ھƷþ޻| ޹Ʒ˾þ| ŷ޹Ʒþø| һaƬþëƬ| þۺһ| þþƷ| ŷսþþþþþ| þһ| þþþþ޾Ʒ| þþþþþþþþѾƷ| ھƷþþþþþӰ鶹| Ʒ99þþƷ| þþûƬ|