?? te_socket.cpp
字號:
////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2000-2001 Softelf Inc. All rights reserved.
////////////////////////////////////////////////////////////////////////////////
//
// Author : Telan
// Date : 2000-10-04
// Purpose : Encapsulate winsock2 functions to make it more easily used
// History :
// 1.0 : 2000-03-10 - First Edition of this source code ( called:FE_SOCKET )
// 2.0 : 2000-06-25 - Second Edition ( name changed to TE_SOCKET )
// - Add Error Control
// 3.0 : 2000-09-21 - Third Edition ( name changed to TE_SOCKET )
// - Change the errors' process mechanism
// - Add BufSocket Model
// - Add TE_ConnectEx(...)
// - Add TE_BSocketGetData(...) for specail usage
// 3.1 : 2000-10-04 - Add TE_AcceptEx(...)
// - Add TE_GetIP(...) to fix NT DNS resolve cache problem
// - Modify TE_ConnectEx
// - Fix several bugs in NetEvent process
//
// Mailto : telan@263.net ( Bugs' Report or Comments )
// Notes : This source code may be used in any form in any way you desire. It is
// provided "as is" without express or implied warranty. Use it at your own
// risk! The author accepts no liability for any damage/loss of business
// that this product may cause.
//
////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "TE_Socket.h"
//需要ws2_32.lib庫
#pragma comment(lib,"ws2_32.lib")
//錯誤代號
static __declspec(thread) int _iThreadedErrorNo = 0;
// 輔助函數
//獲取最近一次操作的錯誤代碼
int TE_GetLastError()
{
return (_iThreadedErrorNo);
}
//設置錯誤代碼
void TE_SetLastError(int iErrorCode)
{
_iThreadedErrorNo = iErrorCode;
}
/////////////////////////////////////////////////////////////////////////
// Winsock2 函數
/////////////////////////////////////////////////////////////////////////
//初始化Winsock2動態連接庫
int TE_InitLibrary()
{
WSADATA WSD;
WORD wVersionRequired = MAKEWORD( TE_SOCKET_MAJOR_VERSION,TE_SOCKET_MINOR_VERSION );
ZeroMemory(&WSD,sizeof(WSADATA));
int nErrorNo = WSAStartup(wVersionRequired, &WSD);
if ( SOCKET_SUCCESS != nErrorNo )
{
TE_SetLastError( nErrorNo );
return ( SOCKET_ERROR );
}
if ( LOBYTE( WSD.wVersion ) != TE_SOCKET_MINOR_VERSION ||
HIBYTE( WSD.wVersion ) != TE_SOCKET_MAJOR_VERSION )
{
WSACleanup( );
TE_SetLastError( WSAVERNOTSUPPORTED );
return (SOCKET_ERROR);
}
//成功初始化
return (SOCKET_SUCCESS);
}
//釋放Winsock2動態連接庫
void TE_CleanupLibrary(void)
{
WSACleanup();
}
//設置套接字屬性
int TE_SetSocketOption(SOCKET hSocket)
{
int iActivate = 1;
//允許地址重用
if (setsockopt(hSocket, SOL_SOCKET, SO_REUSEADDR, (const char *) &iActivate,sizeof(iActivate)) == SOCKET_ERROR )
{
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
// 如果支持,設置KEEPALIVE屬性 (這樣做會帶來其他不良后果)
//setsockopt(hSocket, SOL_SOCKET, SO_KEEPALIVE, (const char *) &iActivate,sizeof(iActivate));
return (SOCKET_SUCCESS);
}
//創建具有重疊IO能力的套接字
SOCKET TE_CreateSocket(int iAddressFamily /*= AF_INET*/, int iType/*= SOCK_STREAM*/,int iProtocol/*= 0*/)
{
SOCKET hSocket = WSASocket(iAddressFamily, iType, iProtocol, NULL,0,WSA_FLAG_OVERLAPPED);
if ( hSocket == INVALID_SOCKET )
{
TE_SetLastError( WSAGetLastError() );
return (INVALID_SOCKET);
}
//設置套接字選項
if ( SOCKET_ERROR == TE_SetSocketOption(hSocket) ) //設置屬性失敗
{
TE_CloseSocket(hSocket, TRUE);
return (INVALID_SOCKET);
}
return (hSocket);
}
//關閉套接字
void TE_CloseSocket(SOCKET hSocket, BOOL bHardClose)
{
// 不需要捕獲錯誤
if (!bHardClose) // 優雅關閉 Graceful close
{
// 不再發送數據,對于TCP套接字,在所有的數據都發送完畢之后,
// 將發送一個 FIN ,通知接收方所有數據已經發送完畢。
shutdown(hSocket, SD_SEND);
// 接收緩沖區有可能還有未接收的數據,在關閉套接字之前應該先
// 讀取殘留的數據。
int iRecvResult;
HANDLE hSocketEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
char szBuffer[256];
do
{
if (hSocketEvent != NULL)
{
//注冊網絡事件
WSAEventSelect(hSocket, (WSAEVENT) hSocketEvent, FD_READ | FD_CLOSE);
WSAWaitForMultipleEvents(1, &hSocketEvent, TRUE,TE_SHUTDOWN_RECV_TIMEOUT, TRUE);
//清除網絡事件
WSAEventSelect(hSocket, (WSAEVENT) hSocketEvent, 0);
}
ZeroMemory(szBuffer,256);
iRecvResult = TE_RecvLL(hSocket, szBuffer, sizeof(szBuffer));
} while (iRecvResult > 0);
if (hSocketEvent != NULL)
CloseHandle(hSocketEvent);
//不再允許接收和發送
shutdown(hSocket, SD_BOTH);
}
// 關閉套接字
closesocket(hSocket);
}
// 一次性接收數據(重疊IO)
int TE_RecvLL(SOCKET hSocket, char *pszBuffer, int iBufferSize)
{
DWORD dwRtxBytes = 0,
dwRtxFlags = 0;
WSABUF WSABuff;
//清空緩沖
ZeroMemory(&WSABuff,sizeof(WSABUF));
WSABuff.len = iBufferSize;
WSABuff.buf = pszBuffer;
return ((WSARecv(hSocket, &WSABuff, 1, &dwRtxBytes, &dwRtxFlags,NULL, NULL) == SOCKET_SUCCESS) ? (int) dwRtxBytes : -WSAGetLastError());
}
// 接收數據(阻塞直至收到數據為止)
int TE_RecvData(SOCKET hSocket, char *pszBuffer, int iBufferSize, DWORD dwTimeout)
{
HANDLE hReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hReadEvent == NULL)
{
TE_SetLastError( (int)GetLastError() );
return ( SOCKET_ERROR );
}
int iRecvBytes = 0;
DWORD dwWaitResult ;
for (;;)
{
// 注冊FD_READ | FD_CLOSE 事件
// (因為可能在等待FD_READ事件中,對方關閉套接字,所以要關注FD_CLOSE)
if( WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
{
CloseHandle(hReadEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
// 等等FD_READ | FD_CLOSE事件的發生
dwWaitResult = WSAWaitForMultipleEvents(1, &hReadEvent, TRUE,dwTimeout, TRUE);
if (dwWaitResult != WSA_WAIT_EVENT_0)
{
// 清除事件
WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
CloseHandle(hReadEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
//////////////////////////////////////////////////////////////
/// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也應該
/// 進一步檢查網絡是否發生錯誤
///////////////////////////////////////////////////////////////
WSANETWORKEVENTS NetEvent;
if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hReadEvent,&NetEvent) == SOCKET_ERROR)
{
// 清除事件
WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
CloseHandle(hReadEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
//判斷發生了什么事件 FD_READ 或 FD_CLOSE
if( ( NetEvent.lNetworkEvents == FD_CLOSE ) ||
( NetEvent.lNetworkEvents == FD_READ &&
NetEvent.iErrorCode[FD_READ_BIT] !=0 ) ) // 發生錯誤
{
// 清除事件
WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
CloseHandle(hReadEvent);
TE_SetLastError(WSAGetLastError() );
return (SOCKET_ERROR);
}
////////////////////////////////////////////////////////////////
// 清除事件
WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
// 接收數據
if ((iRecvBytes = TE_RecvLL(hSocket, pszBuffer, iBufferSize)) >= 0)
break; // 跳出循環
int iErrorCode = -iRecvBytes;
if ( iErrorCode != WSAEWOULDBLOCK ) //太多的未完成重疊操作
{
CloseHandle(hReadEvent);
TE_SetLastError( iErrorCode );
return (SOCKET_ERROR);
}
//阻塞住了
////////////////////////////////////////////////////////////////////////
// 如果發生阻塞,就等待一定時間后重試,以免CPU輪詢浪費時間
////////////////////////////////////////////////////////////////////////
Sleep(TE_BLOCKED_SNDRCV_SLEEP);
}
CloseHandle(hReadEvent);
return (iRecvBytes);
}
// 接收數據直至達到規定的長度(緩沖區滿)或超時或沒有數據可讀取時
int TE_Recv(SOCKET hSocket, char *pszBuffer, int iBufferSize, DWORD dwTimeout)
{
int iRtxBytes = 0;
int iRtxCurrent = 0;
while (iRtxBytes < iBufferSize)
{
iRtxCurrent = TE_RecvData(hSocket, (pszBuffer + iRtxBytes),(iBufferSize - iRtxBytes), dwTimeout);
if (iRtxCurrent < 0) //沒有數據可讀取
return (iRtxBytes);
iRtxBytes += iRtxCurrent;
}
return (iRtxBytes);
}
//一次性發送數據(重疊IO)
int TE_SendLL(SOCKET hSocket, char const * pszBuffer, int iBufferSize)
{
DWORD dwRtxBytes = 0;
WSABUF WSABuff;
ZeroMemory(&WSABuff,sizeof(WSABUF));
WSABuff.len = iBufferSize;
WSABuff.buf = (char *) pszBuffer;
return ((WSASend(hSocket, &WSABuff, 1, &dwRtxBytes, 0,NULL, NULL) == SOCKET_SUCCESS) ? (int) dwRtxBytes : -WSAGetLastError());
}
// 發送數據
int TE_SendData(SOCKET hSocket, char const * pszBuffer, int iBufferSize, DWORD dwTimeout)
{
HANDLE hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hWriteEvent == NULL)
{
TE_SetLastError( (int)GetLastError() );
return (SOCKET_ERROR);
}
int iSendBytes = 0;
for (;;)
{
////////////////////////////////////////////////////////////////
// 發送數據成功
if ((iSendBytes = TE_SendLL(hSocket, pszBuffer, iBufferSize)) >= 0)
break;
int iErrorCode = -iSendBytes;
if (iErrorCode != WSAEWOULDBLOCK)
{
CloseHandle(hWriteEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
///////////////////////////////////////////////////////////////////////////////
// 睡眠一段時間
///////////////////////////////////////////////////////////////////////////////
Sleep(TE_BLOCKED_SNDRCV_SLEEP);
// 注冊FD_WRITE | FD_CLOSE 事件
if( WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, FD_WRITE|FD_CLOSE) == SOCKET_ERROR)
{
CloseHandle(hWriteEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
// 等待事件發生
DWORD dwWaitResult = WSAWaitForMultipleEvents(1, &hWriteEvent, TRUE,dwTimeout, TRUE);
if (dwWaitResult != WSA_WAIT_EVENT_0)
{
// 清除網絡事件
WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
CloseHandle(hWriteEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
//////////////////////////////////////////////////////////////
/// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也應該
/// 進一步檢查網絡是否發生錯誤
///////////////////////////////////////////////////////////////
WSANETWORKEVENTS NetEvent;
if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hWriteEvent,&NetEvent) == SOCKET_ERROR)
{
// 清除網絡事件
WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
CloseHandle(hWriteEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
if( ( NetEvent.lNetworkEvents == FD_CLOSE ) ||
( NetEvent.lNetworkEvents == FD_WRITE &&
NetEvent.iErrorCode[FD_WRITE_BIT] !=0 ) ) // 發生錯誤
{
// 清除網絡事件
WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
CloseHandle(hWriteEvent);
TE_SetLastError( WSAGetLastError() );
return (SOCKET_ERROR);
}
// 清除網絡事件
WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
}
CloseHandle(hWriteEvent);
return (iSendBytes);
}
//發送完所有數據或超時
int TE_Send(SOCKET hSocket, char const * pszBuffer, int iBufferSize, DWORD dwTimeout)
{
int iRtxBytes = 0;
int iRtxCurrent = 0;
while (iRtxBytes < iBufferSize)
{
iRtxCurrent = TE_SendData(hSocket, (pszBuffer + iRtxBytes),(iBufferSize - iRtxBytes), dwTimeout);
if (iRtxCurrent < 0)
return (iRtxBytes);
iRtxBytes += iRtxCurrent;
}
return (iRtxBytes);
}
// 建立連接
int TE_Connect(SOCKET hSocket, const struct sockaddr * pSocketAddress, int iNameLen,DWORD dwTimeout)
{
HANDLE hConnectEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hConnectEvent == NULL)
{
TE_SetLastError( (int)GetLastError() );
return (SOCKET_ERROR);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -