#include "StdAfx.h"
#include ".\bufferdsocket.h"
#include <ws2tcpip.h>

#define	BUF_SIZE			32768

CBufferdSocket::CBufferdSocket(void)
{
	m_socket = INVALID_SOCKET;

	//	イベント生成
	m_event.Create(TRUE,FALSE);
	m_break.Create(TRUE,FALSE);
	m_exBreakEvent = NULL;

	//	メモリの確保
	m_recvBuf.InitBuf(BUF_SIZE);
	m_sendBuf.InitBuf(BUF_SIZE);
}


CBufferdSocket::~CBufferdSocket(void)
{
	Close();
	m_recvBuf.Close();
	m_sendBuf.Close();
}

//////////////////////////////////////////////////////////////////////////////////
//	初期化など
//////////////////////////////////////////////////////////////////////////////////
/*!
	使用開始
*/
int CBufferdSocket::AttachSocket(SOCKET sock)
{
	//	終了
	Close();

	//	ソケットを非ブロッキングモードへ
	if(::WSAEventSelect(sock, m_event, FD_READ | FD_WRITE | FD_CLOSE))
	{
		CloseError(CBS_ERROR_OTHER,"AttachSocket");
		return(-1);
	}

	//	バッファクリア
	m_recvBuf.Clear();
	m_sendBuf.Clear();

	//	変数設定
	m_socket = sock;
	ClearError();

	//	イベントクリア
	::ResetEvent(m_event);
	::ResetEvent(m_break);

	return(0);
}


/*!
	閉じる
*/
void CBufferdSocket::Close()
{
	if(m_socket != INVALID_SOCKET)
	{
		//	ソケットからハンドルを関連付け解除
		::WSAEventSelect(m_socket, m_event, 0);

		//	ソケット無効化
		shutdown(m_socket,1);

		//	ソケットを閉じる
		closesocket(m_socket);
	}

	//	関連付け解除
	m_socket = INVALID_SOCKET;

	//	閉じた後も読み出す可能性があるので、バッファはオブジェクトが削除されるまで開放しない
}

//////////////////////////////////////////////////////////////////////////////////
//	中断インターフェース
//////////////////////////////////////////////////////////////////////////////////
/*!
	中断

	Abortのみ、別スレッドから呼び出し可	
*/
void CBufferdSocket::Abort()
{
	::SetEvent(m_break);
}


/*!
	中断イベントを設定
*/
void CBufferdSocket::SetExBreakEvent(HANDLE exBreakEvent)
{
	m_exBreakEvent = exBreakEvent;
}


//////////////////////////////////////////////////////////////////////////////////
//	送受信(非同期)
//////////////////////////////////////////////////////////////////////////////////
/*!
	送信
*/
int CBufferdSocket::WriteBuf(char *data,int len)
{
	//	エラー情報クリア
	ClearError();

	return(m_sendBuf.WriteBuf(data,len));
}


/*!
	受信
*/
int CBufferdSocket::ReadBuf(char *data,int len)
{
	//	エラー情報クリア
	ClearError();

	return(m_recvBuf.ReadBuf(data,len));
}


/*!
	受信(バッファを消さない)
*/
int CBufferdSocket::PeekBuf(char *data,int len)
{
	//	エラー情報クリア
	ClearError();

	return(m_recvBuf.PeekBuf(data,len));
}



/*!
	次のイベントまで待つ
	タイムアウト時間を指定すると、タイムアウトでエラーとなる

	戻り値: 0->正常終了, -1->エラー,タイムアウト
*/
int CBufferdSocket::WaitNextEvent(int waitTick)
{
	//	エラー情報クリア
	ClearError();

	//	ソケットは？
	if(IsDisconnect(FALSE))
	{
		CloseError(CBS_ERROR_CLOSE,"WaitNextEvent");
		return(-1);
	}

	//	イベント準備
	HANDLE	wait[3];
	wait[0] = m_event;
	wait[1] = m_break;

	//	外部中断イベントの設定
	int waitCount = 2;
	if(m_exBreakEvent)
	{
		wait[2] = m_exBreakEvent;
		waitCount ++;
	}

	//	イベントを待つ
	switch(::WaitForMultipleObjects(waitCount,wait,FALSE,waitTick))
	{
	case WAIT_OBJECT_0:
		break;

	case WAIT_OBJECT_0 + 1:
	case WAIT_OBJECT_0 + 2:
		CloseError(CBS_ERROR_BREAK,"WaitNextEvent - 中断");
		return(-1);

	case WAIT_TIMEOUT:
		if(waitTick != 0)
		{
			CloseError(CBS_ERROR_TIMEOUT,"WaitNextEvent - タイムアウト");
			return(-1);
		}
		break;

	default:
		CloseError(CBS_ERROR_OTHER,"WaitNextEvent - WaitForMultipleObjects");
		return(-1);
	}

	//	イベント識別
	WSANETWORKEVENTS	events;
	if(WSAEnumNetworkEvents(m_socket,m_event,&events) == SOCKET_ERROR)
	{
		CloseError(CBS_ERROR_OTHER,"WaitNextEvent - WSAEnumNetworkEvents");
		return(-1);	//	エラー
	}

	//	FD_CLOSE
	if(events.lNetworkEvents & FD_CLOSE)
	{
		CloseError(CBS_ERROR_CLOSE,"WaitNextEvent - FD_CLOSE");
		return(-1);	//	エラー
	}

	//	FD_READ
	if(events.lNetworkEvents & FD_READ)
	{
		switch(events.iErrorCode[FD_READ_BIT])
		{
		case 0:
			break;

		case WSAECONNRESET:
			CloseError(CBS_ERROR_CLOSE,"WaitNextEvent - FD_READ");
			return(-1);

		default:
			CloseError(CBS_ERROR_OTHER,"WaitNextEvent - FD_READ");
			return(-1);	//	エラー
		}
	}

	//	FD_WRITE
	if(events.lNetworkEvents & FD_WRITE)
	{
		switch(events.iErrorCode[FD_WRITE_BIT])
		{
		case 0:
			break;

		case WSAECONNRESET:
			CloseError(CBS_ERROR_CLOSE,"WaitNextEvent - FD_WRITE");
			return(-1);

		default:
			CloseError(CBS_ERROR_OTHER,"WaitNextEvent - FD_WRITE");
			return(-1);	//	エラー
		}
	}

	return(0);
}

//////////////////////////////////////////////////////////////////////////////////
//	通信ステータス
//////////////////////////////////////////////////////////////////////////////////
/*!
	切断しているか取得
*/
int CBufferdSocket::IsDisconnect(int tryRecv)
{
	//	イベントを待ってみる
	if(tryRecv)
		WaitNextEvent();

	//	中断？
	if(::WaitForSingleObject(m_break, 0) == WAIT_OBJECT_0)
		return(1);

	//	切断？
	if(m_socket == INVALID_SOCKET)
		return(1);
	return(0);
}

/*!
	バッファサイズ取得
*/
int CBufferdSocket::GetSendRecvBufSize()
{
	return(BUF_SIZE);
}

//////////////////////////////////////////////////////////////////////////////////
//	接続情報
//////////////////////////////////////////////////////////////////////////////////
/*!
	実行中のPCのアドレスを得る
*/
CIPAddress CBufferdSocket::GetLocalhost()
{
	//	ダミーのソケットを生成
	SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
	if(sd == SOCKET_ERROR)
		return(0);

	INTERFACE_INFO InterfaceList[20];
	unsigned long nBytesReturned;
	if(WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList,
		sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR)
		return(0);

	int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
	if(nNumInterfaces <= 0)
		return(0);
	
	sockaddr_in *pAddress;
	pAddress = (sockaddr_in *) & (InterfaceList[0].iiAddress);
	return(pAddress->sin_addr);
}

/*!
	自分のアドレスを取得する
*/
CIPAddress CBufferdSocket::GetAddres()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getsockname(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(addr.sin_addr);
}

/*!
	相手のアドレスを取得する
*/
CIPAddress CBufferdSocket::GetPeerAddres()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getpeername(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(addr.sin_addr);
}

/*!
	自分のポートを取得する	
*/
int CBufferdSocket::GetPort()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getsockname(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(ntohs(addr.sin_port));
}

/*!
	相手のポートを取得する
*/
int CBufferdSocket::GetPeerPort()
{
	sockaddr_in	addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int nSockAddrLen = sizeof(sockaddr_in);

	getpeername(m_socket,(SOCKADDR*)&addr,&nSockAddrLen);
	return(ntohs(addr.sin_port));
}


//////////////////////////////////////////////////////////////////////////////////
//	通信処理
//////////////////////////////////////////////////////////////////////////////////
/*!
	送信を試す

	\return	-1:エラー、0:送信完了、>0:残りデータ量
*/
int CBufferdSocket::BlockingSend(int timeOut)
{
	//	データがあるならループ
	while(m_sendBuf.GetInBuf() > 0)
	{
		//	送信用バッファ取得
		int		bufLen;
		char	*sendBuf = m_sendBuf.GetReadBuffer(&bufLen);

		//	送信してみる
		int ret = send(m_socket, sendBuf, bufLen, 0);
		if(ret == SOCKET_ERROR)
		{
			//	エラーの状況は？
			switch(WSAGetLastError())
			{
			//	ブロックされた
			case WSAEWOULDBLOCK:
				break;

			//	閉じられた
			case WSAECONNRESET:
				CloseError(CBS_ERROR_CLOSE,"TrySend");
				return(-1);

			//	エラー
			default:
				CloseError(CBS_ERROR_OTHER,"TrySend");
				return(-1);
			}
		}
		else
		{
			//	送信したデータ量を設定
			m_sendBuf.SetReadedLen(ret);
			return(m_sendBuf.GetInBuf());
		}

		//	イベントを待つ
		if(WaitNextEvent(timeOut))
			return(-1);
	}

	return(0);
}


/*!
	受信を試す

	\return	-1:エラー、0:受信しなかった、>0:受信データ量
*/
int CBufferdSocket::BlockingRecv(int timeOut)
{
	int	firstRecv = 0;

	//	バッファがあるならループ
	while(m_recvBuf.GetFreeBuf() > 0)
	{
		//	受信用バッファ取得
		int		bufLen;
		char	*recvBuf = m_recvBuf.GetWriteBuffer(&bufLen);

		//	送信してみる
		int ret = recv(m_socket, recvBuf, bufLen, 0);
		if(ret == SOCKET_ERROR)
		{
			//	エラーの状況は？
			switch(WSAGetLastError())
			{
			//	ブロックされた
			case WSAEWOULDBLOCK:
				if(firstRecv)
					return(m_recvBuf.GetInBuf());	//	データ受信済みなら終了
				break;

			//	閉じられた
			case WSAECONNRESET:
				CloseError(CBS_ERROR_CLOSE,"TryRecv");
				return(-1);

			//	エラー
			default:
				CloseError(CBS_ERROR_OTHER,"TryRecv");
				return(-1);
			}
		}
		else if(ret == 0)
		{
			CloseError(CBS_ERROR_CLOSE,"TryRecv");
			return(-1);
		}
		else
		{
			//	送信したデータ量を設定
			m_recvBuf.SetWritedLen(ret);
			firstRecv = TRUE;
			continue;
		}

		//	イベントを待つ
		if(WaitNextEvent(timeOut))
			return(-1);
	}

	return(m_recvBuf.GetInBuf());
}


//////////////////////////////////////////////////////////////////////////////////
//	エラー
//////////////////////////////////////////////////////////////////////////////////
/*!
	エラーで閉じる
*/
void CBufferdSocket::CloseError(int code, CString debugInfo)
{
	TRACE("SocketError : %d [%s] (SocketError : %d)\n", code, debugInfo, WSAGetLastError());
	m_lastError = code;

	//	ソケットを閉じる
	if(m_socket != INVALID_SOCKET)
		Close();
}

/*!
	エラーを文字列で取得
*/
CString CBufferdSocket::GetLastErrorString()
{
	return(GetErrorString(GetLastError()));
}

/*!
	エラーコードから文字列を取得
*/
CString CBufferdSocket::GetErrorString(int errorCode)
{
	switch(errorCode)
	{
	case CBS_ERROR_TIMEOUT:
		return(_T("タイムアウトしました"));

	case CBS_ERROR_CLOSE:
		return(_T("接続が終了しました"));

	case CBS_ERROR_BREAK:
		return(_T("中断しました"));

	case CBS_ERROR_OTHER:
		return(_T("ネットワークエラーです"));


	case CBS_NO_ERROR:
	default:
		return(_T("不明なエラーです"));
	}
}
