?? tcp.cpp
字號:
// TCP.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include "TCP.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define CLIENET_MAGIC_WORD 300 //客戶端校驗位
#define SERVER_MAGIC_WORD 500 //服務端校驗位
BEGIN_MESSAGE_MAP(CTCPApp, CWinApp)
//{{AFX_MSG_MAP(CTCPApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTCPApp construction
CTCPApp::CTCPApp()
{
this->m_list.clear();
}
CTCPApp::~CTCPApp()
{
this->m_list.clear();
}
BOOL CTCPApp::RemoveList(int handle)
{
THREAD td;
for(THREAD_LIST::iterator itr = this->m_list.begin(); itr != this->m_list.end();itr++)
{
td = *itr;
if(td.s == handle)
{
this->m_list.erase(itr); return TRUE;
}
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CTCPApp object
CTCPApp theApp;
/////////////////////////////////////////////////////////////////////////////
// CTCPApp initialization
//開始進入dll
BOOL CTCPApp::InitInstance()
{
if (!AfxSocketInit())
{
CString msg;
msg.LoadString(IDP_SOCKETS_INIT_FAILED);
return MessageBox(NULL,msg + "\r\n\r\n按確定退出程序","賽力科技",MB_ICONSTOP | MB_OK) !=IDOK;
}
return TRUE;
}
//退出進入dll
int CTCPApp::ExitInstance()
{
THREAD_LIST::iterator itr;
while(!this->m_list.empty())
{
itr = this->m_list.begin();//取第一個值
::closesocket((*itr).s);//關閉socket
::WaitForSingleObject((*itr).hThread,INFINITE);//等待線程結束
this->m_list.erase(itr);
}
return CWinApp::ExitInstance();
}
//服務器收到一個連接
DWORD WINAPI CTCPApp::OnAccept(void *wParam)
{
INFO *info =(INFO *)wParam;
sockaddr_in addr;
int len=sizeof(addr);
for(;;)
{
SOCKET as = ::accept(info->s,(SOCKADDR *)&addr,&len);
if(as < 0)//出錯
break;
else //連接成功。通過回調函數告訴程序有新的連接連上
{
INFO *in = new INFO;//為每一個新連接的socket重定義信息
ASSERT(in != NULL);
in->Receive = info->Receive;//接收線程
in->s = as;//新連接的socket
in->wParam = info->wParam;//用戶自定義的參數
//啟動線程進行監聽連接
THREAD thread;
DWORD id;
thread.hThread = ::CreateThread(NULL,NULL,CTCPApp::Server,in,0,&id);
thread.s = as;
theApp.m_list.push_back(thread);//保留線程句柄和該線程函數的socket
}
}
theApp.RemoveList(info->s);
closesocket(info->s);
delete info;
::ExitThread(0);
return 0;
}
//服務器接收線程
DWORD WINAPI CTCPApp::Server(void *wParam)
{
INFO *info =(INFO *)wParam;
ASSERT(info->Receive != NULL);
//驗證 ==>> 三秒
struct timeval time = {3,0};
fd_set fd;
FD_ZERO(&fd);
FD_SET(info->s,&fd);
DWORD magic = SERVER_MAGIC_WORD;//服務端
if(::select(0,NULL,&fd,NULL,&time) < 0 || //最長等待3秒鐘
::send(info->s,(char *)&magic,sizeof(DWORD),0) != sizeof(DWORD))//發送出錯
{
::closesocket(info->s); goto end;
}
if(::select(0,&fd,NULL,NULL,&time) < 0 || //最長等待三秒鐘
::recv(info->s,(char *)&magic,sizeof(DWORD),0) != sizeof(DWORD) || //接收出錯
magic != CLIENET_MAGIC_WORD) //校驗位不對
{
::closesocket(info->s); goto end;
}
//外部循環工作,返回true則關閉socket
if(info->Receive(info->wParam,info->s))
::closesocket(info->s);
end://退出工作
theApp.RemoveList(info->s);
delete info;
::ExitThread(0);
return TRUE;
}
//客戶端接收線程
DWORD WINAPI CTCPApp::Client(void *wParam)
{
INFO *info =(INFO *)wParam;
ASSERT(info->Receive != NULL);
//外部循環工作.如果返回true則關閉socket
if(info->Receive(info->wParam,info->s))
::closesocket(info->s);
//退出工作
theApp.RemoveList(info->s);
delete info;
::ExitThread(0);
return TRUE;
}
//服務端,開始創建服務器
extern "C" __declspec(dllexport) TCPHANDLE CreateTCP(BOOL (*OnServer)(void *wParam,TCPHANDLE handle),//進到線程后的工作
void *wParam,
const int port)
{
ASSERT( OnServer != NULL && wParam != NULL);//要保證回調函數不能為空
ASSERT( port > 0);//端口號要大于零
sockaddr_in addr;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_family=AF_INET;
addr.sin_port=::htons(port);
//創建套接字
SOCKET s = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
MessageBox(NULL,"創建套接字失敗","賽力科技",MB_ICONSTOP|MB_OK);
return INVALID_SOCKET;
}
//綁定套接字
if(::bind(s,(SOCKADDR *)&addr,sizeof(addr)) == SOCKET_ERROR)
{
MessageBox(NULL,"綁定套接字失敗","賽力科技",MB_ICONSTOP|MB_OK);
closesocket( s ); return INVALID_SOCKET;
}
//監聽套接字
if(::listen(s,5)==SOCKET_ERROR)
{
MessageBox(NULL,"監聽套接字失敗","賽力科技",MB_ICONSTOP|MB_OK);
closesocket( s ) ;return INVALID_SOCKET;
}
//保存用戶回調函數的信息
INFO *info = new INFO;
info->s = s;
info->Receive = OnServer;
info->wParam = wParam;
//啟動線程進行監聽連接
THREAD thread;
DWORD id;
thread.hThread = ::CreateThread(NULL,NULL,CTCPApp::OnAccept,info,0,&id);
thread.s = s;
theApp.m_list.push_back(thread);//保留線程句柄和該線程函數的socket
return s;
}
//客戶端連接
extern "C" __declspec(dllexport) TCPHANDLE ConnectTCP(BOOL (*OnClient)(void *wParam,TCPHANDLE handle),
void *wParam,
LPCSTR ip,
const int port)
{
ASSERT( ip != NULL && port > 0);
sockaddr_in addr;
addr.sin_addr.s_addr=::inet_addr(ip);
addr.sin_family=AF_INET;
addr.sin_port=::htons(port);
//創建套接字
SOCKET s = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
MessageBox(NULL,"創建套接字失敗","賽力科技",MB_ICONSTOP|MB_OK);
return INVALID_SOCKET;
}
//連接
if(::connect(s,(SOCKADDR *)&addr,sizeof(addr)) == SOCKET_ERROR)
{
MessageBox(NULL,"連接服務器失敗","賽力科技",MB_ICONSTOP|MB_OK);
closesocket(s);return INVALID_SOCKET;
}
//驗證 ==>> 三秒
struct timeval time = {3,0};
fd_set fd;
FD_ZERO(&fd);
FD_SET(s,&fd);
DWORD magic = CLIENET_MAGIC_WORD;//客戶端
if(::select(0,NULL,&fd,NULL,&time) < 0 || //最長等待3秒鐘
::send(s,(char *)&magic,sizeof(DWORD),0) != sizeof(DWORD))
{
MessageBox(NULL,"連接上了非法的服務器","賽力科技",MB_ICONSTOP|MB_OK);
closesocket(s); return INVALID_SOCKET;
}
if(::select(0,&fd,NULL,NULL,&time) < 0 || //最長等待三秒鐘
::recv(s,(char *)&magic,sizeof(DWORD),0) != sizeof(DWORD) || //接收出錯
magic != SERVER_MAGIC_WORD) //校驗位不對
{
MessageBox(NULL,"連接上了非法的服務器","賽力科技",MB_ICONSTOP|MB_OK);
closesocket(s); return INVALID_SOCKET;
}
//如果客戶端要接收數據
if(OnClient)
{
INFO *info = new INFO;
ASSERT(info != NULL);
info->Receive = OnClient;
info->wParam = wParam;
info->s = s;
//啟動線程
THREAD thread;
DWORD id;
thread.hThread = ::CreateThread(NULL,NULL,CTCPApp::Client,info,0,&id);
thread.s = s;
theApp.m_list.push_back(thread);//保留線程句柄和該線程函數的socket
}
return s;
}
//發送
extern "C" __declspec(dllexport) BOOL SendTCP(TCPHANDLE handle,char *buffer,struct timeval *timeout = NULL)
{
DWORD size = *(DWORD *)buffer;//頭四個字節表明包的長度,包括四個字節
fd_set fd;
FD_ZERO(&fd);FD_SET(handle,&fd);
UINT send_size=0;
while(send_size < size)
if(::select(0,NULL,&fd,NULL,timeout) > 0)
{
int ret=::send(handle,buffer + send_size,size - send_size,0);
if( ret <= 0 )
break;
send_size += ret;
}
else
break;
if(send_size < size) ::closesocket(handle);//發送錯誤,關閉socket
return send_size >= size;
}
//接收,包的長度是前四個字節。包括四個字節占的空間
extern "C" __declspec(dllexport) BOOL ReceiveTCP(TCPHANDLE handle,char *buffer,struct timeval *timeout = NULL)
{
DWORD len = 0;
DWORD recv_length = sizeof(DWORD);//已經讀取了四個字節的長度
fd_set fd;
FD_ZERO(&fd);
FD_SET(handle,&fd);
if( ::select(0,&fd,NULL,NULL,timeout) > 0 )
{
int ret=::recv(handle,buffer,sizeof(DWORD),0);
if(ret == sizeof(DWORD))//讀取頭4個字節。表明包的長度
{
len=*(DWORD *)buffer;//讀取4字節判斷包的長度
while( len > recv_length )
{
int ret = ::recv( handle , buffer + recv_length , len - recv_length , 0 );
if( ret <= 0 ) //接收過程中出錯
break;
recv_length += ret;
}
}
}
if(len == 0 || recv_length < len) ::closesocket(handle);//接收錯誤,關閉socket
return len && recv_length >= len;
}
//從一個已經連接的socket得到服務器的ip地址
extern "C" __declspec(dllexport) char * GetHostIPAddr(TCPHANDLE handle)
{
sockaddr_in addr;
int len = sizeof(addr);
::getpeername(handle,(SOCKADDR *)&addr,&len);
return ::inet_ntoa(addr.sin_addr);
}
//從一個已經連接好的socket得到本機ip地址
extern "C" __declspec(dllexport) char *GetLocalIPAddr(TCPHANDLE handle)
{
sockaddr_in addr;
int len = sizeof(addr);
::getsockname(handle,(SOCKADDR *)&addr,&len);
return ::inet_ntoa(addr.sin_addr);
}
//關閉tcp的socket
extern "C" __declspec(dllexport) BOOL CloseTCP(void *handle)
{
TCPHANDLE *h = (TCPHANDLE *)handle;
closesocket(*h); *h = -1;
return TRUE;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -