出于性能考虑, 打算使用IOCP制作代理服务器(或者就是简单的中转服务器)但是碰到了很多困难,最主要就是 A->ProxyServer->B, B->ProxyServer->A 的逻辑完成不了,看似简单,但实现不是很容易啊,网上的有关 IOCP的例子几乎都是在讲怎么去 Echo, 这种中转的逻辑就没有了。如果哪位能提供这方面的参考例子或者点子,将不胜感激。

解决方案 »

  1.   

    http://code.google.com/p/spserver/
      

  2.   

    说一下自己的思路,没有试过不知行不行。来一个抛砖引玉,各位大侠指教在绑定的时候调用下面的函数HANDLE CreateIoCompletionPort (
      HANDLE FileHandle,              // handle to file
      HANDLE ExistingCompletionPort,  // handle to I/O completion port
      ULONG_PTR CompletionKey,        // completion key
      DWORD NumberOfConcurrentThreads // number of threads to execute concurrently
    );
    在ULONG_PTR CompletionKey,        // completion key
    中同时将A和B的参数传进工作者线程,A和B同时绑定这个key
    那么刚才的key中将有一个是HANDLE FileHandle,              // handle to file这样在工作者线程中收到数据的时候就通过判断就可以决定向谁发送什么样的数据
      

  3.   

    对于Proxy,A就是A,B就是B,不能混在一起,当代码A->B的时候,A是Proxy的客户机,B是Proxy的服务器,一边需要Accept,一边需要的是Connect,虽然后续操作没有太大差别,但是还是两个连接,任何单方断开都不能处理资源的回收。
    另外,在通讯当中,他们又是一致性的处理。对于A连接,B是A的Forward,同样的对于B连接A是B的Forward。两两没有差别。
    [code=BatchFile]
    AcceptEx(ClientA)
    ...
    if ClientA.Accepted then
      ConnectEx(ServerB)
    ...
    if ServerB.Connected then
    {  
      ClientA.Forward = ServerB
      ServerB.Forward = ClientA
      WSARecv(ClientA)
      WSARecv(ServerB)
    }
    ...
    if ClientA.Received then
      WSASend(ClientA.Forward/*ServerB*/,ClientA.Data)
    ...
    if ServerB.Received then
      WSASend(ServetB.Forward/*ClientA*/,ServerB.Data)
    ...[/code]
      

  4.   

    谢谢大家的帮助我现在得思路是 每个CompletionKey上都加上对方的CompletionKey比如:1. Proxy Server 开始 Listen 
    2. 有个Client 被 Accept
    3. 创建该Client对应的CompletionKey并加入IOCP
    4. 同时创建一个RemoteServer的Socket, 也创建此RemoteSocket的CompletionKey也加入上边的IOCP
    5. 由于CompletionKey结构中有指向与自己相同结构的指针,即 CompletionKey* p,利用这个指针把刚才建立的两个CompletioKey互指,即变成Ring结构,简单说就是A->B, B->A
    6. 然后进行GetQueuedCompletionStatus, 如果是发生接受完毕,则把接收到的数据全部发到对方,如果是发送完毕事件,假如是A->B,则如果收取的字节<缓冲大小(这么做虽然有问题,但现在这还是小问题),即表示数据已发送完毕则开始在B读取数据,则如果收取的字节=缓冲大小则认为还有数据要从A收取。但这么做有时候能互传数据,有时候就停滞不前了,最大的问题就是如果B是HTTP服务器,假如缓冲大小是4K,虽然网页超过4K比如10K, 但 只传了3K,HTTP服务器就不响应了,后面的数据就不传,结果A收到的是不完整的网页。
    我又怀疑是不是网也有图片,每个要读取图片的时候重新Accept一下,是不是要改用AcceptEx。 虽然CompletionPort处理过程中不要使用阻塞操作,但这里的Accept跟CompletionPort几乎说是无关,哎,头大了。
      

  5.   

    typedef struct _CompletionKey{
      OldCompletionKey Self;
      OldCompletionKey *Forward;
    }NewCompletionKey,*LPNewCompletionKey;
      

  6.   

    代理服务器的角色只是一个转发而已
    其他的什么都不需要做的主要在工作者线程中得到一个对应的关系即可
    这个对应的关系就通过完成key传进去当收到的时候将收到的数据完整的传给对方不过要做好的是资源的回收等事情
      

  7.   

    如果没必要的话,我劝你还是使用其它模式写代理程序.IOCP如果写单向发送的服务器,例如http服务器,ftp服务器什么的,逻辑还是很清楚的.但用来写双向性的服务器,象代理服务器(http代理服务器,socks5代理服务器)之类,逻辑会比较混乱.我用好几种不同模式写过socks5代理服务器(包括IOCP模型的),最困难的就是用IOCP,你所说的问题我也遇到过.具体代码我就不放出来了,我写的IOCP的代理服务器,IO_Handle中是包括另一个IIO_Handle的(目标的IO_Handle,因为代理服务器是双向的),还有在连接到目标服务器后,IOCP的代理服务器,就要向客户端和目标服务器,同时投递WSARecv(),这是因为你无法知道最先的数据流向的方向是从客户端流经代理,再到目标;还是从目标服务器流经代理,再到客户端的(象http,ftp等的访问,是目标服务器流经代理,到客户端的,而连接IRC服务器等,方向则是相向),以上就是我认为比较关键的两点.以下是我的IOCP模型Socks5代理的IO_HANDLE数据:typedef struct IO_HANDLE
    {
     SOCKET      OriginalSocket;
     IO_HANDLE   *FinalHandle;
     HANDLE      DomainHandle;
     char        *DomainBuffer;
     SOCKADDR_IN DestSocketAddress;
     IODATA      IOData;
     DWORD       TotalTransfer;
     DWORD       Timer;
     BOOL        IsActive;
     省略部分数据
     struct      IO_HANDLE *Next;
    }IOHANDLE,*LPIOHANDLE;
    LPIOHANDLE IOHandleListHead = NULL;
      

  8.   

    对了,忘记了说一句.你遇到的问题,只有写过同类型程序的朋友才会知道是什么回事,没写过的朋友,很难清楚是什么回事的(就象我在写IOCP模型的Socks5代理前,已经这用其它模型写过Socks5代理程序,本以为并不会花太多时间和有什么困难,但当写起来后,才发现有不少问题需要解决).
      

  9.   

    我也做過類似的,我的做法是把 Proxy 對內連接的 socket 與 對外連接的 socket 都綁到一個 完成端口上,用幾個工作線程對一個完成端口工作就可以完成Proxy 了。但能力有限,問題多多。最後只能暫時放棄,過段時間再回頭來看了。