??xml version="1.0" encoding="utf-8" standalone="yes"?>精品无码久久久久久尤物,久久天堂电影网,9191精品国产免费久久http://www.shnenglu.com/amyvmiwei/category/6011.html 战胜自己是战胜一? zh-cnWed, 16 Mar 2011 04:37:48 GMTWed, 16 Mar 2011 04:37:48 GMT60[转]Flex Socket ?C++ 通讯 --- 安全沙箱问题解决http://www.shnenglu.com/amyvmiwei/archive/2011/03/16/141942.html不?/dc:creator>不?/author>Wed, 16 Mar 2011 02:22:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2011/03/16/141942.htmlhttp://www.shnenglu.com/amyvmiwei/comments/141942.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2011/03/16/141942.html#Feedback0http://www.shnenglu.com/amyvmiwei/comments/commentRss/141942.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/141942.htmlFlex Socket ?C++ 通讯 --- 安全沙箱问题解决

最q一个项目的客户端要Ҏ(gu)FlexQ用Socket与C++通讯旉C安全沙箱问题Q这是我的解x法;

1):{略文g与主套接字在同一端口Q只需调用 Socket.connect() ?XMLSocket.connect() Ҏ(gu)Q?/p>

2):{略文g与主套接字在不同端口Q需使用Ҏ(gu)?#8220;xmlsocket”语法调用 Security.loadPolicyFile() Ҏ(gu)Q如下所C:(x)

Security.loadPolicyFile("xmlsocket://server.com:2525");

先调?Security.loadPolicyFile() Ҏ(gu)Q然后再调用 Socket.connect() ?XMLSocket.connect() Ҏ(gu)?/p>

试代码Q用同一端口

view plaincopy to clipboardprint?
#include <winsock2.h>  
#include <windows.h>  
#include <iostream>  
using namespace std;  
#pragma comment(lib,"ws2_32.lib")  

void main()  
{   
    WORD wVersionRequested;  
    WSADATA wsaData;  
    int err;  
    short port=1800;//端口?nbsp; 
      
    wVersionRequested = MAKEWORD( 1, 1 );  
    err = WSAStartup( wVersionRequested, &wsaData );//初始化套接字  
    if ( err != 0 )  
    {  
        return;  
    }  
      
    if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )  
    {  
        WSACleanup( );  
        return;  
    }  
      
    SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创徏套接?nbsp; 
    SOCKET sockConn;//用来和客L(fng)通信的套接字  
    SOCKADDR_IN addrSrv;//用来和客L(fng)通信的套接字地址  
    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);  
    addrSrv.sin_family=AF_INET;  
    addrSrv.sin_port=htons(port);  
      
    bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//l定端口  
    listen(sockSrv,5);//侦听  
      
    printf("Server %d is listening......\n",port);  
      
    SOCKADDR_IN addrClient;  
      
    int len=sizeof(SOCKADDR);  
    char buf[4096];//接收的数?nbsp; 
    char rbuf[100]=  
        "<cross-domain-policy> "    
        "<allow-access-from domain=\"*\" to-ports=\"*\"/>"    
        "</cross-domain-policy> ";//套接字策略文?nbsp; 
      
    while(1)  
    {  
        //接受q接  
        sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);  
        printf("Accept connection from %s\n",inet_ntoa(addrClient.sin_addr));  
          
recv:  
        //接收数据  
        int bytes;  
        if((bytes=recv(sockConn,buf,sizeof(buf),0))==SOCKET_ERROR)  
        {  
            printf("接收数据p|!\n");  
            exit(-1);  
        }  

        buf[bytes]='\0';  
        printf("Message from %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);       

        if (0 == strcmp(buf,"<policy-file-request/>"))  
        {  
            //发送数?nbsp; 
            if(send(sockConn,rbuf,strlen(rbuf)+1,0)==SOCKET_ERROR)  
            {  
                printf("发送数据失败!");  
                exit(-1);  
            }  
            printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),rbuf);  
        }  
        else
        {  
            //Echo  
            if(send(sockConn,buf,strlen(buf)+1,0)==SOCKET_ERROR)  
            {  
                printf("发送数据失败!");  
                exit(-1);  
            }  
            printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);  
            goto recv;  
        }  
        //清理套接字占用的资源  
        closesocket(sockConn);  
    }  
}
#include <winsock2.h>
#include <windows.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
short port=1800;//端口?br>
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );//初始化套接字
if ( err != 0 )
{
   return;
}

if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
{
   WSACleanup( );
   return;
}

SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创徏套接?br>SOCKET sockConn;//用来和客L(fng)通信的套接字
SOCKADDR_IN addrSrv;//用来和客L(fng)通信的套接字地址
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(port);

bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//l定端口
listen(sockSrv,5);//侦听

printf("Server %d is listening......\n",port);

SOCKADDR_IN addrClient;

int len=sizeof(SOCKADDR);
char buf[4096];//接收的数?br>char rbuf[100]=
   "<cross-domain-policy> "
   "<allow-access-from domain=\"*\" to-ports=\"*\"/>"
   "</cross-domain-policy> ";//套接字策略文?br>
while(1)
{
        //接受q接
   sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
   printf("Accept connection from %s\n",inet_ntoa(addrClient.sin_addr));
  
recv:
   //接收数据
   int bytes;
   if((bytes=recv(sockConn,buf,sizeof(buf),0))==SOCKET_ERROR)
   {
    printf("接收数据p|!\n");
    exit(-1);
   }

   buf[bytes]='\0';
   printf("Message from %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);  

   if (0 == strcmp(buf,"<policy-file-request/>"))
   {
    //发送数?br>    if(send(sockConn,rbuf,strlen(rbuf)+1,0)==SOCKET_ERROR)
    {
     printf("发送数据失败!");
     exit(-1);
    }
    printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),rbuf);
   }
   else
   {
    //Echo
    if(send(sockConn,buf,strlen(buf)+1,0)==SOCKET_ERROR)
    {
     printf("发送数据失败!");
     exit(-1);
    }
    printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);
    goto recv;
   }
        //清理套接字占用的资源
        closesocket(sockConn);
}
}

 

无论是哪U情况,服务器均必须{待客户端的W一ơ传输之后再军_是发送策略文件还是徏立主q接。当 Flash Player h{略文gӞ它始l会(x)在徏立连接后传输以下字符Ԍ(x)

<policy-file-request/>
服务器收到此字符串后Q即?x)传输该{略文g。程序对于策略文件请求和主连接ƈ不会(x)使用同一q接Q因此应在传输策略文件后关闭q接。如果不关闭q接QFlash Player 关闭策略文件连接,之后重新q接以徏立主q接?/p>

附网l资料:(x)

1,首先目标服务器?43端口是否提供安全{略
2,如果1没有到{略Q则actionscript是否使用了Security.loadPolicyFile(xmlsocket://) 手段提供安全{略Q如果还没检到Q则使用W?步检?br>3,目标服务器目标端口是否提供安全{略

 

本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/xuxiangwin/archive/2009/07/07/4324218.aspx



]]>
【{】完成端口的一个简单封装类 http://www.shnenglu.com/amyvmiwei/archive/2008/01/18/41439.html不?/dc:creator>不?/author>Fri, 18 Jan 2008 13:29:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2008/01/18/41439.htmlhttp://www.shnenglu.com/amyvmiwei/comments/41439.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2008/01/18/41439.html#Feedback3http://www.shnenglu.com/amyvmiwei/comments/commentRss/41439.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/41439.html/////////////////////////////////////////////////////////////////////////////////////

//   Iocp 头文?/span>

 

#pragma once

 

#include <winsock2.h>

#pragma comment( lib, "ws2_32.lib" )

 

const int OP_READ = 0;

const int OP_WRITE = 1;

const int OP_ACCEPT = 2;

 

/*

     OVERLAPPEDPLUS l构体设计思\

     OVERLAPPED 是一个固定的用于处理|络消息事gq回值的l构体变?/span>

     在完成端口和重叠I/O模型里用于返回消息事件的l果

     因ؓ(f)在处理网l消息的时候,发送的是一个返回值的l构体指针,只要l构?/span>

     的前面部分满系l的要求Q在pȝ操作成功的时候也׃(x)把这个结构体指针

     发回l用P我们只要在系l定义的l构体后面扩展一些自q东西Q就可以

     很轻杄定该消息是谁发q来的?/span>

     不过好像完成端口在设计的时候也满了这L(fng)需求,所以在q里我只是放?/span>

     一些与pȝq接有关的数据,用户需要存攄数据q里׃在存?/span>

     q里存储与系l相关的数据有:(x)

     socket

     OpCode 本次消息的操作类型(在完成端口的操作里面Q是以消息通知pȝQ?/span>

         L?/span>/写数据,都是要发q样的消息结构体q去的,所以如果系l要同时

         q行d操作的话Q就需要有一个变量来区分操作了)

 

     WSABUF   wbuf;                  //   d~冲区结构体变量

     DWORD    dwBytes, dwFlags; //   一些在d时用到的标志性变?/span>

     char buf[4096];                  //   自己的缓冲区

     上面?/span>4个变量存攄是一些与消息相关的数据,都是一些操作上用到的,

     q些东西都是固定的,具体作用需要参考一下完成端口相兛_数的参数接口

*/

struct OVERLAPPEDPLUS

{

     OVERLAPPED    ol;

     SOCKET        s;

     int OpCode;

     WSABUF   wbuf;

     DWORD    dwBytes, dwFlags;

     char buf[4096];

};

 

class CIOCP

{

protected:

     HANDLE g_hwThread;     //   工作U程句柄

     DWORD m_wthreadID;

     HANDLE g_haThread;     //   q接U程句柄

     DWORD m_athreadID;

public:

     bool m_workThread;

     bool m_acceptThread;

     HANDLE m_hIocp;             //   完成端口的句?/span>

     SOCKET m_sSocket;

    

public:

     CIOCP(void);

     ~CIOCP(void);

     virtual void OnRead(void * p, char *buf, int len){};

     virtual void OnAccept(SOCKET socket);

     virtual void OnClose(void * p){};

     bool SetIoCompletionPort(SOCKET socket, void *p, char *buf = NULL, int len = 0);

         //   把一?/span>socket与一个自定义的结构体兌到完成端口(相当于把socket与一个结构体变量q行l定Q,

         //   q样当发送上?/span>3U网l事件的时候,该结构体变量?x)再传回l程?/span>

         //   q样可以区分当前网l事件是那个socket发出?/span>

     bool Init(void);

     bool Listen(int port);

     static DWORD __stdcall WorkThread(LPVOID Param);

     static DWORD __stdcall AcceptThread(LPVOID Param);

};

 

class CIOCPClient: public CIOCP

{

protected:

     SOCKET m_socket;

public:

     bool Connect(char *ip, int port);

     void Send(char *buf, int len);

};

 

 

 

 

 

 

 

 

 

 

 

//////////////////////////////////////////////////////////////////////////////////////////

//   Iocp 实现文g

 

#include "StdAfx.h"

#include "iocp.h"

 

static bool bInit = false;

 

DWORD __stdcall CIOCP::WorkThread(LPVOID Param)

{

     CIOCP * pthis = (CIOCP *)Param;

 

     void * re;

     OVERLAPPED * pOverlap;

     DWORD berByte;

     while(pthis->m_workThread)

     {

         int ret;

         ret = GetQueuedCompletionStatus(pthis->m_hIocp, &berByte, (LPDWORD)&re, (LPOVERLAPPED *)&pOverlap, INFINITE);

 

         if (ret == ERROR_SUCCESS)

         {

 

         }

 

         if (berByte == 0)

         {

              //   客户端断开q接

              pthis->OnClose(re);

              OVERLAPPEDPLUS *olp = (OVERLAPPEDPLUS *)pOverlap;

              closesocket(olp->s);

              delete olp;        //   释放 ?/span>socketl定的结构体变量

              continue;

         }

 

         if (re == NULL) return 0;

 

         OVERLAPPEDPLUS *olp = (OVERLAPPEDPLUS *)pOverlap;

 

         switch(olp->OpCode)

         {

         case OP_READ:

              pthis->OnRead(re, olp->wbuf.buf, berByte);     //   调用 OnRead() 通知应用E序Q服务器收到来自客户端的|络数据

              WSARecv(olp->s, &olp->wbuf, 1, &olp->dwBytes, &olp->dwFlags, &olp->ol, NULL); //   l箋调用一个接收的 I/O 异步h

              break;

         default:

              break;

         }

     }

     return 0;

}

 

DWORD __stdcall CIOCP::AcceptThread(LPVOID Param)

{

     CIOCP * pthis = (CIOCP *)Param;

     while(pthis->m_acceptThread)

     {

         SOCKET client;

         if ((client= accept(pthis->m_sSocket, NULL, NULL)) == INVALID_SOCKET)

         {

              //   错误处理

         }

         pthis->OnAccept(client);    //   调用 OnAccept()通知应用E序有新客户端连?/span>

        

     }

     return 1;

}

 

CIOCP::CIOCP(void)

{

}

 

CIOCP::~CIOCP(void)

{

}

 

bool CIOCP::Init(void)

{

     if (bInit)

         return true;

 

     WSADATA wsd;

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

         return false;

 

     bInit = true;

     return true;

}

 

bool CIOCP::Listen(int port)

{

     if (!bInit)

         if (!Init())

              return false;

 

     m_sSocket = socket(AF_INET, SOCK_STREAM, 0);

 

     if (m_sSocket == INVALID_SOCKET)

         return false;

 

     //SOCKADDR_IN addr;

     sockaddr_in addr;

     addr.sin_family = AF_INET;

     addr.sin_port = htons(port);

     addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

     //addr.sin_addr.S_un.S_addr = inet_addr(ip);

 

     if (bind(m_sSocket, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)

         return false;

 

     if (listen(m_sSocket, 10) == SOCKET_ERROR)

         return false;

 

     if ((m_hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0)) == NULL)     //   创徏完成端口的句?/span>

         return false;

 

     this->m_acceptThread = true;

     g_haThread = CreateThread(NULL, 0, AcceptThread, (LPVOID)this, 0, &m_athreadID);    //   创徏q接U程Q用来接收客L(fng)的连?/span>

 

     this->m_workThread = true;

     g_hwThread = CreateThread(NULL, 0, WorkThread, (LPVOID)this, 0, &m_wthreadID); //   创徏工作U程Q用来处理完成端口消息的

     return true;

}

 

bool CIOCP::SetIoCompletionPort(SOCKET socket, void *p, char *buf, int len)

{

     if (CreateIoCompletionPort((HANDLE)socket, m_hIocp, (ULONG_PTR)p, 0) == NULL)

         return false;

 

     OVERLAPPEDPLUS *olp = new OVERLAPPEDPLUS;

     memset(olp, 0, sizeof(OVERLAPPEDPLUS));

     olp->s = socket;

     if (buf)

     {

         //   q里可以使用用户自定义的~冲区地址Q如果用户不惌|,也可以采用默认分配的~冲?/span>

         olp->wbuf.buf = buf;

         olp->wbuf.len = len;

     }

     else

     {

         olp->wbuf.buf = olp->buf;

         olp->wbuf.len = 4096;

     }

     olp->OpCode = OP_READ;

     int ret = WSARecv(olp->s, &olp->wbuf, 1, &olp->dwBytes, &olp->dwFlags, &olp->ol, NULL);

     if (ret == SOCKET_ERROR)

         if (WSAGetLastError() != ERROR_IO_PENDING)

              return false;

     return true;

}

 

void CIOCP::OnAccept(SOCKET socket)

{

     this->SetIoCompletionPort(socket, NULL);

}

 

//===================================================================================

bool CIOCPClient::Connect(char *ip, int port)

{

         //   q接服务?/span>

     if (!bInit)

         if (!Init())

              return false;

 

     //   初始化连?/span>socket

     m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

     if (m_socket == SOCKET_ERROR)

     {

//       printf("cocket Create fail");

         return false;

     }

 

     // 填写服务器地址信息

     // 端口?/span>1982

     // IP地址?/span>INADDR_ANYQ注意?/span>htonl?/span>IP地址转换为网l格?/span>ServerAddr.sin_family = AF_INET;

     sockaddr_in ClientAddr;

     ClientAddr.sin_family = AF_INET;

     ClientAddr.sin_port = htons(port);   

     ClientAddr.sin_addr.s_addr = inet_addr(ip);

 

     // l定监听端口

     bind(m_socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr));

 

     if (connect(m_socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR)

     {

         return false;

     }

    

     if ((m_hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0)) == NULL)     //   创徏完成端口的句?/span>

         return false;

 

     this->m_workThread = true;

     g_hwThread = CreateThread(NULL, 0, WorkThread, (LPVOID)this, 0, &m_wthreadID); //   创徏工作U程Q用来处理完成端口消息的

 

     this->SetIoCompletionPort(m_socket, &m_socket);    //   讄完成端口监听?/span>socket

     return true;

}

 

void CIOCPClient::Send(char *buf, int len)

{

     send(m_socket, buf, len, 0);

}

 

 

///////////////////////////////////////////////////////////////////////////////////

// IOCPclient 应用代码

 

#include "stdafx.h"

#include "IOCP.h"

#include "TClientSocket.h"

class Iocp :public CIOCPClient

{

      void OnRead(void * p, char *buf, int len)

      {

          printf(buf);

          Sleep(1000);

          this->Send(buf, len);

      }

};

 

int _tmain(int argc, _TCHAR* argv[])

{

     Iocp iocp;

     iocp.Init();

     iocp.Connect("127.0.0.1", 4311);

     iocp.Send("test\0", 5);

    

     gets(new char[1000]);

     return 0;

}



]]>
【{】完成端口与高性能服务器程序开?http://www.shnenglu.com/amyvmiwei/archive/2008/01/18/41438.html不?/dc:creator>不?/author>Fri, 18 Jan 2008 13:27:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2008/01/18/41438.htmlhttp://www.shnenglu.com/amyvmiwei/comments/41438.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2008/01/18/41438.html#Feedback0http://www.shnenglu.com/amyvmiwei/comments/commentRss/41438.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/41438.html早在两年前我已l能很熟l的q用完成端口q种技术了,只是一直没有机?x)将它用在什么项目中,q段旉见到q种技术被q分炒作,q分的神U化,想写一解释它如何工作的文?惛_诉大家它没有传说中的那么高深难懂!有什么错误的地方q请高h指正.转蝲h明出处及(qing)作?谢谢!

以一个文件传输服务端Z,在我的机器上它只起两个线E就可以为很多个个客L(fng)同时提供文g下蝲服务,E序的性能?x)随机器内CPU个数的增加而线性增?我尽可能做到使它清晰易懂,虽然E序很小却用CNT 5的一些新Ҏ(gu)?重叠IO,完成端口以及(qing)U程?Zq种模型的服务端E序应该是NTpȝ上性能最好的?

首先.做ؓ(f)完成端口的基,我们应该理解重叠IO,q需要你已经理解了内核对象及(qing)操作pȝ的一些概忉|?什么是信号/非信h?什么是{待函数,什么是成功{待的副作用,什么是U程挂v{?如果q些概o(h)q没有理?你应该先看一下Windows 核心~程中的相关内容.如果已经理解q些,那么重叠IO对你来说q不?

你可以这栯为重叠IO,现在你已l进入一个服务器/客户机环?请不要؜淆概?q里的服务器是指操作pȝ,而客h是指你的E序(它进行IO操作),是当你进行IO操作(send,recv,writefile,readfile....)时你发送一个IOhl服务器(操作pȝ),由服务器来完成你需要的操作,然后你什么事都没有了,当服务器完成IOh时它?x)通知?当然在这期间你可以做M?一个常用的技巧是在发送重叠IOh?E序在一个@环中一边调用PeekMessage,TranslateMessage和DispatchMessage更新界面,同时调用GetOverlappedResult{待服务器完成IO操作,更高效一点的做法是用IO完成例程来处理服务器(操作pȝ)q回的结?但ƈ不是每个支持重叠IO操作的函数都支持完成例程如TransmitFile函数.

?.一ơ重叠写操作q程(GetOverlappedResultҎ(gu)):
1.填写一个OVERLAPPEDl构
2.q行一ơ写操作,q指定重叠操作参?上面的OVERLAPPEDl构变量的指?
3.做其它事(如更新界?
4.GetOverlappedResult取操作结?br>5.如果IOh没有完成,q且没有出错则回到期Q?br>6.处理IO操作l果

?.一ơ重叠写操作q程(完成例程Ҏ(gu)):
1.填写一个OVERLAPPEDl构
2.q行一ơ写操作,q指定重叠操作参?上面的OVERLAPPEDl构变量的指?,q指定完成例E?br>3.做其它事(如更新界?
4.当完成例E被调用说明IO操作已经完成或出?现在可以Ҏ(gu)作结果进行处理了


如果你已l理解上面的概念,已l很接近IO完成端口?当然q只是很常规的重叠操作它已经非常高效,但如果再l合多线E对一个File或是Socketq行重叠IO操作׃(x)非常复杂,通常E序员很难把握这U复杂度.完成端口可以说就是ؓ(f)了充分发挥多U程和重叠IO操作相结合的性能而设计的.很多人都说它复杂,其实如果你自己实C个多U程的对一个File或是Socketq行重叠IO操作的程?注意是多个线E对一个HANDLE或SOCKETq行重叠IO操作,而不是启一个线E对一个HANDLEq行重叠IO操作)׃(x)发现完成端口实际上简化了多线E里使用重叠IO的复杂度,q且性能更高,性能高在?下面q行说明.

我们可能写过q样的服务端E序:

?.ȝ?
1.监听一个端?br>2.{待q接
3.当有q接来时
4.启一个线E对q个客户端进行处?br>5.回到2

服务U程:
1.dL(fng)h
2.如果客户端不再有h,执行6
3.处理h
4.q回操作l果
5.回到1
6.退出线E?br>
q是一U最单的|络服务器模?我们把它优化一?br>
?.ȝ?
1.开一个线E池,里面有机器能承受的最大线E数个线E?U程都处于挂?suspend)状?br>1.监听一个端?br>2.{待q接
3.当有q接来时
4.从线E池里Resume一个线E对q个客户端进行处?br>5.回到2

服务U程与例3模型里的相同,只是当线E处理完客户端所有请求后,不是退是回到U程?再次挂v让出CPU旉,q等待ؓ(f)下一个客h服务.当然在此期间U程?x)因为IO操作(服务U程的第1,5操作,也许q有其它d操作)挂v自己,但不?x)回到线E池,也就是说它一ơ只能ؓ(f)一个客L(fng)服务.

q可能是你能惛_的最高效的服务端模型了吧!它与W一个服务端模型相比了很多个用h到内核态的CONTEXT Switch,反映也更加快?也许你可能觉得这很微不?q说明你~少对大规模高性能服务器程?比如|游服务?的认?如果你的服务端程序要对几千万个客L(fng)q行服务?q也是微软Windows NT开发组在NT 5以上的系l中dU程池的原因.

思考一下什么样的模型可以让一个线Eؓ(f)多个客户端服务呢!那就要蟩出每来一个连接启U程为其服务的固定思维模式,我们把线E服务的最单元分割ؓ(f)单独的读或写操作(注意是读或写不是d?,而不是一个客L(fng)从连接到断开期间的所有读写操?每个U程都用重叠IOq行d操作,投递了dh后线E回到线E池,{待为其它客h服务,当操作完成或出错时再回来处理操作l果,然后再回到线E池.

看看q样的服务器模型:
?.ȝ?
1.开一个线E池,里面有机器内CPU个数两倍的U程,U程都处于挂?suspend)状?它们在都{处理一ơ重叠IO操作的完成结?br>1.监听一个端?br>2.{待q接
3.当有q接来时
4.投递一个重叠读操作d命o(h)
5.回到2

服务U程:
1.如果d?则处理读取的内容(如HTTP GET命o(h)),否则执行3
2.投递一个重叠写操作(如返回HTTP GET命o(h)需要的|页)
3.如果是一个写操作完成,可以再投递一个重叠读操作,d客户机的下一个请?或者是关闭q接(如HTTP协议里每发完一个网就断开)
4.取得下一个重叠IO操作l果,如果IO操作没有完成或没有IO操作则回到线E池

假设q是一个WEB服务器程?可以看到工作者线E是以读或写为最的工作单元q行?在主E序里面q行了一ơ重叠读操作

当读操作完成时一个线E池中的一个工作者线E被Ȁzd得了操作l果,处理GET或POST命o(h),然后发送一个网内?发送也是一个重叠操?然后处理对其它客h的IO操作l果,如果没有其它的东襉K要处理时回到U程池等?可以看到使用q种模型发送和接收可以是也可以不是一个线E?

当发送操作完成时,U程池中的一个工作者线E池Ȁz?它关闭连?HTTP协议),然后处理其它的IO操作l果,如果没有其它的东襉K要处理时回到U程池等?

看看在这L(fng)模型中一个线E怎么为多个客L(fng)服务,同样是模拟一个WEB服务器例?

假如现在pȝ中有两个U程,ThreadA,ThreadB它们在都{处理一ơ重叠IO操作的完成结?br>
当一个客hClientAq接来时ȝ序投递一个重叠读操作,然后{待下一个客hq接,当读操作完成时ThreadA被激z?它收C个HTTP GET命o(h),然后ThreadA使用重叠写操作发送一个网늻C(j)lientA,然后立即回到U程池等待处理下一个IO操作l果,q时发送操作还没有完成,又有一个客hClientBq接?ȝ序再投递一个重叠读操作,当读操作完成时ThreadA(当然也可能是ThreadB)再次被激z?它重复同h?收到一个GET命o(h),使用重叠写操作发送一个网늻C(j)lientB,q次它没有来得及(qing)回到U程池时,又有一个连接ClientCq入,ȝ序再投递一个重叠读操作,L作完成时ThreadB被激z?因ؓ(f)ThreadAq没有回到线E池)它收C个HTTP GET命o(h),然后ThreadB使用重叠写操作发送一个网늻C(j)lientC,然后ThreadB回到U程?q时ThreadA也回CU程?

可以惌现在有三个挂L(fng)发送操作分别是ThreadA发送给C(j)lientA和ClientB的网?以及(qing)ThreadB发送给C(j)lientC的网?它们由操作系l内核来处理.ThreadA和ThreadB现在已经回到U程?可以l箋为其它Q何客L(fng)服务.

当对ClientA的重叠写操作已经完成,ThreadA(也可以是ThreadB)又被Ȁzd关闭与ClientAq接,但还没有回到U程?与此同时发送给C(j)lientB的重叠写操作也完?ThreadB被激z?因ؓ(f)ThreadAq没有回到线E池)它关闭与ClientB的连?然后回到U程?q时ClientC的写操作也完?ThreadB再次被激z?因ؓ(f)ThreadAq是没有回到U程?,它再关闭与ClientC的连?q时ThreadA回到U程?ThreadB也回到线E池.q时对三个客L(fng)的服务全部完?可以看到在整个服务过E中,"建立q接","L?,"写数??关闭q接"{操作是逻辑上连l而实际上分开?

到现在ؓ(f)止两个线E处理了三次L作和三次写操?在这些读写操作过E中所出现的状态机(state machine)是比较复杂的,我们模拟的是l过我简化过?实际上的状态要比这个还要复杂很?然而这L(fng)服务端模型在客户端请求越多时与前两个模型相比的性能高.而用完成端口我们可以很Ҏ(gu)实现q样的服务器模型.

微Y的IIS WEB服务器就是用这L(fng)服务端模?很多什么阿帕奇服务器比IIS的性能好什么什么的我表C怀?除非阿帕奇服务器可以线E分割成,为更的单元服务,我觉得不太可?q种完成端口模型已经单个读或写操作作ؓ(f)最的服务单元,我觉得在相同机器配置的情况下IIS的性能要远q高于其它WEB服务?q也是从实现机理上来分析?如果出现性能上的差别可能是在不同的操作系l上,也许Linux的内核比Windows的要?有h真的研究q吗?q是大家一起在炒作?

对于状态机概念,在很多方面都用到,TCPIP中有,~译原理中有,OpengGL中有{等,我的L数学不好(我是?x)计专业不学q个),不过q是搞懂了些,我想如果你多׃旉?q是可以搞懂?最后是一个简单的文g传输服务器程序代?只用了两个线E?我的机器里只有一块CPU)可以服务多个客L(fng).我调试时用它同时?个nc客户端提供文件下载服务都没有问题,当然更多也不?x)有问?只是略ؓ(f)使用了一下NT 5的线E池和完成端口技术就可以有这样高的性能,更不用说IIS的性能?

希望大家不要陷在q个E序的框架中,Ctrl+C,Ctrl+V没有什么意?要理解它的实?E序使用Visual C++ 6.0 SP5+2003 Platform SDK~译通过,在Windows XP Professional下调试运行通过.E序q行的最低要求是Windows 2000操作pȝ.

/********************************************************************
  created:   2005/12/24
  created:   24:12:2005   20:25
  modified:   2005/12/24
  filename:   d:\vcwork\iocomp\iocomp.cpp
  file path:   d:\vcwork\iocomp
  file base:   iocomp
  file ext:   cpp
  author:     kruglinski(kruglinski_at_gmail_dot_com)
 
  purpose:   利用完成端口技术实现的高性能文g下蝲服务E序
*********************************************************************/

#define _WIN32_WINNT   0x0500

#include <cstdlib>
#include <clocale>
#include <ctime>
#include <iostream>//一使用输入输出程序顿时增?0K
#include <vector>
#include <algorithm>
#include <winsock2.h>
#include <mswsock.h>

using namespace std;

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"mswsock.lib")

const int MAX_BUFFER_SIZE=1024;
const int PRE_SEND_SIZE=1024;
const int QUIT_TIME_OUT=3000;
const int PRE_DOT_TIMER=QUIT_TIME_OUT/80;

typedef enum{IoTransFile,IoSend,IoRecv,IoQuit} IO_TYPE;

typedef struct
{
  SOCKET hSocket;
  SOCKADDR_IN ClientAddr;
}PRE_SOCKET_DATA,*PPRE_SOCKET_DATA;

typedef struct
{
  OVERLAPPED   oa;
  WSABUF     DataBuf;
  char     Buffer[MAX_BUFFER_SIZE];
  IO_TYPE     IoType;
}PRE_IO_DATA,*PPRE_IO_DATA;

typedef vector<PPRE_SOCKET_DATA>   SocketDataVector;
typedef vector<PPRE_IO_DATA>     IoDataVector;

SocketDataVector   gSockDataVec;
IoDataVector     gIoDataVec;

CRITICAL_SECTION   csProtection;

char* TimeNow(void)
{
  time_t t=time(NULL);
  tm *localtm=localtime(&t);
  static char timemsg[512]={0};
 
  strftime(timemsg,512,"%Z: %B %d %X,%Y",localtm);
  return timemsg;
}

BOOL TransFile(PPRE_IO_DATA pIoData,PPRE_SOCKET_DATA pSocketData,DWORD dwNameLen)
{
  //q一句是为nc做的,你可以修改它
  pIoData->Buffer[dwNameLen-1]='\0';
 
  HANDLE hFile=CreateFile(pIoData->Buffer,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
  BOOL bRet=FALSE;

  if(hFile!=INVALID_HANDLE_VALUE)
  {
    cout<<"Transmit File "<<pIoData->Buffer<<" to client"<<endl;
    pIoData->IoType=IoTransFile;
    memset(&pIoData->oa,0,sizeof(OVERLAPPED));
    *reinterpret_cast<HANDLE*>(pIoData->Buffer)=hFile;
    TransmitFile(pSocketData->hSocket,hFile,GetFileSize(hFile,NULL),PRE_SEND_SIZE,reinterpret_cast<LPOVERLAPPED>(pIoData),NULL,TF_USE_SYSTEM_THREAD);
    bRet=WSAGetLastError()==WSA_IO_PENDING;
  }
  else
    cout<<"Transmit File "<<"Error:"<<GetLastError()<<endl;

  return bRet;
}

DWORD WINAPI ThreadProc(LPVOID IocpHandle)
{
  DWORD dwRecv=0;
  DWORD dwFlags=0;
 
  HANDLE hIocp=reinterpret_cast<HANDLE>(IocpHandle);
  DWORD dwTransCount=0;
  PPRE_IO_DATA pPreIoData=NULL;
  PPRE_SOCKET_DATA pPreHandleData=NULL;

  while(TRUE)
  {
    if(GetQueuedCompletionStatus(hIocp,&dwTransCount,
        reinterpret_cast<LPDWORD>(&pPreHandleData),
        reinterpret_cast<LPOVERLAPPED*>(&pPreIoData),INFINITE))
    {
        if(0==dwTransCount&&IoQuit!=pPreIoData->IoType)
        {
          cout<<"Client:"
            <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
            <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
            <<" is closed"<<endl;

          closesocket(pPreHandleData->hSocket);

          EnterCriticalSection(&csProtection);
            IoDataVector::iterator itrIoDelete=find(gIoDataVec.begin(),gIoDataVec.end(),pPreIoData);
            gIoDataVec.erase(itrIoDelete);
            SocketDataVector::iterator itrSockDelete=find(gSockDataVec.begin(),gSockDataVec.end(),pPreHandleData);
            gSockDataVec.erase(itrSockDelete);
          LeaveCriticalSection(&csProtection);

          delete *itrIoDelete;
          delete *itrSockDelete;
         
          continue;
        }
       
        switch(pPreIoData->IoType){
        case IoTransFile:
          cout<<"Client:"
            <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
            <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
            <<" Transmit finished"<<endl;
          CloseHandle(*reinterpret_cast<HANDLE*>(pPreIoData->Buffer));
          goto LRERECV;
         
        case IoSend:
          cout<<"Client:"
            <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
            <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
            <<" Send finished"<<endl;

LRERECV:
          pPreIoData->IoType=IoRecv;
          pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;
          memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));

          WSARecv(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,
            &dwRecv,&dwFlags,
            reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);

          break;

        case IoRecv:
          cout<<"Client:"
            <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
            <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
            <<" recv finished"<<endl;
          pPreIoData->IoType=IoSend;
         
          if(!TransFile(pPreIoData,pPreHandleData,dwTransCount))
          {
            memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));
            strcpy(pPreIoData->DataBuf.buf,"File transmit error!\r\n");
            pPreIoData->DataBuf.len=strlen(pPreIoData->DataBuf.buf);
           
            WSASend(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,
                &dwRecv,dwFlags,
                reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);
          }
          break;
         
        case IoQuit:
          goto LQUIT;
         
        default:
          ;
        }
    }  
  }
 
LQUIT:
  return 0;
}

HANDLE hIocp=NULL;
SOCKET hListen=NULL;

BOOL WINAPI ShutdownHandler(DWORD dwCtrlType)
{
  PRE_SOCKET_DATA PreSockData={0};
  PRE_IO_DATA PreIoData={0};

  PreIoData.IoType=IoQuit;

  if(hIocp)
  {
    PostQueuedCompletionStatus(hIocp,1,
        reinterpret_cast<ULONG_PTR>(&PreSockData),
        reinterpret_cast<LPOVERLAPPED>(&PreIoData));

    cout<<"Shutdown at "<<TimeNow()<<endl<<"wait for a moment please"<<endl;
   
    //让出CPU旉,让线E退?br>    for(int t=0;t<80;t+=1)
    {
        Sleep(PRE_DOT_TIMER);
        cout<<".";
    }
   
    CloseHandle(hIocp);
  }
 
  int i=0;

  for(;i<gSockDataVec.size();i++)
  {
    PPRE_SOCKET_DATA pSockData=gSockDataVec[i];
    closesocket(pSockData->hSocket);
    delete pSockData;
  }

  for(i=0;i<gIoDataVec.size();i++)
  {
    PPRE_IO_DATA pIoData=gIoDataVec[i];
    delete pIoData;
  }

  DeleteCriticalSection(&csProtection);
  if(hListen)
    closesocket(hListen);

  WSACleanup();
  exit(0);
  return TRUE;
}

LONG WINAPI MyExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
  ShutdownHandler(0);
  return EXCEPTION_EXECUTE_HANDLER;
}

u_short DefPort=8182;

int main(int argc,char **argv)
{
  if(argc==2)
    DefPort=atoi(argv[1]);

  InitializeCriticalSection(&csProtection);
  SetUnhandledExceptionFilter(MyExceptionFilter);
  SetConsoleCtrlHandler(ShutdownHandler,TRUE);

  hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

  WSADATA data={0};
  WSAStartup(0x0202,&data);

  hListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(INVALID_SOCKET==hListen)
  {
    ShutdownHandler(0);
  }
 
  SOCKADDR_IN addr={0};
  addr.sin_family=AF_INET;
  addr.sin_port=htons(DefPort);
 
  if(bind(hListen,reinterpret_cast<PSOCKADDR>(&addr),
    sizeof(addr))==SOCKET_ERROR)
  {
    ShutdownHandler(0);
  }
 
  if(listen(hListen,256)==SOCKET_ERROR)
    ShutdownHandler(0);

  SYSTEM_INFO si={0};
  GetSystemInfo(&si);
  si.dwNumberOfProcessors<<=1;

  for(int i=0;i<si.dwNumberOfProcessors;i++)
  {
   
    QueueUserWorkItem(ThreadProc,hIocp,WT_EXECUTELONGFUNCTION);
  }
 
  cout<<"Startup at "<<TimeNow()<<endl
    <<"work on port "<<DefPort<<endl
    <<"press CTRL+C to shutdown"<<endl<<endl<<endl;

  while(TRUE)
  {
    int namelen=sizeof(addr);
    memset(&addr,0,sizeof(addr));
    SOCKET hAccept=accept(hListen,reinterpret_cast<PSOCKADDR>(&addr),&namelen);

    if(hAccept!=INVALID_SOCKET)
    {
        cout<<"accept a client:"<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port)<<endl;

        PPRE_SOCKET_DATA pPreHandleData=new PRE_SOCKET_DATA;
        pPreHandleData->hSocket=hAccept;
        memcpy(&pPreHandleData->ClientAddr,&addr,sizeof(addr));
       
        CreateIoCompletionPort(reinterpret_cast<HANDLE>(hAccept),
          hIocp,reinterpret_cast<DWORD>(pPreHandleData),0);
       
        PPRE_IO_DATA pPreIoData=new(nothrow) PRE_IO_DATA;

        if(pPreIoData)
        {
          EnterCriticalSection(&csProtection);
            gSockDataVec.push_back(pPreHandleData);
            gIoDataVec.push_back(pPreIoData);
          LeaveCriticalSection(&csProtection);

          memset(pPreIoData,0,sizeof(PRE_IO_DATA));
          pPreIoData->IoType=IoRecv;
          pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;
          pPreIoData->DataBuf.buf=pPreIoData->Buffer;
          DWORD dwRecv=0;
          DWORD dwFlags=0;
          WSARecv(hAccept,&pPreIoData->DataBuf,1,
            &dwRecv,&dwFlags,
            reinterpret_cast<WSAOVERLAPPED*>(pPreIoData),NULL);
        }
        else
        {
          delete pPreHandleData;
          closesocket(hAccept);
        }
    }
  }
 
  return 0;
}

参考资?
《MSDN 2001?br>《Windows |络~程?br>《Windows 核心~程?br>《TCP/IP详解?/font>


]]>
socket~程—技术实?http://www.shnenglu.com/amyvmiwei/archive/2008/01/17/41375.html不?/dc:creator>不?/author>Thu, 17 Jan 2008 14:22:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2008/01/17/41375.htmlhttp://www.shnenglu.com/amyvmiwei/comments/41375.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2008/01/17/41375.html#Feedback0http://www.shnenglu.com/amyvmiwei/comments/commentRss/41375.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/41375.html什么是socketQsocket是...Q我在这里就不抄书了Q有兴趣的同仁去查查书吧?br>不过q要说一句,socket是不同q程之间的一U通信方式。就象打?sh)话是朋友之间的一U通信方式是一栗个人理解:(x)所?#8220;通信”Q就是相互之间发送数据。有人理解socket是不同计机之间的一U通信?br>式,q是不确切的。两个进E,不管是运行在同一台计机上,q是q行在不同计机上,都可通过
socket技术进行通信?br>
socket套接字的使用需要有|卡的支持,所以socket一般都被用来在不同机器之间通信Q而如果在同一台计机上的两个q程q行通信Q通常采用效率更高的共享内存技术来实现?br>
两个q程之间q行通讯Q就需要两个进E同旉在运行了Q废话)Q在具体实现中,两个q程我们通常要区别对待,一个进E专门等待另一个进E给自己发消息,收到消息后进行处理,在把处理l果发送回厅R我们把专门处理消息、提供服务的q程UCؓ(f)服务器端Q把发送消息、请求处理的q程UCؓ(f)客户端。Mq程是客户端发送一个消息给服务器端Q服务器端进E收到消息进行处理,把处理结果发送给客户端。恩Q就是这栗?br>
q有一个问题,如果我现在有一个进E要跟另一台计机上的某个q程q行socket通信Q那在我q个q程中如何指定另一个进E呢Q这里还需要说一下另一个概念——端口,如果把操作系l比作一座房子的话,那端口就是房子的H口Q是pȝ外界同系l内部进行通信的通道。在socket实现中,我们不进行另一个进E的指定Q而是指定发送消息或接收消息的端口号。比如说现在q程A要给q程B发消息,我们?x)把消息发送到q程B所q行的计机的端口N上,而进EB此时正在监视端口NQ这栯EBp收到q程A发送来的数据,同样q程B也把消息发送到该端口上Q进EA也能从该端口收到q程B发送来的数据,当然Q这需要客L(fng)和服务器端关于端口号q行一个约定,卛_同操作同一个端口。如果客L(fng)把消息发送到端口N1上,而服务器端监视的是端口N2Q那通信一定不能成功。端口号最大ؓ(f)65535Q不能比q个再大了,但在我们自己的程序中量不要用小?024的端口号Q小?024的端口好很多都被pȝ使用了,比如23被telnet所使用?br>
socket的实现是很简单的Q只要按照一定的步骤Q就可马上徏立一个这L(fng)通信通道?br>
下面较详l的介绍几个核心的函敎ͼ(x)

SOCKET socket(int af, int type, int protocol);
无论是客L(fng)q是服务器端Q下面这个函数是一定要用到的,也是最先用到的?br>q个函数是要告诉pȝQ给我准备好一个socket通道Q我要和其它q程通信了。函数的q回值很重要Q我们要C来,它表C系lؓ(f)我们准备好的q个socket通道Q在以后的每个socket相关函数中都?x)用刎ͼ如果q个值等于SOCKET_ERRORQ表C函数执行失败了。函数的参数我们分别l:(x)PF_INET、SOCK_STREAM和IPPROTO_TCP?br>
int bind(SOCKET s, const sockaddr *addr, int namelen);
q个函数只有服务器端E序使用Q作用是与某个socket通道l定。可以用q回值判断该函数执行l果怎么P如果{于SOCKET_ERRORQ那是p|了。第一个参数sQ就是socket()函数的返回|在结构addr中,我们要给定一个端口号Qnamelen{于l构sockaddr的大?br>
int listen(SOCKET s, int backlog);
q个函数只有服务器端E序使用Q作用是监听该端口。返回gbind函数意义一栗?br>
int accept(SOCKET s, sockaddr *addr, int *addrlen);
q个函数只有服务器端E序使用Q作用是响应客户端的q接。返回gbind函数意义一栗?br>
int connect(SOCKET s, const sockaddr *name, int namelen);
q个函数只有客户端程序用,作用是把客户端和某个计算机的某个端口建立q接。返回gbind函数意义一栗第一个参数sQ就是socket()函数的返回|在结构name中,我们要给定一个端口号和目的机器名Qnamelen{于l构sockaddr的大?br>
int send(SOCKET s, char *buf, int len, int flags);
int recv(SOCKET s, char *buf, int len, int flags);
q两个函数就是发送数据和接收数据Q客L(fng)和服务器端程序都能用Q哪个发送哪个接收不用说了吧Q呵c(din)?br>从函数的q回值可以检查函数执行是否成功。参Cbuf是指向发送或接收的数据的指针Qlen是数据长度。flags我们l个0可以(其实是我不知道具体含义)?br>
最后就是关闭socket了,q个很容易忘掉,但这个函数很重要Q一定要用?br>int closesocket(SOCKET s);


好了Q关键函数就q么几个Q下图是q几个函数的执行序Q?br>
client?service?br>
  |                    |
  v                 v
socket()       socket()
  |                          |
  |                          v
  |                        bind()
  |                          |
  |                          v
  |                        listen()
  |                          |
  |                          v
  |                        accept() 挂vQ直到有客户端来q接
  |                          |
  v                  三段握手q程   |
connect()       <-------------> |
  |                       |
  v   发送消?  v
  +---> send() ---------------> recv() <-------+
  |   |     . |
  |   |     . 处理消息 |
  |   v   响应消息   . |
  +---- recv() <--------------- send() --------+
  |     |
  v     |
close() ---------------> recv()
    |
    v
  closesocket()

上图我觉得能很好的说明客L(fng)和服务器端的q行轨迹?br>
使用以上几个函数?linux pȝ上就可成功徏立一个socket通信q\Q但如果在windowspȝ上,q要用到另一个函敎ͼ(x)
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
在windowspȝ上,首先要执行这个函敎ͼ所以要把这个函数放在socket()函数的前面?br>
我对上面的函数进行了一些封装,省篇q,我去掉所有注释和非重要的函数Q在q里可以看到各个函数的具体用法:(x)

?VC60 环境下要q行下面的函敎ͼ要包含头文g errno.h ?winsock2.hQ还有,在连接的时候要q接上ws2_32.dll文g?br>
q是头文件内容:(x)
class Socket {
public:

bool setup();

void close();

bool connect(string host, int port);

bool listen();

int accept();

int recv(char *buf, int len);

int recv(int new_fd, char *buf, int len);

int send(const char *msg, int len);

int send(int new_fd, const char *msg, int len);

private:
  int _fd;
};

q是实现文g内容Q?br>bool Socket::setup() {

WSADATA wsd;
_fd = WSAStartup(MAKEWORD(2,2), &wsd);
if(_fd) {
return false;
}

_fd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_fd == -1) {
return false;
}
return true;
}

bool Socket::listen() {
struct sockaddr_in my_addr;

my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(52309);
my_addr.sin_addr.s_addr = INADDR_ANY;

if(::bind(_fd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == SOCKET_ERROR) {
return false;
}

if(::listen(_fd, BACKLOG) == SOCKET_ERROR) {
return false;
}

return true;
}

int Socket::accept()
{
int new_fd;
struct sockaddr_in their_addr;
int sin_size = sizeof(their_addr);

printf("accepting... \n");

new_fd = ::accept(_fd,
  (struct sockaddr *)&their_addr,
  &sin_size);
return new_fd == SOCKET_ERROR ? -1:new_fd;
}

bool Socket::connect(string host, int port) {
struct hostent *_h = gethostbyname(host.c_str());
if (_h == 0) {
return false;
}

struct in_addr *_addr = (struct in_addr *)_h->h_addr;
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr = *_addr;
sin.sin_port = htons(port);

if (::connect(_fd, (sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) {
return false;
}

return true;
}

int Socket::recv(int new_fd, char *buf, int len)
{
int nb = ::recv(new_fd, buf, len, 0);
if (nb == -1) {
printf("Error! recv.\n");
}
return nb;
}

int Socket::recv(char *buf, int len) {
return recv(_fd, buf, len);
}

int Socket::send(const char *msg, int len) {
return send(_fd, msg, len);
}

int Socket::send(int new_fd, const char *msg, int len)
{
int nb = ::send(new_fd, msg, len, 0);
if (nb == -1) {
printf("Error! send.\n");
}

return nb;
}

void Socket::close() {

int trytimes = 0;
while(::closesocket(_fd) && trytimes < CLOSE_TRY_TIMES)
trytimes++;

if(trytimes == 10) {
printf("Cannot close socket!\n");
}
}

好,socketcL装好了Q下面就是组l了Q服务器端和客户端是不一L(fng)Q下面分别给Z码,到这里已l就很简单了?br>
客户端:(x)
int main(int argc, char **argv)
{
printf("socket of client is run ...\n");
Socket s;
if (!s.connect("dezhi", 52309))
return 0;

char *msg = "ok, send a message.";
for (int i=0; i<10; i++) {
s.send(msg, 20);
printf("message = %s\n", msg);
}
s.send("q", 1);
s.close();

return 0;
}

服务器:(x)
int main(int argc, char **argv) {
printf("socket of service is run ...\n");

Socket s;
s.listen();
int new_fd = s.accept();

char buf[8];
buf[7] = '\0';
while (1) {
if (s.recv(new_fd, buf, 5) != -1) {
  printf("%s\n", buf);
  if (buf[0] == 'q')
  break;
}
}
s.close();
}

下面行结果:(x)
客户端:(x)
socket of client is run ...
Socket: WSAStartup success execute.
Socket: socket success execute.
Socket: Establish the connection to "127.0.0.1:52309"
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
message = ok, send a message.
Socket: Close connection to "127.0.0.1:52309"
Press any key to continue

服务器端
socket of service is run ...
Socket: WSAStartup success execute.
Socket: socket success execute.
bind ok!
listen ok!
accepting...
ok, send a message.
ok, send a message.
ok, send a message.
ok, send a message.
ok, send a message.
ok, send a message.
ok, send a message.
ok, send a message.
ok, send a message.
ok, send a message.
qk, send a message.
Press any key to continue

到q里吧。socket的相兛_容可q不止这些,我在q里只是l大家来个抛砖引玉,xIӞ路还很Oѝ关于详l的实现代码Q去我的《源码》上扑֐Q不攑֜q里Q是Z让篇q小些?/font>


]]>
Winsock工作模型 ( ?)http://www.shnenglu.com/amyvmiwei/archive/2008/01/17/41365.html不?/dc:creator>不?/author>Thu, 17 Jan 2008 13:22:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2008/01/17/41365.htmlhttp://www.shnenglu.com/amyvmiwei/comments/41365.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2008/01/17/41365.html#Feedback0http://www.shnenglu.com/amyvmiwei/comments/commentRss/41365.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/41365.html首先得弄清楚同步、异步、阻塞、非d的概c(din)?br>同步和异步是针对通讯的工作模式,d和非d是指socket的I/O操作?br>实际上对于socketQ只存在d和非dQ同步与异步是在E序实现上有所不同?br>以阻塞的方式执行recv函数Q在没有收到数据前,此函数是不会(x)q回的,所以这很容易执行函数的U程处于{待I/O上的数据状态,然后被挂赗非d׃一P执行recv时候不有没有数据都立卌回,有数据时q回数据Q没数据时返回错误。非d可以带来E序的高效,也带来了写程序中必须注意的地方,非阻塞情况下Q发送与接收数据时候,要用戯q理自q~冲区,q且要记录发送与接受的位|,因ؓ(f)很可能发送与接受数据的Q务不能一ơ完成,需要多ơ调用send和recv才可以完成?br>本来同步异步是用来表C通讯模式的,通信的同步,主要是指客户端在发送请求后Q必d在服务端有回应后才发送下一个请求。所以这个时候的所有请求将?x)在服务端得到同步。通信的异步,指客L(fng)在发送请求后Q不必等待服务端的回应就可以发送下一个请求,q样对于所有的h动作来说会(x)在服务端得到异步Q这条请求的链\p是一个请求队列,所有的动作在这里不?x)得到同步的。但是个人感觉,在说到socket的同步异步时候,同步跟阻塞概念差不多Q都是有了结果才q回Q异步则是告诉系l我要recv数据Q然后马上返回,{待数据来了后,pȝ跟程序说数据CQ然后程序再recv数据。引用在|上看到的比较好的描q?#8220;d block 是指Q你拨通某人的?sh)话Q但是此Z在,于是你拿着?sh)话{他回来Q其间不能再用电(sh)话。同步大概和d差不多。非d nonblock 是指Q你拨通某人的?sh)话Q但是此Z在,于是你挂断电(sh)话,待会(x)儿再打。至于到时候他回来没有Q只有打了电(sh)话才知道。即所谓的“轮询 / poll”。异步是指,你拨通某人的?sh)话Q但是此Z在,于是你叫接电(sh)话的人告诉那?leave a message)Q回来后l你打电(sh)话(call backQ?#8221;

昄Q异步要高效一些。在Winsock中实现异步的Ҏ(gu)有很多,W(xu)insock工作模型有下面六U?br>    一Qselect模型
    二:(x)WSAAsyncSelect模型
    三:(x)WSAEventSelect模型
    四:(x)Overlapped I/O 事g通知模型
    五:(x)Overlapped I/O 完成例程模型
    六:(x)IOCP模型
从一到六来高U,来高效,实现来复杂。曾在网上看C些比ȝ来很好的说明q些模型Q在q里引用一下?/p>

    老陈有一个在外地工作的女儿,不能l常回来Q老陈和她通过信g联系。他们的信会(x)被邮递员投递到他们的信里?br>一Qselect模型

老陈非常想看到女儿的信。以至于他每?0分钟׃楼检查信,看是否有奛_的信~~~~~
在这U情况下Q?#8220;下楼查信?#8221;然后回到g耽误了老陈太多的时_(d)以至于老陈无法做其他工作?/p>

二:(x)WSAAsyncSelect模型

后来Q老陈使用了微软公司的新式信箱。这U信非常先q,一旦信里有新的信Ӟ盖茨׃(x)l老陈打电(sh)话:(x)喂,大爷Q你有新的信件了Q从此,老陈再也不必频繁上下楼检查信׃Q牙也不gQ你瞅准了,蓝天......不是Q微软~~~~~~~~

三:(x)WSAEventSelect模型

后来Q微软的信箱非常畅销Q购买微软信qZ百万计数......以至于盖茨每?4时l客h?sh)话Q篏得腰酸背痛,喝蚁力神都不好~~~~~~
微Y改进了他们的信箱Q在客户的家中添加一个附加装|,q个装置?x)监视客L(fng)信箱Q每当新的信件来_(d)此装|会(x)发出“C件到?#8221;壎ͼ提醒老陈L信。盖茨终于可以睡觉了?/p>

四:(x)Overlapped I/O 事g通知模型

后来Q微软通过调查发现Q老陈不喜Ƣ上下楼收发信gQ因Z下楼其实很浪Ҏ(gu)间。于是微软再ơ改q他们的信箱。新式的信箱采用了更为先q的技术,只要用户告诉微Y自己的家在几楼几P新式信箱?x)把信g直接传送到用户的家中,然后告诉用户Q你的信件已l放C的家中了Q老陈很高_(d)因ؓ(f)他不必再亲自收发信g了!

五:(x)Overlapped I/O 完成例程模型

老陈接收到新的信件后Q一般的E序是:(x)打开信封----掏出信纸----阅读信g----回复信g......Zq一步减ȝ戯担,微Y又开发了一U新的技术:(x)用户只要告诉微Y对信件的操作步骤Q微软信将按照q些步骤d理信Ӟ不再需要用户亲自拆?阅读/回复了!老陈l于q上了小资生z!

六:(x)IOCP模型

微Y信箱g很完,老陈也很满意。但是在一些大公司情况却完全不同!q些大公司有C万计的信,每秒钟都有数以百计的信g需要处理,以至于微软信q常因负药转而崩溃!需要重新启动!微Y不得不出杀手锏......
微Yl每个大公司z了一名名?#8220;Completion Port”的超U机器hQ让q个机器人去处理那些信gQ?/p>


其实Q上面每U模型都有优点,要根据程序需求而适当选择合适的模型Q前面三U模型效率已l比较高Q实现v来难道不大,很多一般的|络E序都采用前三种模型Q只有对|络要求特别高的一些服务器才会(x)考虑用后面的那些模型。MFC中的CAsyncSocketcd是用的WSAAsyncSelect模型Q电(sh)驴中也是用的q种Q不q在L对应socket的时候进行了优化Q查找更快,在GridCast中采用的是WSAEventSelect模型Q等待?/p>


BTWQ上面所说均在Windowsq_下,只用WinSock才有q么多模型,在linux下,好像只有第一Uselect模式Q我对linux下的socket不是很了解,应该也有很多提高效率的地斏V?/p>

]]>
þþƷƷ޾Ʒ| ˾þô߽Ʒ| þ99ۺϾƷҳ| ޹Ʒþþ| þӰԺһ| 99ȶǾƷþþþþ| 91ƷۺϾþþƷ| Ļ뾫ƷԴþ| þþþ޾Ʒ˵| һɫþ88ձȡۺ| þ99ëƬѹۿ | þþþavר| þþþþþ91Ʒѹۿ| þþƷ99Ʒ| ƷëٸAVѾþ| ҹƷþþþþþС˵| þ99Ʒһ| þó˾ƷƵ| ɫۺϾþ88ɫۺ| 91þþƷһëƬ| þþþþúݺݶ| 91þۺ| þþƷav٤| ձƷþþþӰԺձ| ?VþþƷ | ŷһþþþþþô| ľþþþ| ӰȷŮAV³ɫԴþ| ޹ƷƬþ| þþþþþþþþþĻ | 91ƷѾþþþþþþ| wwþþþþþþþ| þþƷƷëƬ| ҹƷþþĸ | þþþþƵ| þþƷһAV| þþ뾫ƷպĦ| þùƷ-Ʒ| ƷVIDEOSSEXþ÷| պ޹ۺϾþþ| þþƷëƬѹۿ|