IOCP之WSASend,0字節探測發送。工作線程真正發送。

IOCP_WSASend函數是多線程消息包發送函數,這裏會進行加鎖拷貝數據到緩衝區,然後檢測當前是否正在發送過程中。如果沒有正在異步發送,那麼投遞0字節發送。

#define OVL_RECEIVE				0X01
#define OVL_SEND0				0X02
#define OVL_CLOSE				0X03
#define OVL_SENDE				0X04


// 緩衝區安全鎖
	CRITICAL_SECTION m_csCache;
	// 發送緩衝區
	char *m_pCacheBuf;
	int m_nCacheLength;
	// WSASend異步投遞標識,當沒有投遞時,先投遞一個0字節的空發送
	BOOL m_bIoSending;
	// WSASend WSABuf數據和長度
	char m_bySendBuf[MAX_WSASEND_BUFF_SIZE];
	int m_nSendLength;

// debug統計
	unsigned int m_nWillSend;
	unsigned int m_nHadSend;
	// 重用次數
	int m_nInitCount;
	// 丟棄消息包數量
	int m_nDropSendCount;
case	OVL_SEND0:
			case	OVL_SENDE:
				{
					long errCode = pBSocket->IOCP_OnWSASend(nbytes, pOvl->Offset);
					if (0L != errCode)
					{
						if (WSAENOBUFS != errCode)
						{
							pBSocket->B_OnClose(errCode);
							break;
						}
					}

					// 舊代碼
					pBSocket->m_ActivatedFlag = 1;
					pBSocket->B_OnSend(0);
					break;
				}
long CIOCPSocket::IOCP_WSASend( char* pBuf, long length)
{
	EnterCriticalSection(&m_csCache);
	int errCode = 0L;

	if (m_nCacheLength + length > INIT_CACHE_BUFF_SIZE)
	{
		LeaveCriticalSection(&m_csCache);
		++m_nDropSendCount;
		return errCode;
	}

	// 緩存數據
	memcpy(m_pCacheBuf + m_nCacheLength, pBuf, length);
	m_nCacheLength += length;
	
	m_nWillSend += length;

	if (!m_bIoSending) {
		//IOCP_OnWSASend();
		// 發送0字節數據包,觸發真正的數據發送
		DWORD dwIoSize;
		DWORD dwFlags = 0;

		memset(&m_SendOverlap, 0, sizeof(m_SendOverlap));
		m_SendOverlap.Offset = OVL_SEND0;
		m_out.buf = m_bySendBuf;
		m_out.len = 0;
		
		// 發送數據
		m_bIoSending = TRUE;

		if (SOCKET_ERROR == WSASend(m_Socket, &m_out, 1, &dwIoSize, dwFlags, &m_SendOverlap, NULL)) 
		{
			//發送數據
			int nLastErr = WSAGetLastError();
			if (WSA_IO_PENDING != nLastErr) 
			{
				if (WSAENOBUFS == nLastErr)
				{
					// 內存分頁死鎖
					CMyLog::m_pLog->_XGSysLog("[WARN]IOCP_WSASend[s:%d]WSAENOBUFS.m_nCacheLength:%d,socket errCode:%d.\n", 
						m_Socket, m_nCacheLength, nLastErr);
					errCode = 0L;
				}
				else
				{
					// 斷開連接時返回-1
					CMyLog::m_pLog->_XGSysLog("[ERROR]IOCP_WSASend[s:%d]m_nCacheLength:%d, m_nSendLength:%d, socket errCode:%d.\n", 
						m_Socket, m_nCacheLength, m_nSendLength, nLastErr);
					errCode = -1;
				}
			}
		}
	}

	LeaveCriticalSection(&m_csCache);

	return errCode;
}

IOCP_OnWSASend是iocp的工作線程異步WSASend的結果調用函數。這裏返回發送的字節數。當投遞0自己發送時,這裏會加鎖從緩衝區拷貝數據,真正的WSASend發送數據。WSASend的發送只要投遞成功,投遞的所有數據都會發送成功,不會出現只發送一部分的情況。所以m_nSendLenght在投遞發送之後必定爲0.也就是m_nSendLenght==nBytes。也就是(m_nSendLength -= nBytes;當nByte補位0時,m_nSendLenght==nBytes必定相等。)

long CIOCPSocket::IOCP_OnWSASend(int nBytes, int n)
{
	EnterCriticalSection(&m_csCache);
	long errCode = 0L;
	m_nSendLength -= nBytes;

	m_nHadSend += nBytes;

	if (m_nSendLength > 0)
	{
		// 這裏不應該進來,進來就有問題
		if (nBytes > 0) 
		{
			// 數據沒有發送完,數據前移
			memcpy(m_bySendBuf, m_bySendBuf + nBytes, m_nSendLength);
		}

		CMyLog::m_pLog->_XGSysLog("[ERROR]IOCP_OnWSASend[s:%d]n:%d.m_nCacheLength:%d, m_nSendLength:%d, nBytes:%d.m_nRecyleCount:%d.\n", 
			m_Socket, n, m_nCacheLength, m_nSendLength, nBytes, m_nInitCount);
	}
	else if (m_nSendLength < 0)
	{
		LeaveCriticalSection(&m_csCache);
		// 出現了錯誤!
		CMyLog::m_pLog->_XGSysLog("[ERROR]IOCP_OnWSASend[s:%d]m_nSendLength:%d.\n", m_nSendLength);
		return errCode;
	}
	else
	{
		// m_nSendLength == 0
	}

	if (m_nCacheLength > 0) 
	{
		int nCanCopyLength = MAX_WSASEND_BUFF_SIZE - m_nSendLength;
		if (m_nCacheLength > nCanCopyLength) 
		{
			// copy a part bytes from cache
			memcpy(m_bySendBuf + m_nSendLength, m_pCacheBuf, nCanCopyLength);
			m_nSendLength += nCanCopyLength;
			m_nCacheLength -= nCanCopyLength;

			CMyLog::m_pLog->_XGSysLog("[WARN]IOCP_OnWSASend[s:%d]m_nCacheLength > nCanCopyLength:%d.m_nCacheLength:%d, m_nSendLength:%d.\n", 
				m_Socket, nCanCopyLength, m_nCacheLength, m_nSendLength);
		}
		else
		{
			// copy all data from cache.
			memcpy(m_bySendBuf + m_nSendLength, m_pCacheBuf, m_nCacheLength);
			m_nSendLength += m_nCacheLength;
			m_nCacheLength = 0;
		}
	}
	
	// 異步發送數據
	if (m_nSendLength > 0) 
	{
		DWORD dwIoSize = 0;
		DWORD dwFlags = 0;

		memset(&m_SendOverlap, 0, sizeof(m_SendOverlap));
		m_SendOverlap.Offset = OVL_SENDE;
		m_out.buf = m_bySendBuf;
		m_out.len = m_nSendLength;

		//int nMiniSendLen = 9;
		//if (m_nSendLenght < nMiniSendLen) 
		//{
		//	nMiniSendLen = m_nSendLenght;
		//}
		//m_out.len = nMiniSendLen;

		m_bIoSending = TRUE;

		if (SOCKET_ERROR == WSASend(m_Socket, &m_out, 1, &dwIoSize, dwFlags, &m_SendOverlap, NULL)) 
		{
			//發送數據
			int nLastErr = WSAGetLastError();
			if (WSA_IO_PENDING != nLastErr) 
			{
				if (WSAENOBUFS == nLastErr)
				{
					// 內存分頁死鎖。
					m_bIoSending = FALSE;
					CMyLog::m_pLog->_XGSysLog("[WARN]IOCP_OnWSASend[s:%d]WSAENOBUFS.m_nCacheLength:%d, m_nSendLength:%d.\n", 
						m_Socket, m_nCacheLength, m_nSendLength);
					errCode = 0L;
				}
				else
				{
					// socket 錯誤斷開
					CMyLog::m_pLog->_XGSysLog("[ERROR]IOCP_OnWSASend[s:%d]socket will close errCode:%d.m_nCacheLength:%d, m_nSendLength:%d.\n", 
						m_Socket, nLastErr, m_nCacheLength, m_nSendLength);
					errCode = nLastErr;
				}
			}
			else
			{
				CMyLog::m_pLog->_XGSysLog("[WARN]IOCP_OnWSASend[s:%d]WSA_IO_PENDING.m_nCacheLength:%d,m_nSendLength:%d.\n", 
					m_Socket, m_nCacheLength, m_nSendLength);
			}
		}
	} 
	else 
	{
		m_bIoSending = FALSE;
	}

	LeaveCriticalSection(&m_csCache);

	return 0L;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章