Windows socket I/O模型 之 select(3)

好久没有研究select了,不过我近几天我又有时间研究了。

但是还有好几个问题:

1.  Send和Write是分开为2个线程的。

2.  定时器没有使用“最小堆”

3.  没有事件通知队列

/

// 对于问题1

目前我的代码中send和receive是为2个线程的,是因为我考虑到receive线程可能在recv这个API函数处阻塞,所以才将send和write分开到2个线程的。


但是今天上午研究了一下,并不是这样的。select的用法是当fdWrite可读的时候,fdWrite才有值,此时你再recv就不会阻塞(这是我看MSDN的),然后我就测试了一下,果然如此。因此我将ThreadSend和ThreadReceive合并为一个线程了。

供参考的代码段如下:

DWORD WINAPI ThreadSend(LPVOID lpvParam)
{
    g_bRunning = TRUE;

    fd_set fdRead, fdWrite, fdExcept;

    while( TRUE ) {
        // initialize
        FD_ZERO(&fdRead);
        FD_ZERO(&fdWrite);
        FD_ZERO(&fdExcept);
        FD_SET(g_soClient, &fdRead);
        FD_SET(g_soClient, &fdWrite);
        FD_SET(g_soClient, &fdExcept);

        int iResult = select(0, &fdRead, &fdWrite, &fdExcept, NULL);
        if( iResult == SOCKET_ERROR) {
            printf("SOCKET_ERROR happened...\n");
            break;
        } else if( iResult == 0 ) {
            printf("Time limit expired\n");
            break;
        }

        // check except
        for( int i=0; i<(int)fdExcept.fd_count; i++) {
            if (FD_ISSET(fdExcept.fd_array[0], &g_fdSocketSet)) {
                    
                int a=2;
                int b=a;
            }
        }

        // check read
        for( int i=0; i<(int)fdRead.fd_count; i++) {
            if (FD_ISSET(fdRead.fd_array[0], &g_fdSocketSet)) {
                sockaddr_in name;
                int namelen = sizeof(sockaddr_in);
                getpeername(fdRead.fd_array[0], (sockaddr *)&name, &namelen);

                char buf[256] = {0};
                int len = 256;
                int ret = recv(fdRead.fd_array[0], buf, len, 0);
                CCSLock lock(&g_criSec);
                if( ret == SOCKET_ERROR ) {
                    int nErr = GetLastError();
                    if( nErr == 10054 ) {
                        // Connection reset by peer.
                        FD_CLR(fdRead.fd_array[0], &g_fdSocketSet);
                        printf( "\n[%s:%d] is closed.\n", inet_ntoa(name.sin_addr), ntohs(name.sin_port) );

                        // 服务端关闭套接字,我们需要终止ThreadIO
                        g_bRunning = FALSE;
                    } else {
                        printf("fdread failed with %d\n", nErr);
                    }
                } else {
                    CCSLock lock(&g_criSec);
                    printf("\nRecv from [%s:%d] : %s\n", inet_ntoa(name.sin_addr), ntohs(name.sin_port), buf);
                }
            }
        }

        // check write
        for( int i=0; i<(int)fdWrite.fd_count; i++) {
            if( FD_ISSET(fdWrite.fd_array[0], &g_fdSocketSet) ) {

                // send buffer
                CBuffer* pBuffer = g_pSendPool->GetReadyBuffer();
                while( pBuffer ) {

                    int ret = send(fdWrite.fd_array[0], (const char *)pBuffer->GetData(), pBuffer->GetLength(), 0);
                    std::cout << "send: " << ret << std::endl;

                    pBuffer = g_pSendPool->GetReadyBuffer();
                }
            }
        }

        // check timeout
        HEAP_ENTRY entry;
        while( g_timeoutHeap.PeekMin(entry) ) {
            if( entry.dwTimeout <= GetTickCount() ) {
                g_timeoutHeap.RemoveMin(entry);
                OnTimeout(entry.dwTimeID);
            } else {
                break;
            }
        }

        if( !g_bRunning ) {
            break;
        }
    }

    return 0;
}
线程开始的时候,将读,写,异常都加到select中判断。然后分开处理,读,写,和超时事件。



/

// 对于问题2

我又研究了一下最小堆的算法,参考网上的代码,编写了一个最小堆的类。我们可以使用该最小堆的类来完成超时处理。

下面分享下最小堆的代码:

// Minheap.h

/*
 * Copyright: oldmtn (oldmtn@qq.com)
 *
 * Author	: oldmtn
 * Blog		: http://blog.csdn.net/oldmtn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _MINHEAP_H
#define _MINHEAP_H
#include <list>
#include <vector>
#include <limits.h>

typedef struct tagHEAP_ENTRY {
    DWORD dwTimeout;
    DWORD dwTimeID;

    bool operator>(const tagHEAP_ENTRY& rhs) const {
        if(this->dwTimeout > rhs.dwTimeout) {
            return true;
        } else {
            return false;
        }
    }
    bool operator<(const tagHEAP_ENTRY& rhs) const {
        if(this->dwTimeout < rhs.dwTimeout) {
            return true;
        } else {
            return false;
        }
    }
    bool operator==(const tagHEAP_ENTRY& rhs) const {
        if(this->dwTimeout == rhs.dwTimeout) {
            return true;
        } else {
            return false;
        }
    }
    bool operator>=(const tagHEAP_ENTRY& rhs) const {
        if(this->dwTimeout >= rhs.dwTimeout) {
            return true;
        } else {
            return false;
        }
    }
    bool operator<=(const tagHEAP_ENTRY& rhs) const {
        if(this->dwTimeout >= rhs.dwTimeout) {
            return true;
        } else {
            return false;
        }
    }

    // 赋值操作符
    void operator=(const tagHEAP_ENTRY& rhs) {
        this->dwTimeout = rhs.dwTimeout;
        this->dwTimeID = rhs.dwTimeID;
    }
} HEAP_ENTRY, *PHEAP_ENTRY;

 
class CMinHeap
{
public:
    CMinHeap(const int iDefault = 1024, const int iMax = INT_MAX, const int iIncrement = 1024);
    CMinHeap(const int iMax, const HEAP_ENTRY* array, int size);
    virtual ~CMinHeap();

    bool isLeaf(int pos) const;    //如果是叶结点,返回TRUE
    int leftchild(int pos) const;    //返回左孩子位置
    int rightchild(int pos) const;    //返回右孩子位置
    int parent(int pos) const;    // 返回父结点位置
    bool Remove(int pos, HEAP_ENTRY& node);    // 删除给定下标的元素

    bool Insert(const HEAP_ENTRY& newNode);
    bool RemoveMin(HEAP_ENTRY& t);

    bool PeekMin(HEAP_ENTRY& t);
    void PrintHeap();
    int GetCurrSize();

private:
    void SiftUp(int position);  //从position向上开始调整,使序列成为堆
    void SiftDown(int left);    //筛选法函数,参数left表示开始处理的数组下标

    void BuildHeap();	//建堆
    bool swap(int iBegin, int iEnd);

private:
    HEAP_ENTRY*      m_arrHeapEntry;
    int     m_iCurrSize;
    int     m_iCurrMaxSize;
    int     m_iMaxSize;
};

///


#endif

/*
 * Copyright: oldmtn (oldmtn@qq.com)
 *
 * Author	: oldmtn
 * Blog		: http://blog.csdn.net/oldmtn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "StdAfx.h"
#include "minheap.h"

CMinHeap::CMinHeap(const int iDefault/* = 1024*/, const int iMax/* = INT_MAX*/, const int iIncrement/* = 1024*/)
{
    m_iCurrSize = 0;

    m_iCurrMaxSize = iDefault;
    m_iMaxSize = iMax;
    m_arrHeapEntry = (HEAP_ENTRY*)malloc(iDefault * sizeof(HEAP_ENTRY));
}

 
CMinHeap::CMinHeap(const int iMax, const HEAP_ENTRY* array, int size)
{
    if( iMax <= 0 || size > iMax ) {
        return;
    }

    m_iCurrSize = 0;
    m_iMaxSize = iMax;
    m_arrHeapEntry = new HEAP_ENTRY[m_iMaxSize];

    //此处进行堆元素的赋值工作
    //要为m_arrHeapEntry数组进行一些赋值,不然
    //无法实现建堆操作,因为m_iCurrSize为0
    for( int i=0; i < size; i ++) {
        m_arrHeapEntry[i] = array[i];
    }
    m_iCurrSize = size;
    BuildHeap();
}

//析构函数
CMinHeap::~CMinHeap()
{
    delete []m_arrHeapEntry;
}

//建堆
void CMinHeap::BuildHeap()
{
    //反复调用筛选函数,问题:m_iCurrSize<2?
    for( int i=m_iCurrSize/2-1; i >= 0; i --) {
        SiftDown(i);
    }
}

bool CMinHeap::swap(int iBegin, int iEnd)
{
    if( iBegin < 0 || iBegin >= m_iCurrSize ) {
        return false;
    }
    
    if( iEnd < 0 || iEnd >= m_iCurrSize) {
        return false;
    }

    if( iBegin == iEnd ) {
        return true;
    }
    HEAP_ENTRY t = m_arrHeapEntry[iBegin];
    m_arrHeapEntry[iBegin] = m_arrHeapEntry[iEnd];
    m_arrHeapEntry[iEnd] = t;

    return true;
}

void CMinHeap::SiftDown(int position)
{
    int i=position;//标识父结点
    //此时j不一定是关键值较小的子结点,
    //但是希望它标识关键值较小的子结点。
    int j=2*i+1;
    HEAP_ENTRY	temp=m_arrHeapEntry[i];//保存父结点
    //过筛
    while( j < m_iCurrSize ) {
        if( ( j < m_iCurrSize-1) && ( m_arrHeapEntry[j] > m_arrHeapEntry[j+1] )) {
            //while第一次循环时,如果进入,比较左结点和右结点的值
            //如果左结点的值大于右结点的值则,j指向数值较小的子结点
            j++;
        }
        if( temp > m_arrHeapEntry[j] ) {
            m_arrHeapEntry[i] = m_arrHeapEntry[j];
            i = j;
            j = 2*j+1;//向下继续
        } else {
            break;
        }
    }
    m_arrHeapEntry[i]=temp;
}

bool CMinHeap::isLeaf(int pos) const
{
    return (pos >= m_iCurrSize/2) && (pos < m_iCurrSize);
}

int CMinHeap::leftchild(int pos) const
{
    return 2*pos + 1;//返回左孩子位置
}

int CMinHeap::rightchild(int pos) const
{
    return 2*pos + 2;//返回右孩子位置
}

int CMinHeap::parent(int pos) const
{
    return (pos-1)/2;//返回父结点位置
}

bool CMinHeap::Insert(const HEAP_ENTRY& newNode)
{
    if( m_iCurrSize == m_iCurrMaxSize ) { //堆空间已经满
        void * pNew = realloc(m_arrHeapEntry, m_iCurrSize * 2 * sizeof(HEAP_ENTRY));
        if( pNew == NULL ) {
            return false;
        } else {
            m_arrHeapEntry = (HEAP_ENTRY*)pNew;
            return true;
        }
    }
    
    m_arrHeapEntry[m_iCurrSize] = newNode;
    SiftUp(m_iCurrSize);//向上调整
    m_iCurrSize++;
    return true;
}

void CMinHeap::SiftUp(int position)
{
    int temppos = position;
    HEAP_ENTRY temp = m_arrHeapEntry[temppos];
    //请比较父子结点直接swap的方法
    while( (temppos > 0) && (m_arrHeapEntry[parent(temppos)] > temp)) {
        m_arrHeapEntry[temppos] = m_arrHeapEntry[parent(temppos)];
        temppos = parent(temppos);
    }
    m_arrHeapEntry[temppos]=temp;
}

bool CMinHeap::RemoveMin(HEAP_ENTRY& t)
{
    if( m_iCurrSize == 0 ) {
        return false;
    } else {
        t = m_arrHeapEntry[0];

        swap( 0, m_iCurrSize-1 );
        m_iCurrSize --;
        if( m_iCurrSize > 1 ) { // <=1就不要调整了
            //从堆顶开始筛选
            SiftDown(0);
        }
        return true;
    }
}

// 删除给定下标的元素
bool CMinHeap::Remove(int pos, HEAP_ENTRY& node)
{
    // 删除给定下标的元素
    if( ( pos < 0 ) || ( pos >= m_iCurrSize ) ) {
        return false;
    }
    
    //指定元素置于最后
    HEAP_ENTRY temp = m_arrHeapEntry[pos];
    m_arrHeapEntry[pos] = m_arrHeapEntry[--m_iCurrSize];
    SiftUp(pos);//上升筛
    SiftDown(pos);//向下筛,不是SiftDown(0);
    node = temp;
    return true;
}

/
bool CMinHeap::PeekMin(HEAP_ENTRY& t)
{
    if( m_iCurrSize == 0 ) {
        return false;
    } else {
        t = m_arrHeapEntry[0];
        return true;
    }
}

void CMinHeap::PrintHeap()
{
    //OutputDebugString(_T("Begin print heap element......\n"));
    //CString strTmp;
    //for( int i=0; i < m_iCurrSize; i ++ ) {
    //    strTmp.Format(_T("__%d__"), m_arrHeapEntry[i]);

    //    OutputDebugString(strTmp);
    //}
    //OutputDebugString(_T("\nEnd print heap element......\n"));
}

int CMinHeap::GetCurrSize()
{
    return m_iCurrSize;
}



/

// 对于问题3

至于事件通知队列,就赞不列出,就是普通的预分配一段内存,分割成若干个块。并将其分为InUse和Free2个队列,不断的操作这2个队列来处理事件。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值