📄 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 + -