/*
	CMySocket
	Socket処理を行う

	Version		: 0.0 (Release1)

	作成日		: 2002/05/25 
	修正日		: ----/--/--
	改定履歴	: 
*/

#include "stdafx.h"
#include "MySocket.h"
#include <winsock2.h>
#include <ws2tcpip.h>


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define	STRING_BUF_SIZE	32767
#define	PEEK_BUF_SIZE	65536

#define	TIME_OUT_TIME	0	//標準を使用

static int		_initSocketApi = 0;
static WSADATA	_winsockData;

//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////

CMySocket::CMySocket()
{
	m_disconnect = 0;
	m_connect = 0;
	m_peekBufUsed = 0;
	m_timeoutTick = TIME_OUT_TIME;
	m_stringBuf = new char[STRING_BUF_SIZE];
	m_peekBuf = new char[PEEK_BUF_SIZE];

	InitApi();
}

CMySocket::~CMySocket()
{
	Close();
	delete m_stringBuf;
	delete m_peekBuf;
}

int CMySocket::InitApi()
{
	if(_initSocketApi == 0)
	{
		int nResult;
		WORD wRequired;

		//	1.1を要求
		wRequired=MAKEWORD(1,1);
		nResult=WSAStartup(wRequired,&_winsockData);

		if(_winsockData.wVersion != wRequired)
		{
			return(-1);
		}
		if(nResult)
		{
			return(-1);
		}

		_initSocketApi = 1;
	}
	return(0);
}

WSADATA * CMySocket::GetWinsockInfo()
{
	if(!InitApi())
		return(&_winsockData);
	return(NULL);
}

int CMySocket::Create()
{
	//	ソケット生成
	if((m_socket=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
		ErrorReturn(_T("ソケットの生成に失敗しました。"),-3);
	m_connect = 1;
	m_disconnect = 0;
	return(0);
}

//////////////////////////////////////////////////////////////////////
// 接続
//////////////////////////////////////////////////////////////////////
int CMySocket::Connect(CString server, int port)
{
	Close();

	//	初期化
	if(InitApi())
		ErrorReturn(_T("WinSockが使用できません。"),-1);

	//	ホスト名解決
	in_addr		iadr;
	HOSTENT		*phost;
	SOCKADDR_IN	sai;

	iadr.s_addr=inet_addr(server.GetBuffer(0));
	if(iadr.s_addr==INADDR_NONE)
	{
		//	ドメイン名
		phost=gethostbyname(server.GetBuffer(0));
		if(!phost)
		{
			CString	temp;
			temp.Format(_T(" ホストが見つかりませんでした [%s]"),server);
			ErrorReturn(temp,-2);
		}

		sai.sin_addr=*((LPIN_ADDR)*phost->h_addr_list); 
	}
	else
	{
		//	IP
		sai.sin_addr=iadr; 
	}

	sai.sin_port=htons((u_short)port);
	sai.sin_family=AF_INET;

	//	生成
	if(Create())
		return(-1);

	//	接続
	SetTimeOut(m_timeoutTick);
	if(connect(m_socket,(LPSOCKADDR)&sai,sizeof(sai)))
	{
		StopTimeOut();
		closesocket(m_socket);

		CString	temp;
		temp.Format(_T(" ホストに接続できませんでした。 [%s:%d]"),server,port);
		ErrorReturn(temp,-2);
	}
	StopTimeOut();

	m_disconnect = 0;
	return(0);
}

//	閉じる
void CMySocket::Close()
{
	if(m_connect)
	{
		closesocket(m_socket);
		StopTimeOut();
		m_connect = 0;
		m_disconnect = 1;
		m_peekBufUsed = 0;
	}
}


//	ハンドルより
int CMySocket::SetHandle(SOCKET socket)
{
	Close();

	m_socket = socket;
	m_connect = 1;
	m_disconnect = 0;

	return(0);
}

//////////////////////////////////////////////////////////////////////
// 転送
//////////////////////////////////////////////////////////////////////
//	受信する
int CMySocket::Recv(char *data, int len)
{
	int	ret = RecvToPeekBuf();

	//	エラー + データなし
	if(ret < 0 && m_peekBufUsed == 0)
		return(ret);

	len = GetPeekBuf(data,len);

	return(len);
}

//	受信する(文字列)
int CMySocket::RecvString(CString &str,int recvLen)
{
	if(recvLen == 0 || recvLen >= STRING_BUF_SIZE)
		recvLen = STRING_BUF_SIZE - 1;

	int	ret = Recv(m_stringBuf,recvLen);

	if(ret>=0)
	{
		m_stringBuf[ret] = 0;
		str = m_stringBuf;
	}

	return(ret);
}

//	受信する(バッファを消さない)
int CMySocket::Peek(char *data, int len)
{
	int	ret = RecvToPeekBuf();

	//	エラー
	if(ret < 0)
		return(ret);

	len = PeekPeekBuf(data,len);

	return(len);
}

//	受信する(バッファを消さない、文字列)
int CMySocket::PeekString(CString &str)
{
	int	ret = Peek(m_stringBuf,STRING_BUF_SIZE - 1);

	if(ret>=0)
	{
		m_stringBuf[ret] = 0;
		str = m_stringBuf;
	}

	return(ret);
}

//	送信する
int CMySocket::Send(char *data, int len)
{
	if(IsDisconnect())
		ErrorReturn("回線は切断されています。",-1);

	SetTimeOut(m_timeoutTick);
	int	ret = _send(m_socket,data,len,0);
	StopTimeOut();

	if(ret == SOCKET_ERROR)
	{
		ErrorReturn("データ送信に失敗しました。",-1);
	}
	else
	{
		return(0);
	}
}

//	文字列を送信する
int CMySocket::SendString(CString str)
{
	return(Send(str.GetBuffer(0),str.GetLength()));
}

//////////////////////////////////////////////////////////////////////
// IP、ポート
//////////////////////////////////////////////////////////////////////
//	ポート番号をえる
int CMySocket::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));
}

//	アドレスを得る
CIPAddress CMySocket::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 CMySocket::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 CMySocket::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));
}

//	バッファ中にあるデータを取得
int CMySocket::GetInRecvBuf()
{
	int	ret = RecvToPeekBuf();

	//	エラー + データなし
	if(ret < 0 && m_peekBufUsed == 0)
		return(ret);

	return(m_peekBufUsed);
}

//////////////////////////////////////////////////////////////////////
// 接続(受信)
//////////////////////////////////////////////////////////////////////
//	接続開始
int CMySocket::Listen(int port)
{
	if(Create())
		return(-1);

	if(Bind(port))
		return(-1);

	if(listen(m_socket,5)==0)
		return(0);
	ErrorReturn("ポート監視に失敗しました。",-1);
}

//	割り当てる
int CMySocket::Bind(int port)
{
	SOCKADDR_IN sockAddr;
	memset(&sockAddr,0,sizeof(sockAddr));

	sockAddr.sin_family = AF_INET;

	sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	sockAddr.sin_port = htons((u_short)port);

	if(bind(m_socket,(SOCKADDR*)&sockAddr, sizeof(sockAddr))==0)
		return(0);
	ErrorReturn("ポート割り当てに失敗しました。",-1);
}


//	接続許可
int CMySocket::Accept(CMySocket &recv,int blocking)
{
	if(CanRecv()==0 && blocking==FALSE)
		return(0);

	SOCKET hTemp = accept(m_socket, NULL, NULL);

	if (hTemp == INVALID_SOCKET)
	{
//		int ret = WSAGetLastError();
		ErrorReturn("接続許可に失敗しました。",-1);
	}
	else
	{
		if(recv.SetHandle(hTemp))
			return(-1);
		return(1);
	}

	return(0);
}



//	受信可能か？
int CMySocket::CanRecv(int timeOutMs)
{
	//	PeekBufに残っているか？
	if(m_peekBufUsed)
		return(1);

	if(RecvDataPending(timeOutMs))
		return(1);

	return(0);
}

//	データは来ているのか？
int CMySocket::RecvDataPending(int timeOut)
{
	struct timeval	timeout = {0, 0};
	fd_set			rfds, wfds, efds;

	timeout.tv_usec = timeOut;

	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);
	FD_SET(m_socket, &rfds);
	FD_SET(m_socket, &wfds);
	FD_SET(m_socket, &efds);
	
	int n = select((int)m_socket +1, &rfds, &wfds, &efds, &timeout);
	if(n > 0)
	{
		if(FD_ISSET(m_socket, &rfds))
		{
			return(1);
		}
	}

	return(0);
}

//	書き込み可能か？
int CMySocket::CanWrite(int timeOutMs)
{
	struct timeval	timeout = {0, 0};
	fd_set			rfds, wfds, efds;

	timeout.tv_usec = timeOutMs;

	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);
	FD_SET(m_socket, &rfds);
	FD_SET(m_socket, &wfds);
	FD_SET(m_socket, &efds);
	
	int n = select((int)m_socket +1, &rfds, &wfds, &efds, &timeout);
	if(n > 0)
	{
		if(FD_ISSET(m_socket, &wfds))
		{
			return(1);
		}
	}

	return(0);
}


//	エラー？
int CMySocket::IsError()
{
	struct timeval	timeout = {0, 0};
	fd_set			rfds, wfds, efds;

	timeout.tv_usec = 0;

	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);
	FD_SET(m_socket, &rfds);
	FD_SET(m_socket, &wfds);
	FD_SET(m_socket, &efds);
	
	int n = select((int)m_socket +1, &rfds, &wfds, &efds, &timeout);
	if(n > 0)
	{
		if(FD_ISSET(m_socket, &efds))
		{
			return(1);
		}
	}

	return(0);
}

//	切断された？
int CMySocket::IsDisconnect()
{
	//	ダミー読み込み
	char	temp[10];
	Peek(temp,9);

	return(m_disconnect);
}

//	切断されて、データも無いの？
int CMySocket::IsDisconnectAndNodata()
{
	if(IsDisconnect() == 0)
		return(0);

	if(m_peekBufUsed)
		return(0);
	return(1);
}

//////////////////////////////////////////////////////////////////////
// Peek処理
//////////////////////////////////////////////////////////////////////
//	PeekBufの残りは？
int CMySocket::GetPeekBufLeft()
{
	return(PEEK_BUF_SIZE - m_peekBufUsed);
}

//	バッファ一杯?
int CMySocket::PeekBufIsFull()
{
	if(GetPeekBufLeft() == 0)
		return(1);
	return(0);
}

//	バッファ使用量取得
int CMySocket::InPeekBuf()
{
	return(m_peekBufUsed);
}

//	PeekBufに追加
void CMySocket::AddPeekBuf(char *data, int len)
{
	if(GetPeekBufLeft() >= len)
	{
		memcpy(m_peekBuf + m_peekBufUsed,data,len);
		m_peekBufUsed += len;
	}
}

//	PeekBufを取得
int CMySocket::GetPeekBuf(char *data, int len)
{
	if(len > m_peekBufUsed)
		len = m_peekBufUsed;

	memcpy(data,m_peekBuf,len);

	if(m_peekBufUsed > len)
	{
		memmove(m_peekBuf,m_peekBuf + len,m_peekBufUsed - len);
	}
	m_peekBufUsed -= len;

	return(len);
}

//	PeekBuf読み込み
int CMySocket::PeekPeekBuf(char *buf, int len)
{
	if(len > m_peekBufUsed)
		len = m_peekBufUsed;

	memcpy(buf,m_peekBuf,len);
	return(len);
}

//	PeekBufへ受信
int CMySocket::RecvToPeekBuf()
{
	//	PeekBufが一杯？
	if(GetPeekBufLeft() == 0)
		return(0);

/*
	struct timeval	timeout = {0, 0};
	fd_set			rfds, wfds, efds;

	timeout.tv_usec = 0;
	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);
	FD_SET(m_socket, &rfds);
	FD_SET(m_socket, &wfds);
	FD_SET(m_socket, &efds);
	
	int n = select((int)m_socket +1, &rfds, &wfds, &efds, &timeout);
	if(n > 0)
	{
		if(FD_ISSET(m_socket, &rfds))
		{
			SetTimeOut(m_timeoutTick);
			int	ret = _recv(m_socket,m_peekBuf + m_peekBufUsed,GetPeekBufLeft(),0);
			StopTimeOut();

			if(ret == SOCKET_ERROR)
			{
				ErrorReturn("データ受信に失敗しました。",-1);
			}
			else if(ret == 0)
			{
				m_disconnect = 1;
			}
			else
			{
				m_peekBufUsed += ret;
				return(0);
			}
		}
	}
	else if(m_disconnect)
		ErrorReturn("回線は切断されています。",-1);
*/

	if(RecvDataPending())
	{
		SetTimeOut(m_timeoutTick);
		int	ret = _recv(m_socket,m_peekBuf + m_peekBufUsed,GetPeekBufLeft(),0);
		StopTimeOut();

		if(ret == SOCKET_ERROR)
		{
			ErrorReturn("データ受信に失敗しました。",-1);
		}
		else if(ret == 0)
		{
			m_disconnect = 1;
		}
		else
		{
			m_peekBufUsed += ret;
			return(0);
		}
	}
	else if(m_disconnect)
		ErrorReturn("回線は切断されています。",-1);

	return(0);
}

//	タイムアウト時間を設定
void CMySocket::SetTimeOutTick(int timeOut)
{
	m_timeoutTick = timeOut;
}

//	タイムアウト
void CMySocket::OnTimeOut()
{
	Close();
	m_lastErrorString.Format("タイムアウトしました");
	CTimeOut::OnTimeOut();
}

//	IPアドレスを取得
CIPAddress CMySocket::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);
}

//////////////////////////////////////////////////////////////////////
// オーバーロード
//////////////////////////////////////////////////////////////////////
int CMySocket::_send(SOCKET s,char *buf,int len,int flags)
{
	return send(s,buf,len,flags);
}

int CMySocket::_recv(SOCKET s,char *buf,int len,int flags)
{
	return recv(s,buf,len,flags);
}

