好久没有研究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个队列来处理事件。