<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>Socket程序设计循序渐进</title><link>http://blog.vckbase.com/arong/category/126.html</link><description>考虑到大家对socket程序设计都很感兴趣，而许多初学者都不是很熟悉怎么处理。这里从头开始介绍socket得设计。需要得时间可能比较长，希望能对大家有所帮助
涉及课题：多线程、数据分析、设计模式等</description><managingEditor>馨荣家园</managingEditor><dc:language>af</dc:language><generator>.Text Version 0.958.2004.214</generator><item><dc:creator>馨荣家园</dc:creator><title>CAsyncSocket对象不能跨线程之分析</title><link>http://blog.vckbase.com/arong/archive/2005/12/03/15578.html</link><pubDate>Sat, 03 Dec 2005 06:08:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/12/03/15578.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/15578.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/12/03/15578.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/15578.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/15578.html</trackback:ping><description>&lt;h2&gt;现象&lt;/h2&gt;
用多线程方法设计socket程序时，你会发现在跨线程使用CAsyncSocket及其派生类时，会出现程序崩溃。所谓跨线程，是指该对象在一个线程中调用Create/AttachHandle/Attach函数，然后在另外一个线程中调用其他成员函数。下面的例子就是一个典型的导致崩溃的过程：

&lt;PRE&gt;
CAsyncSocket Socket;
UINT Thread(LPVOID)
{
       Socket.Close ();
       return 0;
}
void CTestSDlg::OnOK() 
{
       // TODO: Add extra validation here
       Socket.Create(0);
       AfxBeginThread(Thread,0,0,0,0,0);
}
&lt;/PRE&gt;
&lt;P&gt;其中Socket对象在主线程中被调用，在子线程中被关闭。
&lt;h2&gt;跟踪分析&lt;/h2&gt;
&lt;P&gt;这个问题的原因可以通过单步跟踪(F11)的方法来了解。我们在Socket.Create(0)处设断点，跟踪进去会发现下面的函数被调用：
&lt;PRE&gt;
void PASCAL CAsyncSocket::AttachHandle(
          SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead)
{
    _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
    BOOL bEnable = AfxEnableMemoryTracking(FALSE);
    if (!bDead)
    {
             ASSERT(CAsyncSocket::LookupHandle(hSocket, bDead) == NULL);
             if (pState-&amp;gt;m_pmapSocketHandle-&amp;gt;IsEmpty())
             {
                  ASSERT(pState-&amp;gt;m_pmapDeadSockets-&amp;gt;IsEmpty());
                  ASSERT(pState-&amp;gt;m_hSocketWindow == NULL);
                  CSocketWnd* pWnd = new CSocketWnd;
                  pWnd-&amp;gt;m_hWnd = NULL;
                  if (!pWnd-&amp;gt;CreateEx(0, AfxRegisterWndClass(0),
                                   _T("Socket Notification Sink"),
                                 WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL))
                 {
                       TRACE0("Warning: unable to create socket notify window!\n");
                       AfxThrowResourceException();
                 }
                 ASSERT(pWnd-&amp;gt;m_hWnd != NULL);
                 ASSERT(CWnd::FromHandlePermanent(pWnd-&amp;gt;m_hWnd) == pWnd);
                 pState-&amp;gt;m_hSocketWindow = pWnd-&amp;gt;m_hWnd;
            }
            pState-&amp;gt;m_pmapSocketHandle-&amp;gt;SetAt((void*)hSocket, pSocket);
    }
    else
    {
           int nCount;
           if (pState-&amp;gt;m_pmapDeadSockets-&amp;gt;Lookup((void*)hSocket, (void*&amp;amp;)nCount))
                     nCount++;
           else
                     nCount = 1;
           pState-&amp;gt;m_pmapDeadSockets-&amp;gt;SetAt((void*)hSocket, (void*)nCount);
   }
   AfxEnableMemoryTracking(bEnable);
}
&lt;/PRE&gt;
&lt;P&gt;在这个函数的开头，首先获得了一个pState的指针指向_afxSockThreadState对象。从名字可以看出，这似乎是一个和线程相关的变量，实际上它是一个宏，定义如下：&lt;/P&gt;
&lt;PRE&gt;
#define _afxSockThreadState AfxGetModuleThreadState()
&lt;/PRE&gt;
&lt;P&gt;我们没有必要去细究这个指针的定义是如何的，只要知道它是和当前线程密切关联的，其他线程应该也有类似的指针，只是指向不同的结构。&lt;/P&gt;
&lt;P&gt;在这个函数中，CAsyncSocket创建了一个窗口，并把如下两个信息加入到pState所管理的结构中：
&lt;PRE&gt;
    pState-&amp;gt;m_pmapSocketHandle-&amp;gt;SetAt((void*)hSocket, pSocket);
    pState-&amp;gt;m_pmapDeadSockets-&amp;gt;SetAt((void*)hSocket, (void*)nCount);
    pState-&amp;gt;m_hSocketWindow = pWnd-&amp;gt;m_hWnd;
    pState-&amp;gt;m_pmapSocketHandle-&amp;gt;SetAt((void*)hSocket, pSocket);
&lt;/PRE&gt;
&lt;P&gt;当调用Close时，我们再次跟踪，就会发现在KillSocket中，下面的函数出现错误:

&lt;PRE&gt;
    void PASCAL CAsyncSocket::KillSocket(SOCKET hSocket, CAsyncSocket* pSocket)
    {
            ASSERT(CAsyncSocket::LookupHandle(hSocket, FALSE) != NULL);
&lt;/PRE&gt;
&lt;P&gt;我们在这个ASSERT处设置断点，跟踪进LookupHandle，会发现这个函数定义如下：
&lt;PRE&gt;
CAsyncSocket* PASCAL CAsyncSocket::LookupHandle(SOCKET hSocket, BOOL bDead)
{
     CAsyncSocket* pSocket;
     _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
     if (!bDead)
     {
             pSocket = (CAsyncSocket*)
             pState-&amp;gt;m_pmapSocketHandle-&amp;gt;GetValueAt((void*)hSocket);
             if (pSocket != NULL)
                  return pSocket;
    }
    else
    {
             pSocket = (CAsyncSocket*)
                  pState-&amp;gt;m_pmapDeadSockets-&amp;gt;GetValueAt((void*)hSocket);
             if (pSocket != NULL)
                   return pSocket;
    }
    return NULL;
}
&lt;/PRE&gt;
&lt;P&gt;显然，这个函数试图从当前线程查询关于这个 socket的信息，可是这个信息放在创建这个socket的线程中，因此这种查询显然会失败，最终返回NULL。
&lt;/P&gt;
&lt;P&gt;有人会问，既然它是ASSERT出错，是不是Release就没问题了。这只是自欺欺人。ASSERT/VERIFY都是检验一些程序正常运行必须正确的条件。如果ASSERT都失败，在Release中也许不会显现，但是你的程序肯定运行不正确，啥时候出错就不知道了。&lt;/P&gt;
&lt;h2&gt;如何在多线程之间传递socket&lt;/h2&gt;
&lt;P&gt;有些特殊情况下，可能需要在不同线程之间传递socket。当然我不建议在使用CAsyncSOcket的时候这么做，因为这增加了出错的风险（尤其当出现拆解包问题时，有人称为粘包，我基本不认同这种称呼）。如果一定要这么做，方法应该是：
&lt;ol type =1&gt;
&lt;li&gt;当前拥有这个socket的线程调用Detach方法，这样socket句柄和C++对象及当前线程脱离关系
&lt;li&gt;当前线程把这个对象传递给另外一个线程
&lt;li&gt;另外一个线程创建新的CAsyncSocket对象，并调用Attach
&lt;/ol&gt;
&lt;P&gt;上面的例子，我稍微做修改，就不会出错了：
&lt;PRE&gt;
CAsyncSocket Socket;
UINT Thread(LPVOID sock)
{
         Socket.Attach((SOCKET)sock);
         Socket.Close ();
         return 0;
}
void CTestSDlg::OnOK() 
{
         // TODO: Add extra validation here
         Socket.Create(0);
         SOCKET hSocket = Socket.Detach ();
         AfxBeginThread(Thread,(LPVOID)hSocket,0,0,0,0);
}
&lt;/PRE&gt;&lt;img src ="http://blog.vckbase.com/arong/aggbug/15578.html" width = "1" height = "1" /&gt;</description></item><item><dc:creator>馨荣家园</dc:creator><title>socket 程序设计(4) － 服务端套接字demo1</title><link>http://blog.vckbase.com/arong/archive/2005/11/10/14704.html</link><pubDate>Thu, 10 Nov 2005 07:04:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/11/10/14704.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/14704.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/11/10/14704.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/14704.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/14704.html</trackback:ping><description>根据前面一节的说明，服务端套接字应该按照如下顺序建立： 
&lt;OL type=a&gt;
&lt;LI&gt;初始化 
&lt;LI&gt;创建套接字 
&lt;LI&gt;绑定本地地址 
&lt;LI&gt;进入侦听状态 
&lt;LI&gt;处理接受循环 &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;下面首先创建一个例子来演示服务端套接字的实现，并在以后的各节中优化这个设计。 
&lt;P&gt;这个设计实现的功能如下：允许客户端（实际上就是telnet程序）登陆，并对客户端的输入回显。 
&lt;H2&gt;4.1 准备工程&lt;/H2&gt;
&lt;P&gt;为了实现这个demo，我打算使用Visual Studio 6.0提供的对话框模板来实现，请按照下述步骤准备工程： 
&lt;OL type=a&gt;
&lt;LI&gt;启动Visual C++ 6.0 
&lt;LI&gt;选择File/New/Project 
&lt;LI&gt;选择MFC AppWizard(exe) 
&lt;LI&gt;在Project name中输入demo1, Next 
&lt;LI&gt;选择Dialog Base, Next 
&lt;LI&gt;在Windows Sockets前打钩 
&lt;LI&gt;其他都保持缺省值，点击Next完成向导 
&lt;LI&gt;在工程中加入两个文件：sockutil.h和sockutil.cpp：这两个文件将保存公共的函数，避免以后重复编写。 &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;这样创建的工程会自动加入WinSock2支持，具体的内容包括： 
&lt;OL type=a&gt;
&lt;LI&gt;在stdafx.h中加入#include &amp;lt;afxsock.h&amp;gt;,该头文件包含如下语句载入对应LIB: &lt;BR&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;#pragma comment(lib&lt;/B&gt;&lt;/FONT&gt;, "wsock32.lib") 
&lt;LI&gt;在InitInstance中加入如下代码： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(!AfxSocketInit())
{
      AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
      &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; FALSE;
}
&lt;/PRE&gt;
&lt;LI&gt;在资源中定义字符串资源IDP_SOCKETS_INIT_FAILED:Windows通讯端口初始化失败&lt;/LI&gt;&lt;/OL&gt;
&lt;OL&gt;&lt;/OL&gt;
&lt;P&gt;如果大家创建工程时没有加入对应的sock支持，我们可以参照上述列表手工加入。 
&lt;P&gt;对于前文所述的sockutil.h文件，加入如下初始代码： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;#if&lt;/B&gt;&lt;/FONT&gt; !defined(__SOCKET_UTILITY_HEADER__)
&lt;FONT color=#0000ff&gt;&lt;B&gt;#define&lt;/B&gt;&lt;/FONT&gt; __SOCKET_UTILITY_HEADER__
&lt;FONT color=#0000ff&gt;&lt;B&gt;#if&lt;/B&gt;&lt;/FONT&gt; _MSC_VER &amp;gt; 1000
&lt;FONT color=#0000ff&gt;&lt;B&gt;#pragma&lt;/B&gt;&lt;/FONT&gt; once
&lt;FONT color=#0000ff&gt;&lt;B&gt;#endif&lt;/B&gt;&lt;/FONT&gt; // _MSC_VER &amp;gt; 1000
&lt;FONT color=#0000ff&gt;&lt;B&gt;#endif&lt;/B&gt;&lt;/FONT&gt; 
&lt;/PRE&gt;
&lt;P&gt;对于sockutil.cpp文件，则加入如下初始代码： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; "stdafx.h"
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; "sockutil.h"
&lt;FONT color=#0000ff&gt;&lt;B&gt;#ifdef&lt;/B&gt;&lt;/FONT&gt; _DEBUG
&lt;FONT color=#0000ff&gt;&lt;B&gt;#define&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;new&lt;/B&gt;&lt;/FONT&gt; DEBUG_NEW
&lt;FONT color=#0000ff&gt;&lt;B&gt;#undef&lt;/B&gt;&lt;/FONT&gt; THIS_FILE
&lt;FONT color=#0000ff&gt;&lt;B&gt;static&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt; THIS_FILE[] = __FILE__;
&lt;FONT color=#0000ff&gt;&lt;B&gt;#endif&lt;/B&gt;&lt;/FONT&gt;

&lt;/PRE&gt;
&lt;H2&gt;4.2 准备错误处理方式&lt;/H2&gt;
&lt;P&gt;在编制每个程序之前，我习惯是为错误处理定制一个统一的处理方式。在以后的例子中，我会按照如下的方式处理错误：在一个log文件中记录错误发生的位置（文件、行号)，错误号和字符串解释。为了实现这个目的，我在sockutil.cpp定义如下的函数： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;bool&lt;/B&gt;&lt;/FONT&gt; ErrorHandle(LPCTSTR expression, &lt;FONT color=#0000ff&gt;&lt;B&gt;bool&lt;/B&gt;&lt;/FONT&gt; bFalse, LPCTSTR file, UINT line)
{
    &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(!bFalse)
    {//没有错误，直接返回
     &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;false&lt;/B&gt;&lt;/FONT&gt;;
    }
    FILE * fp;
    fp = fopen("demo.log","at");
    &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(NULL == fp)
    {//如果文件打开失败，放弃记录
     &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;true&lt;/B&gt;&lt;/FONT&gt;;
    }
    //获得错误码
    DWORD ErrorCode = GetLastError();
    //格式化成字符串格式
    LPVOID lpMsgBuf;
    FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        ErrorCode ,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &amp;amp;lpMsgBuf,
        0,
        NULL 
     );
    //输出到文件
    fprintf(fp,_T("file=%s,line=%u\n%d:%s\n"),file,line,ErrorCode, (LPCTSTR)lpMsgBuf);
    // 释放空间，该空间由FormatMessage分配
    LocalFree( lpMsgBuf );
    //关闭文件
    fclose(fp);
 
    &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;true&lt;/B&gt;&lt;/FONT&gt;;
}
&lt;/PRE&gt;
&lt;P&gt;在sockutil.h中，添加如下代码： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;bool&lt;/B&gt;&lt;/FONT&gt; ErrorHandle(LPCTSTR expression, &lt;FONT color=#0000ff&gt;&lt;B&gt;bool&lt;/B&gt;&lt;/FONT&gt; bFalse, LPCTSTR file, UINT line);
&lt;FONT color=#0000ff&gt;&lt;B&gt;#define&lt;/B&gt;&lt;/FONT&gt; ERRORHANDLE(expression) ErrorHandle(#expression,(expression),__FILE__,__LINE__)
&lt;/PRE&gt;
&lt;H2&gt;4.3 核心代码&lt;/H2&gt;
&lt;P&gt;根据前文，设计该服务端套接字主函数如下： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;void&lt;/B&gt;&lt;/FONT&gt; sockmain(LPCTSTR ip, UINT port)
{

   SOCKET hSocket;
   hSocket = socket(AF_INET,SOCK_STREAM,0);
   &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(hSocket == INVALID_SOCKET))
   {
      &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt;;
   }
   sockaddr_in addr;
   InitializeAddress(inet_addr(ip), port, addr);
   &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(SOCKET_ERROR == bind(hSocket, (&lt;FONT color=#0000ff&gt;&lt;B&gt;const&lt;/B&gt;&lt;/FONT&gt; sockaddr*) &amp;amp; addr, &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr))))
   {
      closesocket(hSocket);
      &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt;;
   }
   &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(SOCKET_ERROR == listen(hSocket,5)))
   {
      closesocket(hSocket);
      &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt;;
   }
   SOCKET hClient;
   &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; size;
   &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt; buffer[2048];
   &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; length;
   size = &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr);
   &lt;FONT color=#0000ff&gt;&lt;B&gt;while&lt;/B&gt;&lt;/FONT&gt;(INVALID_SOCKET != (hClient = accept(hSocket,(sockaddr*)&amp;amp;addr, &amp;amp; size)))
   {
     size = &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr);
     &lt;FONT color=#0000ff&gt;&lt;B&gt;while&lt;/B&gt;&lt;/FONT&gt;((length = recv(hClient, buffer, &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(buffer),0)) &amp;gt; 0)
     {
       SendData(hClient,buffer, length);
     }
     closesocket(hClient);
   }
   closesocket(hSocket);

   &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt;;
}
&lt;/PRE&gt;
&lt;P&gt;其中，InitializeAddress定义如下： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;void&lt;/B&gt;&lt;/FONT&gt; InitializeAddress(DWORD ip, UINT port, sockaddr_in &amp;amp; addr)
{
   memset(&amp;amp;addr,0,&lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr));
   addr.sin_family  = AF_INET;
   addr.sin_addr.s_addr= ip;
   addr.sin_port       = htons(port);
}
&lt;/PRE&gt;SendData定义如下： &lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; SendData(SOCKET hSocket, &lt;FONT color=#0000ff&gt;&lt;B&gt;const&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt; * data, &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; length)
{
   &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; result;
   &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; pos = 0;
   &lt;FONT color=#0000ff&gt;&lt;B&gt;while&lt;/B&gt;&lt;/FONT&gt;(pos &amp;lt; length)
   {
      result = send(hSocket, data + pos, length - pos , 0);
      &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(result &amp;gt; 0 )
     {
        pos += result;
     }&lt;FONT color=#0000ff&gt;&lt;B&gt;else&lt;/B&gt;&lt;/FONT&gt;{
        &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; result;
     }
   }
 &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; length;
}
&lt;/PRE&gt;
&lt;H2&gt;4.4 启动该任务&lt;/H2&gt;
&lt;P&gt;为了启动服务端套接字，我们可以在对话框资源的OK按钮上双击，然后在OnOK中添加如下代码 &lt;PRE&gt;   sockmain("0.0.0.0",2000);
&lt;/PRE&gt;
&lt;P&gt;当我们启动该工程，并点击OK按钮，就可以通过telnet来测试是否有回显功能了。&lt;/P&gt;&lt;img src ="http://blog.vckbase.com/arong/aggbug/14704.html" width = "1" height = "1" /&gt;</description></item><item><dc:creator>馨荣家园</dc:creator><title>Demo1 for socket programing(3)</title><link>http://blog.vckbase.com/arong/archive/2005/11/04/14484.html</link><pubDate>Fri, 04 Nov 2005 11:46:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/11/04/14484.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/14484.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/11/04/14484.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/14484.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/14484.html</trackback:ping><description>&lt;H2&gt;sockutil.cpp&lt;/H2&gt;&lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; "stdafx.h"
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; &amp;lt;iostream.h&amp;gt;
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; &amp;lt;winsock2.h&amp;gt;
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; "sockutil.h"
&lt;FONT color=#0000ff&gt;&lt;B&gt;void&lt;/B&gt;&lt;/FONT&gt; ShowError(unsigned &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; nError)
{
    &lt;FONT color=#0000ff&gt;&lt;B&gt;void&lt;/B&gt;&lt;/FONT&gt;* lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        nError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &amp;amp;lpMsgBuf,
        0, NULL );
    
 cout &amp;lt;&amp;lt;"(" &amp;lt;&amp;lt; nError  &amp;lt;&amp;lt; "):" &amp;lt;&amp;lt; lpMsgBuf;
    LocalFree(lpMsgBuf);
}
BOOL ErrorHandle(&lt;FONT color=#0000ff&gt;&lt;B&gt;const&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt; * position, BOOL condition, &lt;FONT color=#0000ff&gt;&lt;B&gt;const&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt; * file, unsigned &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; line)
{
 &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(!condition)
 {
  &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; condition;
 }
 cout &amp;lt;&amp;lt;file &amp;lt;&amp;lt; "(" &amp;lt;&amp;lt; line &amp;lt;&amp;lt; ")" &amp;lt;&amp;lt; endl;
 cout &amp;lt;&amp;lt;position&amp;lt;&amp;lt; endl;
 unsigned &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; nError = GetLastError();
 ShowError(nError);
 &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; condition;
}
&lt;FONT color=#0000ff&gt;&lt;B&gt;void&lt;/B&gt;&lt;/FONT&gt; InitializeAddress(DWORD ip, UINT port, sockaddr_in &amp;amp; addr)
{
 memset(&amp;amp;addr,0,&lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr));
 addr.sin_family  = AF_INET;
 addr.sin_addr.s_addr= ip;
 addr.sin_port       = htons(port);
}
&lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; SendData(SOCKET hSocket, &lt;FONT color=#0000ff&gt;&lt;B&gt;const&lt;/B&gt;&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt; * data, &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; length)
{
 &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; result;
 &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; pos = 0;
 &lt;FONT color=#0000ff&gt;&lt;B&gt;while&lt;/B&gt;&lt;/FONT&gt;(pos &amp;lt; length)
 {
  result = send(hSocket, data + pos, length - pos , 0);
  &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(result &amp;gt; 0 )
  {
   pos += result;
  }&lt;FONT color=#0000ff&gt;&lt;B&gt;else&lt;/B&gt;&lt;/FONT&gt;{
   &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; result;
  }
 }
 &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; length;
}
&lt;/PRE&gt;
&lt;H2&gt;demo1.cpp&lt;/H2&gt;&lt;PRE&gt;// demo1.cpp : Defines the entry point for the console application.
//
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; "stdafx.h"
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; &amp;lt;stdio.h&amp;gt;
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; &amp;lt;winsock2.h&amp;gt;
&lt;FONT color=#0000ff&gt;&lt;B&gt;#pragma&lt;/B&gt;&lt;/FONT&gt; comment(lib,"ws2_32")
&lt;FONT color=#0000ff&gt;&lt;B&gt;#include&lt;/B&gt;&lt;/FONT&gt; "sockutil.h"
&lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; main(&lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; argc, &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt;* argv[])
{
 unsigned &lt;FONT color=#0000ff&gt;&lt;B&gt;short&lt;/B&gt;&lt;/FONT&gt; wVersion;
 WSADATA wsa;
 wVersion = MAKEWORD(2,2);
 &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(WSAStartup(wVersion, &amp;amp;wsa) != 0))
 {
  &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1;
 }
 SOCKET hSocket;
 &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; a = PROTO_ICMP;
 hSocket = socket(AF_INET,SOCK_STREAM,0);
 &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(hSocket == INVALID_SOCKET))
 {
  WSACleanup();
  &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1;
 }
 sockaddr_in addr;
 InitializeAddress(INADDR_ANY, 2000, addr);
 &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(SOCKET_ERROR == bind(hSocket, (&lt;FONT color=#0000ff&gt;&lt;B&gt;const&lt;/B&gt;&lt;/FONT&gt; sockaddr*) &amp;amp; addr, &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr))))
 {
  closesocket(hSocket);
  WSACleanup();
  &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1;
 }
 &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(SOCKET_ERROR == listen(hSocket,5)))
 {
  closesocket(hSocket);
  WSACleanup();
  &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1;
 }
 SOCKET hClient;
 &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; size;
 &lt;FONT color=#0000ff&gt;&lt;B&gt;char&lt;/B&gt;&lt;/FONT&gt; buffer[2048];
 &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; length;
 size = &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr);
 &lt;FONT color=#0000ff&gt;&lt;B&gt;while&lt;/B&gt;&lt;/FONT&gt;(INVALID_SOCKET != (hClient = accept(hSocket,(sockaddr*)&amp;amp;addr, &amp;amp; size)))
 {
  size = &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr);
  &lt;FONT color=#0000ff&gt;&lt;B&gt;while&lt;/B&gt;&lt;/FONT&gt;((length = recv(hClient, buffer, &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(buffer),0)) &amp;gt; 0)
  {
   SendData(hClient,buffer, length);
  }
  closesocket(hClient);
 }
 closesocket(hSocket);
 WSACleanup();
 &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; 0;
}
&lt;/PRE&gt;&lt;img src ="http://blog.vckbase.com/arong/aggbug/14484.html" width = "1" height = "1" /&gt;</description></item><item><dc:creator>馨荣家园</dc:creator><title>socket 程序设计(3) － 服务端套接字</title><link>http://blog.vckbase.com/arong/archive/2005/10/31/14340.html</link><pubDate>Mon, 31 Oct 2005 12:14:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/10/31/14340.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/14340.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/10/31/14340.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/14340.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/14340.html</trackback:ping><description>&lt;P&gt;服务端套接字是编程最简单的一个部分，甚至在各种环境下都可以用类似的代码。对于一个服务端套接字而言，他的基本工作包括：&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;初始化 
&lt;LI&gt;创建套接字 
&lt;LI&gt;给套接字捆绑一个本地地址 
&lt;LI&gt;【可选】设置套接字属性 
&lt;LI&gt;调用listen函数 
&lt;LI&gt;进入accept循环，接受来自客户端的请求 
&lt;LI&gt;对PCS进行管理 
&lt;LI&gt;释放服务端套接字所占用的资源 &lt;/LI&gt;&lt;/OL&gt;
&lt;H2&gt;3.1 初始化&lt;/H2&gt;
&lt;P&gt;对于每个需要处理套接字的程序，在调用任何其他套接字函数之前，必须首先调用&lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsastartup_2.asp"&gt;WSAStartup&lt;/A&gt;函数。如果不调用它，其他函数调用会失败，并返回WSANOTINITIALISED错误码，表示WinSock库还没有初始化。对于除了其他操作系统，初始化的方法会不一样，请参见对应的文档找到初始化方法。&lt;/P&gt;
&lt;P&gt;&lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsastartup_2.asp"&gt;WSAStartup&lt;/A&gt;的函数原型为： &lt;PRE&gt;&lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;int&lt;/FONT&gt;&lt;/STRONG&gt; &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsastartup_2.asp"&gt;WSAStartup&lt;/A&gt;(
  WORD wVersionRequested,
  LPWSADATA lpWSAData
);&lt;/PRE&gt;
&lt;P&gt;其中，各个参数的含义如下： 
&lt;TABLE border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;wVersionRequested: 
&lt;TD&gt;本程序所需要的最低版本号。请注意，MSDN中所说的是本程序可能用到的最高版本的函数，其含义和我所说的一样。&lt;/TD&gt;
&lt;TR&gt;
&lt;TD&gt;lpWSAData 
&lt;TD&gt;返回&lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsadata_2.asp"&gt;WSADATA&lt;/A&gt;结构，说明WinSock库当前的实现细节&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;该函数成功时返回0，否则可能返回如下一些错误码： 
&lt;TABLE border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;WSASYSNOTREADY 
&lt;TD&gt;底层网络系统未准备好 
&lt;TR&gt;
&lt;TD&gt;WSAVERNOTSUPPORTED 
&lt;TD&gt;所要求版本号本实现不支持 
&lt;TR&gt;
&lt;TD&gt;WSAEINPROGRESS 
&lt;TD&gt;一个阻塞性套接字操作未完成 
&lt;TR&gt;
&lt;TD&gt;WSAEPROCLIM 
&lt;TD&gt;达到当前实现所支持的最大任务数 
&lt;TR&gt;
&lt;TD&gt;WSAEFAULT 
&lt;TD&gt;lpWSAData是一个非法指针 &lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;一般用户并不关心实现细节，只要当前实现库满足最低版本需求即可。常见代码为： &lt;PRE&gt;&lt;FONT size=4&gt; WORD wVersion = MAKEWORD(2,2);//最低版本2.2&lt;BR&gt; WSADATA WSAData;
 &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; nResult;
 &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERROR_SUCCESS != (nResult = &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsastartup_2.asp"&gt;&lt;FONT size=3&gt;WSAStartup&lt;/FONT&gt;&lt;/A&gt;(wVersion,&amp;amp;WSAData)))
 {
     ReportError("WSAStartup", nResult);
     &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1;
 }&lt;/FONT&gt;&lt;/PRE&gt;
&lt;H2&gt;3.2 创建套接字&lt;/H2&gt;
&lt;P&gt;每个套接字任务都从创建套接字开始。我们可以用&lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/socket_2.asp"&gt;socket&lt;/A&gt;函数来创建套接字，该函数的原型为： &lt;PRE&gt;SOCKET &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/socket_2.asp"&gt;socket&lt;/A&gt;(
  &lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;int&lt;/FONT&gt;&lt;/STRONG&gt; af,
  &lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;int&lt;/FONT&gt;&lt;/STRONG&gt; type,
  &lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;int&lt;/FONT&gt;&lt;/STRONG&gt; protocol
)
&lt;/PRE&gt;其中，各个参数的含义为： 
&lt;TABLE border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;af&lt;/TD&gt;
&lt;TD&gt;地址族，说明该socket支持的地址类型。我们可以在winsock2.h中找到所支持的地址族。不过一般来说，对于TCP/IP编程，我们都会设置为AF_INET&lt;/TD&gt;
&lt;TR&gt;
&lt;TD&gt;type 
&lt;TD&gt;协议类型，Winsock2.h中列出了5种类型，我们一般会使用其中的三种，SOCK_STREAM表示流协议，SOCK_DGRAM表示数据报协议，SOCK_RAW表示原始套接字。我会在数据传输部分详细解释这些内容&lt;/TD&gt;
&lt;TR&gt;
&lt;TD&gt;protocol&lt;/TD&gt;协议号，说明该套接字所处理的协议。他的可选值随前面两个参数不同而不同。似乎在RAW协议中用的比较多，大家可以在ROUTPROT.h中找到类似定义。&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;用于侦听的套接字需要是流套接字，下面代码会创建这样的套接字： &lt;PRE&gt;    SOCKET hSocket;
    &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; a = PROTO_ICMP;
    hSocket = &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/socket_2.asp"&gt;socket&lt;/A&gt;(AF_INET,SOCK_STREAM,0);
    &lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(hSocket == INVALID_SOCKET))
    {
          WSACleanup();
          &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1;
    }
&lt;/PRE&gt;
&lt;H2&gt;3.3 捆绑本地地址&lt;/H2&gt;
&lt;P&gt;每个套接字必须有一个地址才能和对方通讯。&lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/bind_2.asp"&gt;bind&lt;/A&gt;函数用于捆绑地址，它的函数原型为： &lt;PRE&gt;&lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;int&lt;/FONT&gt;&lt;/STRONG&gt; &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/bind_2.asp"&gt;bind&lt;/A&gt;(
  SOCKET s,
  &lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;const&lt;/FONT&gt;&lt;/STRONG&gt; &lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;struct&lt;/FONT&gt;&lt;/STRONG&gt; sockaddr* name,
  &lt;STRONG&gt;&lt;FONT color=#0000ff size=4&gt;int&lt;/FONT&gt;&lt;/STRONG&gt; namelen
);
&lt;/PRE&gt;
&lt;P&gt;各个参数含义如下：&lt;BR&gt;
&lt;TABLE border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;s 
&lt;TD&gt;套接字号&lt;/TD&gt;
&lt;TR&gt;
&lt;TD&gt;name 
&lt;TD&gt;地址信息：需要注意的是，套接字接口是给多种协议共享的，sockaddr结构只是一个占位符，不同协议使用不同的地址结构，例如，TCP/IP编程使用的结构是sockaddr_in&lt;/TD&gt;
&lt;TR&gt;
&lt;TD&gt;namelen&lt;/TD&gt;
&lt;TD&gt;地址结构的长度，加入这个参数的原因也就是因为不同协议有不同的结构&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;需要注意的，bind函数只能给socket绑定本机的IP地址，如果你给出的地址信息是其他机器的，则必然会失败。此时该函数返回SOCKET_ERROR，WSAGetLastError则返回WSAEADDRNOTAVAIL。 
&lt;P&gt;如果没有特殊需求，应该设置该结构的IP地址为INADDR_ANY。对于端口，服务端套接字需要指定一个端口，而客户端端口最好设置为0，让系统选择一个可用端口。 
&lt;P&gt;下面代码初始化一个地址结构：&lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;void&lt;/B&gt;&lt;/FONT&gt; InitializeAddress(DWORD ip, UINT port, sockaddr_in &amp;amp; addr)&lt;BR&gt;{ &lt;BR&gt;      memset(&amp;amp;addr,0,&lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr)); &lt;BR&gt;      addr.sin_family  = AF_INET; &lt;BR&gt;      addr.sin_addr.s_addr= ip; &lt;BR&gt;      addr.sin_port       = htons(port);&lt;BR&gt;}&lt;/PRE&gt;
&lt;P&gt;下面代码则把一个套接字绑定到本机2000端口上：&lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(SOCKET_ERROR == &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/bind_2.asp"&gt;bind&lt;/A&gt;(hSocket, (&lt;FONT color=#0000ff&gt;&lt;B&gt;const&lt;/B&gt;&lt;/FONT&gt; sockaddr*) &amp;amp; addr, &lt;FONT color=#0000ff&gt;&lt;B&gt;sizeof&lt;/B&gt;&lt;/FONT&gt;(addr)))) &lt;BR&gt;{  &lt;BR&gt;     closesocket(hSocket);  &lt;BR&gt;     WSACleanup();  &lt;BR&gt;     &lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1; &lt;BR&gt;}&lt;/PRE&gt;
&lt;H2&gt;3.4 进入侦听状态&lt;/H2&gt;
&lt;P&gt;当我们创建一个套接字，并绑定了地址后，我们需要设置这个套接字进入侦听状态，进入侦听状态后，该套接字就可以处理来自客户端的链接请求。&lt;/P&gt;
&lt;P&gt;listen函数设置套接字进入侦听状态，其函数原型为：&lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/listen_2.asp"&gt;listen&lt;/A&gt;(
  SOCKET s,
  &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt; backlog
);&lt;/PRE&gt;
&lt;P&gt;对于刚开始编程的人，最容易误解的是backlog参数。许多人以为这就是该套接字最多能接收的链接的数目。实际上，一个套接字能接受的链接的数目不受这个参数控制，它只受系统资源的限制。例如对于linux，套接字用文件句柄实现，那么它可能受最大文件句柄数的限制。 
&lt;P&gt;这个参数的含义是最多未决连接的数目，也就是连接请求已经到了服务端套接字，但是用户还没有调用accept的套接字数目。对于WinSock2，这个参数最大值为5。 
&lt;P&gt;下面示例代码说明了如何调用listen：&lt;PRE&gt;&lt;FONT color=#0000ff&gt;&lt;B&gt;if&lt;/B&gt;&lt;/FONT&gt;(ERRORHANDLE(SOCKET_ERROR == &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/listen_2.asp"&gt;listen&lt;/A&gt;(hSocket,5))) 
{  
      closesocket(hSocket);  
      WSACleanup();  
&lt;FONT color=#0000ff&gt;&lt;B&gt;return&lt;/B&gt;&lt;/FONT&gt; -1; 
}&lt;/PRE&gt;
&lt;H2&gt;3.5 accept循环&lt;/H2&gt;
&lt;P&gt;当一个套接字处于侦听状态以后，我们就可以循环调用&lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/accept_2.asp"&gt;accept&lt;/A&gt;来接受新连接。accept函数的原型如下：&lt;PRE&gt;SOCKET &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/accept_2.asp"&gt;accept&lt;/A&gt;(
  SOCKET s,
  &lt;FONT color=#0000ff&gt;&lt;B&gt;struct&lt;/B&gt;&lt;/FONT&gt; sockaddr* addr,
  &lt;FONT color=#0000ff&gt;&lt;B&gt;int&lt;/B&gt;&lt;/FONT&gt;* addrlen
);
&lt;/PRE&gt;
&lt;P&gt;这个函数的参数说明和前面bind的一样。需要说明的是，在MSDN中说后面两个参数都是out参数，经过我的测试，结论并不一样。对于addrlen参数，应该是一个in/out参数，也就是说，如果第二个参数是一个结构指针，则第三个参数必须是一个整型变量的指针，该整型变量还必须被设置为该结构的长度。 
&lt;H2&gt;3.6 PCS管理&lt;/H2&gt;
&lt;P&gt;由于一个一个服务端套接字可能接收无数个PCS，如何管理这些PCS就成为一个问题。不同的程序员有自己不同的管理方式，在此我就不准备细讲了。&lt;/P&gt;&lt;img src ="http://blog.vckbase.com/arong/aggbug/14340.html" width = "1" height = "1" /&gt;</description></item><item><dc:creator>馨荣家园</dc:creator><title>关于socket编程的几点说明</title><link>http://blog.vckbase.com/arong/archive/2005/10/06/12914.html</link><pubDate>Thu, 06 Oct 2005 03:52:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/10/06/12914.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/12914.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/10/06/12914.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/12914.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/12914.html</trackback:ping><description>1。我这个人缺点就是不爱看书，不愿意学习先进知识。因此这里的东西都是经验总结而已，有可能有错误或者比较落后。建议有高追求的读者在看完后看看ACE等成熟的平台&lt;BR&gt;2。本人提供的代码都是简单测试即写出，因此不保证在您的系统中能够运行，也不提供后续的调试支持。&lt;BR&gt;3。由于我个人也比较忙，因此可能提供文章的过程不够连续，所以希望大家耐心。&lt;BR&gt;&lt;BR&gt;如果其中有任何错误，请把错误信息发送给&lt;A href="mailto:arongustc@hotmail.com"&gt;arongustc@hotmail.com&lt;/A&gt;。在本站留言可能会被我忽略。但是请不要发邮件去讨论您编程中的问题，因为实在没有时间回复各位。&lt;BR&gt;&lt;BR&gt;谢谢。&lt;img src ="http://blog.vckbase.com/arong/aggbug/12914.html" width = "1" height = "1" /&gt;</description></item><item><dc:creator>馨荣家园</dc:creator><title>socket 程序设计(2) － 地址</title><link>http://blog.vckbase.com/arong/archive/2005/10/06/12913.html</link><pubDate>Thu, 06 Oct 2005 03:47:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/10/06/12913.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/12913.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/10/06/12913.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/12913.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/12913.html</trackback:ping><description>&lt;A href="http://blog.vckbase.com/arong/archive/2005/10/06/12914.html"&gt;说明&lt;/A&gt;&amp;nbsp;&lt;A href="http://blog.vckbase.com/arong/archive/2005/10/02/12768.html"&gt;前一篇&lt;/A&gt; 
&lt;P&gt;&lt;BR&gt;所有的WinSock函数都使用sockaddr结构来传递地址信息，该结构定义如下： 
&lt;P&gt;&lt;PRE&gt;struct sockaddr {&lt;BLOCKQUOTE&gt;
u_short sa_family; /* address family */
char    sa_data[14];/* up to 14 bytes of direct address */
&lt;/BLOCKQUOTE&gt;};&lt;/PRE&gt;
&lt;P&gt;需要注意的是，socket并不只是为TCP服务的，它支持多种协议，而各种协议的地址格式又大相径庭。因此，我们在socket相关的API中不可能用同样的地址结构来描述地址信息，这里的sockaddr只是一个占位符的角色，我们在实际编程中必须替换乘合适的地址类型。&lt;/P&gt;
&lt;P&gt;对于TCP/IP族，我们需要用的结构类型是sockaddr_in，该结构定义如下： &lt;PRE&gt;struct sockaddr_in {&lt;BLOCKQUOTE&gt;
short   sin_family;
u_short sin_port;
struct  in_addr sin_addr;
char    sin_zero[8];&lt;/BLOCKQUOTE&gt;
};
&lt;/PRE&gt;其中in_addr定义为： &lt;PRE&gt;struct in_addr {
        union {
                struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
                struct { u_short s_w1,s_w2; } S_un_w;
                u_long S_addr;
        } S_un;
#define s_addr  S_un.S_addr
/* can be used for most tcp &amp;amp; ip code */
#define s_host  S_un.S_un_b.s_b2
/* host on imp */
#define s_net   S_un.S_un_b.s_b1
/* network */
#define s_imp   S_un.S_un_w.s_w2
/* imp */
#define s_impno S_un.S_un_b.s_b4
/* imp # */
#define s_lh    S_un.S_un_b.s_b3
/* logical host */
};
&lt;/PRE&gt;
&lt;P&gt;需要注意的是，这个sockaddr_in结构和sockaddr结构尺寸一样，我不清楚这是巧合还是必须遵守的一个准则。在我看来，这个结构不一样也是可以的。 
&lt;P&gt;我不打算一一介绍各个宏和域的含义，因为绝大多数人都不会记住这个。我只想简要介绍一下如何初始化这个地址信息&lt;/P&gt;
&lt;OL type=1&gt;
&lt;LI&gt;sa_family：这个域描述了地址族信息。对于TCP/IP，这个值必须设置为AF_INET。有兴趣的朋友可以到WinSock.H中找找看它还支持哪些值。 
&lt;LI&gt;sin_port：端口号，对于我们提供的端口号，必须用htons转换一下再赋值，方法是：&lt;BR&gt;addr.sin_port = htons(port); 
&lt;LI&gt;sin_addr：地址，这里应该是IP地址。我们可以用inet_addr函数从点分式IP地址转换得到这个IP地址，方法是：&lt;BR&gt;addr.sin_addr.s_addr = inet_addr("100.101.102.103");&lt;BR&gt;注意：这里使用了宏s_addr，该宏的定义请参考上文。 
&lt;LI&gt;sin_zero：填充信息，必须设置为0。初学编程的人往往忘记初始化这个域，根据我的经验，这会导致函数调用失败。 &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;由于API中都使用sockaddr结构，因此在使用时必须进行强制类型转换，并提供结构尺寸信息。下面就是accept函数调用时的例子： &lt;PRE&gt;sockaddr_in addr;
memset(&amp;amp;addr, 0, sizeof(addr));
addr.sa_family = AF_INET;
int size = sizeof(addr);
SOCKET sd = accept(serversd, (sockaddr*)&amp;amp;addr, &amp;amp;size);
&lt;/PRE&gt;&lt;img src ="http://blog.vckbase.com/arong/aggbug/12913.html" width = "1" height = "1" /&gt;</description></item><item><dc:creator>馨荣家园</dc:creator><title>socket 程序设计(1) － 开篇：主机的角色</title><link>http://blog.vckbase.com/arong/archive/2005/10/02/12768.html</link><pubDate>Sun, 02 Oct 2005 15:06:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/10/02/12768.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/12768.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/10/02/12768.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/12768.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/12768.html</trackback:ping><description>&lt;A href="http://blog.vckbase.com/arong/archive/2005/10/06/12914.html"&gt;说明&lt;/A&gt; &lt;A href="http://blog.vckbase.com/arong/archive/2005/10/06/12913.html"&gt;下一篇&lt;/A&gt; &lt;BR&gt;&lt;I&gt;由于我对TCP稍微熟悉，所以本系列文章只描述socket（套接字）接口的TCP编程&lt;/I&gt; &lt;BR&gt;
&lt;P&gt;在IT领域中，大家经常听说Client/Server的概念，这个概念是描述了提供服务的一方（服务器）和接受服务的一方（客户端）之间的拓扑结构。举例来说，&lt;A href="http://www.vckbase.com/"&gt;http://www.vckbase.com&lt;/A&gt;这个网站提供大家交流和娱乐的服务，他们的计算机对于我们而言就是服务器，而我们的计算机则是客户端。一般而言，服务器功能比较强大，一个服务器能提供服务给多个客户端。 
&lt;P&gt;对于socket设计而言，我们往往也提到一个服务器端和客户端的概念，但是它的概念和前面而言基本是不一样的。这里的概念其实是指在开始通讯时谁先发起连接的过程。&lt;/P&gt;
&lt;P&gt;对于通讯的双方而言，存在一个建立对话通道的过程，该过程的建立必然是一方主动发起的。我们称呼主动建立链路的一方为客户端，而另外一方为服务器端。(至少我个人这么称呼，不知道业界是不是都这么说)&lt;/P&gt;
&lt;P&gt;在通讯过程中，服务器端必须在某个预先约定的端口等待其他主机建立链路，这个过程称为侦听(Listen)。客户端主动建立链路的过程成为连接(Connect)。当服务器端套接字接到一个连接请求后，会生成一个新的套接字。服务器端主机就通过这个套接字和客户端主机通讯。&lt;/P&gt;
&lt;P&gt;在整个过程中，涉及到三个套接字，他们分别是服务器端套接字（我一般称呼它为server socket)、客户端套接字（一般我称呼它为client socket）和服务器端套接字创建出来的套接字。在编程过程中，第三种套接字除了不会主动建立链路外，功能和client socket完全一致，因此我也把它称为client socket。为了区别，对于客户端的client socket，大家可以称呼它为主动client socket(Active Client Socket, ACS)，而对服务器端的那个客户端套接字，则称为被动client socket(Passive Client Socket, PCS)。一般情况下，我们没有必要区分ACS和PCS，因为PCS的功能是ACS的一个部分，ACS只多一项功能而已。在后文中，我将不区分这两种client socket&lt;/P&gt;
&lt;P&gt;对于server socket, 它有如下功能： 
&lt;OL type=1&gt;
&lt;LI&gt;等待客户端建立连接(listen) 
&lt;LI&gt;当客户端建立发送连接请求时，能接受请求，完成链路建立(accept) 
&lt;LI&gt;关闭(close) &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;对于client socket，它有如下功能: 
&lt;OL type=1&gt;
&lt;LI&gt;建立链路(connect) 
&lt;LI&gt;发送数据(send) 
&lt;LI&gt;接收数据(recv) 
&lt;LI&gt;关闭(close) &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;一个简单的通讯过程应该这样： 
&lt;UL&gt;
&lt;LI&gt;server socket开始侦听(listen) 
&lt;LI&gt;client socket开始连接(connect) 
&lt;LI&gt;server socket接受连接(accept) 
&lt;LI&gt;ACS和PCS开始互相发送数据(send/recv) 
&lt;LI&gt;ACS或PCS关闭链路(close) 
&lt;LI&gt;server socket关闭(close) &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;本系列将根据以上过程介绍socket编程的方方面面。由于阻塞套接字相对简单，我会先从阻塞套接字（也就是伯可力套接字）编程开始，之间会穿插一些多线程的知识。很多方面我也没用过，所以有些东西只能边试边写，和大家探讨。&lt;/P&gt;&lt;img src ="http://blog.vckbase.com/arong/aggbug/12768.html" width = "1" height = "1" /&gt;</description></item><item><dc:creator>馨荣家园</dc:creator><title>同步、异步、阻塞和非阻塞</title><link>http://blog.vckbase.com/arong/archive/2005/07/24/9970.html</link><pubDate>Sat, 23 Jul 2005 16:32:00 GMT</pubDate><guid>http://blog.vckbase.com/arong/archive/2005/07/24/9970.html</guid><wfw:comment>http://blog.vckbase.com/arong/comments/9970.html</wfw:comment><comments>http://blog.vckbase.com/arong/archive/2005/07/24/9970.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://blog.vckbase.com/arong/comments/commentRss/9970.html</wfw:commentRss><trackback:ping>http://blog.vckbase.com/arong/services/trackbacks/9970.html</trackback:ping><description>&lt;img src ="http://blog.vckbase.com/arong/aggbug/9970.html" width = "1" height = "1" /&gt;</description></item></channel></rss>