本文作者:sodme, SeaWave
本文出处:http://blog.csdn.net/sodme
http://blog.csdn.net/SeaWave/article/details/747863
声明:本文可以不经作者同意任意转载、复制、传播,但任何对本文的引用均须保留本文的作者、出处及本行声明信息!谢谢!
前言:
错误处理和socket释放, 是IOCP编程中的一大难点. 本文试图就IOCP设计中经常遇到的这个难题展开论述并寻找其解决方案, 事实上, 文中所述的解决方式不仅仅适用于IOCP, 它同样适用于EPOLL等多种服务器编程的网络模型中, 前提是: 领会这种处理方式的实质.
正文:
在使用IOCP开发时, 大家经常遇到的一个难题是与socket相关的缓冲区释放不当带来的错误, 这种错误通常是由于多次对同一个指针执行了delete操作引起的. 比如, 当在执行wsasend或wsarecv返回了非pending的错误信息时, 我们就要对此错误进行处理, 通常情况下, 我们会想到执行这两步操作:
a. 释放此次操作使用的缓冲区数据(如果不释放可能造成内存泄漏);
b. 关闭当前操作所使用的socket.
而另一方面, 我们可能也会在get函数(GetQueuedCompletionStatus)的处理中, 当get函数返回值为FALSE时也作这两步相同的操作. 此时, 就会造成对同一缓冲区的重复释放, 问题由此产生.
解决的方法, 可以有这几种:
1. 对数据缓冲区使用引用计数机制;
2. 在clientsock的对象设计机制上使释放操作线性化.
关于这两种方法, 任何一种如果要详细说清, 可能篇幅都会比较长, 笔者并无耐心和精力将每一个细节都一一道来, 在此仅选第2种方案的关键步骤和核心思想来与大家分享.
由前面对问题的描述可以看出, 造成多次释放的原因可能是在执行收发操作和GET函数返回值为FALSE时, 我们重复执行了释放操作. 很自然地, 我们会想到, 能不能把这两次释放合并成一次释放, 这样不就没问题了吗? yes, 这个思路是没问题的. 但要想让这个思路能变成现实, 需要在设计机制上对这个思路进行一定的支持.
首先, 我们假设, 是在get函数返回时统一进行相应的释放和关闭操作.
如果在执行wsasend操作时, 发生了非pending错误(io操作正在进行中), 而此时我们如果不释放资源, 那至少得让IOCP在GET返回时得知这个错误和发生错误时的缓冲区指针. 通知IOCP的方式, 是使用post函数(PostQueuedCompletionStatus)向IOCP抛一个特殊标志的消息, 这个特殊标志可以通过get函数的第二个参数, 即: 传送字节数来表示, 可以选择任何一个不可能出现的值, 比如任何一个跟它的初始值不相等的负数. 当然, 如果你通过单句柄数据或单IO数据来传递也是可以的. 而发生错误的这个缓冲区指针, 我们是必须要通过单句柄数据或单IO数据来传递的. 但是, 从整个缓冲区的管理机制上来说, 我不推荐这样的离散缓冲区机制, 我的建议是: 把收发缓冲区或数据队列与相应的clientsocket对象相绑定, 释放操作写在该对象的析构函数里, 这样当释放clientsocket对象时就释放了这些缓冲区.
ok, 这样一来, 在get函数里, 有三种情况需要执行释放逻辑:
1. get的返回值为FALSE;
2. 传送字节数为0;
3. 接收到刚才我们post的那个错误类型消息.
把释放操作全放在get函数里以后, 对释放操作的处理, 就比较统一了. 当然, 为了实现真正的线性化和元子化, 在释放操作的最终执行逻辑上, 还需要对释放代码加锁以实现线程互斥(当然, 这是在你开了多个工作者线程的情况下).
-------------------------------------------承上启下----------------------------------------------------
001: /************************************************************************
002: 引用计数基类、及引用计数指针模板类
003: ----NoSound QQ2591570 可随意复制、改动、使用、拍砖,概不追究!
004: ************************************************************************/
005: #ifndef _REFCOUNTED_INCLUDED_
006: #define _REFCOUNTED_INCLUDED_
007:
008: #include
009: #ifdef _MT
010: #include
011: #endif
012:
013: class RefCountable {
014: public:
015: int addRef(void) {
016: #ifdef _MT
017: return ::InterlockedIncrement(&refCount_);
018: #else
019: return ++refCount_;
020: #endif
021: }
022:
023: int decRef(void) {
024: int r =
025: #ifdef _MT
026: ::InterlockedDecrement(&refCount_);
027: #else
028: --refCount_;
029: #endif
030: assert(r>=0);
031: if (0==r)
032: delete this;
033: return r;
034: }
035:
036: int getRefCount(void) const { return refCount_; }
037:
038: protected:
039: RefCountable(void) : refCount_(0) {}
040: virtual ~RefCountable(void) { assert(0==refCount_); }
041:
042: private:
043: #ifdef _MT
044: long
045: #else
046: int
047: #endif
048: refCount_;
049: RefCountable(const RefCountable &);
050: RefCountable & operator = (const RefCountable &);
051: };
052:
053: template
054: class RefCountedPtr {
055: public:
056: RefCountedPtr(void) : ptr_(0) {}
057: RefCountedPtr(T *ptr) : ptr_(ptr) {
058: if (ptr_)
059: ptr_->addRef();
060: }
061: RefCountedPtr(const RefCountedPtr &sour) : ptr_(sour.ptr_) {
062: if (ptr_)
063: ptr_->addRef();
064: }
065: RefCountedPtr & operator = (const RefCountedPtr &right) {
066: if (this!=&right) {
067: if (0!=ptr_)
068: ptr_->decRef();
069: ptr_ = right.ptr_;
070: if (ptr_)
071: ptr_->addRef();
072: }
073: return *this;
074: }
075: ~RefCountedPtr(void) {
076: if (0!=ptr_)
077: ptr_->decRef();
078: }
079:
080: T & operator*() const { return *ptr_; }
081: T * operator->() const { return (&**this); }
082:
083: friend bool operator == (const RefCountedPtr &left, const RefCountedPtr &right) {
084: return (left.ptr_ == right.ptr_);
085: }
086: friend bool operator != (const RefCountedPtr &left, const RefCountedPtr &right) {
087: return (left.ptr_ != right.ptr_);
088: }
089: friend bool operator < (const RefCountedPtr &left, const RefCountedPtr &right) {
090: return (left.ptr_ < right.ptr_);
091: }
092: friend bool operator > (const RefCountedPtr &left, const RefCountedPtr &right) {
093: return (left.ptr_ > right.ptr_);
094: }
095: friend bool operator <= (const RefCountedPtr &left, const RefCountedPtr &right) {
096: return (left.ptr_ <= right.ptr_);
097: }
098: friend bool operator >= (const RefCountedPtr &left, const RefCountedPtr &right) {
099: return (left.ptr_ >= right.ptr_);
100: }
101:
102: bool isNull() const { return 0==ptr_; }
103: bool isValid() const { return 0!=ptr_; }
104:
105: // 返回所控制的对象指针
106: T * get(void) const { return ptr_; }
107:
108: //取得对另一指针的控制权
109: void reset(T * ptr=0) {
110: if (0!=ptr)
111: ptr->addRef();
112: if (0!=ptr_)
113: ptr_->decRef();
114: ptr_ = ptr;
115: }
116:
117: private:
118: T *ptr_;
119: };
120:
121: #endif // ifndef _REFCOUNTED_INCLUDED