⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp.cpp

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