WSAAsyncSelect模型

本文详细介绍了TCP服务器和客户端的通信实现过程,包括初始化套接字、监听、接受连接、读取数据、关闭连接等关键步骤。通过实例代码演示了如何在Windows环境下创建TCP服务器和客户端,并进行数据交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



TCPServer.cpp

#include "TCPServer.h"
#include "resource.h"

#define WM_SOCKET WM_USER+1

CMyApp theApp;

BOOL CMyApp::InitInstance()
{
	//初始化套接字
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD(2,0);
	::WSAStartup(wVersionRequested, &wsaData);
	//显示对话框
	CMainDialog dlg;
	m_pMainWnd = &dlg;
	dlg.DoModal();
	//释放套接字
	::WSACleanup();
	return FALSE;
}

//CMainDialog
CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_MAINDIALOG,pParentWnd)
{

}
BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
ON_BN_CLICKED(IDC_START, OnStart)
ON_BN_CLICKED(IDC_CLEAR, OnClear)
ON_MESSAGE(WM_SOCKET, OnSocket)
END_MESSAGE_MAP()

void CMainDialog::OnCancel()
{
	this->CloseAllSocket();
	CDialog::OnCancel();
}

BOOL CMainDialog::OnInitDialog()
{
	CDialog::OnInitDialog();

	//设置图标
	SetIcon(theApp.LoadIconA(IDI_MAIN), FALSE);

	//创建状态栏并设置其属性
	m_bar.Create(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP, CRect(0,0,0,0), this, 101);
	m_bar.SetBkColor(RGB(0xa6, 0xca, 0xfa));
	int arWidth[]={200,-1};
	m_bar.SetParts(2, arWidth);
	m_bar.SetText("windows程序设计", 1, 0);
	m_bar.SetText("空闲", 0, 0);
	//关联列表控件
	m_listInfo.SubclassDlgItem(IDC_LIST, this);

	//初始化套接字和连接列表
	m_socket = INVALID_SOCKET;
	m_nClient = 0;

	//取得本机IP,在状态栏中显示
	char szHostName[MAX_PATH] = {0};
	::gethostname(szHostName, MAX_PATH);
	hostent *pHost = gethostbyname(szHostName);
	if (pHost != NULL)
	{
		CString strIP;
		in_addr* addr = (in_addr*)*pHost->h_addr_list;
		strIP.Format("本机IP:%s",inet_ntoa(addr[0]));
		m_bar.SetText(strIP, 0, 0);
	}
	return TRUE;
}

BOOL CMainDialog::CreateAndListen(int nPort)
{
	if (m_socket == INVALID_SOCKET)
	{
		::closesocket(m_socket);
	}
	//创建套接字
	m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_socket == INVALID_SOCKET)
	{
		return FALSE;
	}
	//绑定端口
	sockaddr_in	sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(nPort);
	//sin.sin_addr.S_un.S_addr = INADDR_ANY;
	sin.sin_addr.s_addr = INADDR_ANY;
	int nErr = GetLastError();
	if (::bind(m_socket, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
		nErr = GetLastError();
		return FALSE;
	}
	::WSAAsyncSelect(m_socket, m_hWnd, WM_SOCKET, FD_ACCEPT|FD_CLOSE|FD_READ);

	//进入监听模式
	::listen(m_socket, 5);

	return TRUE;
}

BOOL CMainDialog::AddClient(SOCKET s)
{
	
	if (m_nClient < MAX_SOCKET)
	{
		m_arClient[m_nClient++] = s;
		return TRUE;
	}
	return FALSE;
	
}

void CMainDialog::RemoveClient(SOCKET s)
{
	BOOL bFound = FALSE;
	int i;
	for (i=0;i<m_nClient;i++)
	{
		if (m_arClient[i] == s)
		{
			bFound = TRUE;
			break;
		}
	}

	//找到
	if (bFound)
	{
		m_nClient--;
		for (int j=i;j<m_nClient;j++)
		{
			m_arClient[j] = m_arClient[j+1];
		}
	}
}
void CMainDialog::CloseAllSocket()
{
	if (m_socket != INVALID_SOCKET)
	{
		::closesocket(m_socket);
		m_socket = INVALID_SOCKET;
	}
	for (int i=0;i<m_nClient;i++)
	{
		::closesocket(m_arClient[i]);
	}
	m_nClient = 0;
}

void CMainDialog::OnStart()
{
	if (m_socket == INVALID_SOCKET) //开启服务
	{
		CString strPort;
		GetDlgItem(IDC_PORT)->GetWindowText(strPort);
		int nPort = atoi(strPort);
		if (nPort < 1 || nPort >65535)
		{
			MessageBox("port error");
			return;
		}
		//创建套接字
		if (!this->CreateAndListen(nPort))
		{
			MessageBox("create socket error");
			return;
		}
		//设置控件状态
		GetDlgItem(IDC_START)->SetWindowTextA("停止服务");
		m_bar.SetText("正在监听...", 0, 0);
		GetDlgItem(IDC_PORT)->EnableWindow(FALSE);
	}
	else //关闭服务
	{
		CloseAllSocket();
		GetDlgItem(IDC_START)->SetWindowTextA("开启服务");
		m_bar.SetText("空闲", 0, 0);
		GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
	}
	return ;
}
void CMainDialog::OnClear()
{
	m_listInfo.ResetContent();
	return ;
}

long CMainDialog::OnSocket(WPARAM wParam, LPARAM lParam)
{
	//得到句柄 
	SOCKET s = wParam;
	//查看是否出错
	if (WSAGETSELECTERROR(lParam))
	{
		RemoveClient(s);
		::closesocket(s);
		return 0;
	}
	//处理发生的事件
	switch (WSAGETSELECTEVENT(lParam))
	{
	case FD_ACCEPT: //监听到有套接字中有连接进入
		{
			MessageBox("server:accept");
			if (m_nClient < MAX_SOCKET)
			{
				SOCKET client = ::accept(s, NULL, NULL);
				this->AddClient(client);
			}
			else
			{
				MessageBox("too many connection");
			}
		}
		break;
	case FD_CLOSE:
		{
			MessageBox("server:close");
			RemoveClient(s);
			closesocket(s);
		}
		break;
	case FD_READ: //接收到对方发来的数据包
		{
			MessageBox("server:read");
			//得到对方的地址
			sockaddr_in sockAddr;
			memset(&sockAddr, 0, sizeof(sockAddr));
			int nSockAddrLength = sizeof(sockAddr);
			::getpeername(s, (sockaddr*)&sockAddr, &nSockAddrLength);

			int nPeerPort = ntohs(sockAddr.sin_port);
			CString strIP = inet_ntoa(sockAddr.sin_addr);  // strIP

			//获得主机名称
			DWORD dwIP = ::inet_addr(strIP);
			hostent* pHost = ::gethostbyaddr((LPSTR)&dwIP, 4, AF_INET);
			char szHostName[256]={0};
			strncpy(szHostName, pHost->h_name, 256);

			//得到网络数据
			char szContent[1024]={0};
			::recv(s, szContent, 1024, 0);

			//显示
			CString strItem = CString(szHostName) + "[" + strIP + "]:" + CString(szContent);
			m_listInfo.InsertString(0, strItem);
		}
		break;
	}
	return 0;
}

TCPServer.h

#include <afxwin.h>
#include <afxext.h>  //CStatusBar
#include <WinSock2.h>
#include <afxcmn.h>

#pragma comment(lib, "WS2_32.lib")
#define  MAX_SOCKET 56 //最大客户量

class CMyApp:public	 CWinApp
{
public:
	BOOL InitInstance();
};


//CMainDialog
class CMainDialog:public CDialog
{
public:
	CMainDialog(CWnd* pParentWnd=NULL);

protected:
	virtual BOOL OnInitDialog();
	virtual void OnCancel();
	//开启或停止服务
	afx_msg void OnStart();
	afx_msg void OnClear();
	afx_msg long OnSocket(WPARAM wParam, LPARAM lParam);

	BOOL CreateAndListen(int nPort);

	//向客户连接列表中加一个客户
	BOOL AddClient(SOCKET s);
	//从客户连接列表中移除一个客户
	void RemoveClient(SOCKET s);
	//关闭所有连接
	void CloseAllSocket();


protected:
	SOCKET m_socket;
	//两个子窗口控件
	CListBox m_listInfo;
	CStatusBarCtrl m_bar;

	//客户连接列表
	SOCKET m_arClient[MAX_SOCKET]; //套接字列表
	int m_nClient; //上述数组的大小

	DECLARE_MESSAGE_MAP()
};


TCPClient.cpp

#include "TCPClient.h"
#include "resource.h"

#define WM_SOCKET WM_USER+1

CMyApp theApp;

BOOL CMyApp::InitInstance()
{
	//初始化套接字
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD(2,0);
	::WSAStartup(wVersionRequested, &wsaData);
	//显示对话框
	CMainDialog dlg;
	m_pMainWnd = &dlg;
	dlg.DoModal();
	//释放套接字
	::WSACleanup();
	return FALSE;
}

//CMainDialog
CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_MAINDIALOG,pParentWnd)
{

}
BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
	ON_BN_CLICKED(IDC_CONNECT, OnConnect)
	ON_BN_CLICKED(IDC_SEND, OnSend)
	ON_MESSAGE(WM_SOCKET, OnSocket)
END_MESSAGE_MAP()

void CMainDialog::OnCancel()
{
	
	CDialog::OnCancel();
}

BOOL CMainDialog::OnInitDialog()
{
	CDialog::OnInitDialog();

	//设置图标
	SetIcon(theApp.LoadIconA(IDI_MAIN), FALSE);

	//关联控件
	m_edit_text.SubclassDlgItem(IDC_EDIT_CONTENT, this);
	//状态栏
	m_bar.Create(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP, CRect(0, 0, 0,0), this, NULL);
	int nWidth[]={100,-1};
	m_bar.SetParts(2, nWidth);
	m_bar.SetText("windows程序设计", 1, 0);
	m_bar.SetText("空闲", 0, 0);

	GetDlgItem(IDC_ADDR)->SetWindowTextA("192.168.19.143");
	GetDlgItem(IDC_PORT)->SetWindowTextA("9999");

	//
	m_socket = INVALID_SOCKET;
	
	return TRUE;
}
void CMainDialog::AddStringToList(CString strText)
{
	CString strContent;
	GetDlgItem(IDC_EDIT_CONTENT)->GetWindowText(strContent);
	GetDlgItem(IDC_EDIT_CONTENT)->SetWindowText(strContent+strText);

}
long CMainDialog::OnSocket(WPARAM wParam, LPARAM lParam)
{
	SOCKET	s = wParam;
	if (WSAGETSELECTERROR(lParam))
	{
		::closesocket(m_socket);
		m_socket = INVALID_SOCKET;
		return 0;
	}
	switch (WSAGETSELECTEVENT(lParam))
	{
	case FD_READ:
		{
			MessageBox("client:read");
			char szText[1024]={0};
			::recv(s, szText, 1024, 0);
			AddStringToList(CString(szText)+"\r\n");
		}
		break;
	case FD_CONNECT:
		{
			MessageBox("client:connect");
			GetDlgItem(IDC_CONNECT)->SetWindowTextA("断开连接");
			GetDlgItem(IDC_ADDR)->EnableWindow(FALSE);
			GetDlgItem(IDC_PORT)->EnableWindow(FALSE);
			GetDlgItem(IDC_TEXT)->EnableWindow(TRUE);
			GetDlgItem(IDC_SEND)->EnableWindow(TRUE);
			m_bar.SetText("已经连接到服务器", 0, 0);
		}
		break;
	case FD_CLOSE:
		{
			MessageBox("client:close");
			OnConnect();
		}
		break;
	}
	return 0;
}

BOOL CMainDialog::Connect(LPCTSTR pszRemoteAddr, u_short nPort)
{
	//创建套接字
	m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == m_socket)
	{
		return FALSE;
	}
	::WSAAsyncSelect(m_socket, m_hWnd, WM_SOCKET, FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE);

	ULONG uAddr = ::inet_addr(pszRemoteAddr);
	if (uAddr == INADDR_NONE)
	{
		//不是IP地址,就认为是主机名称
		//从主机名得到IP
		hostent* pHost = ::gethostbyname(pszRemoteAddr);
		if (pHost == NULL)
		{
			::closesocket(m_socket);
			m_socket = INVALID_SOCKET;
			return FALSE;
		}
		uAddr = ((struct in_addr*)*(pHost->h_addr_list))->s_addr;
	}

	//填写服务器信息
	sockaddr_in remote;
	remote.sin_family = AF_INET;
	remote.sin_addr.S_un.S_addr = uAddr;
	remote.sin_port = ::htons(nPort);
	//连接
	::connect(m_socket, (sockaddr*)&remote, sizeof(sockaddr));
	return TRUE;
}

void CMainDialog::OnConnect()
{
	if (INVALID_SOCKET == m_socket) //连接服务器
	{
		CString strAddr;
		GetDlgItem(IDC_ADDR)->GetWindowText(strAddr);
		if (strAddr.IsEmpty())
		{
			MessageBox("the servers IP is empty");
			return;
		}
		CString strPort;
		GetDlgItem(IDC_PORT)->GetWindowTextA(strPort);
		int nPort = atoi(strPort);
		if (nPort < 1 || nPort > 65535)
		{
			MessageBox("port error");
			return;
		}
		if (Connect(strAddr, nPort) == FALSE)
		{
			MessageBox("connect to servers error...");
			return;
		}
		//设置用户界面
		GetDlgItem(IDC_CONNECT)->SetWindowText("取消");
		m_bar.SetText("正在连接..", 0, 0);
		
	}
	else //断开服务器
	{
		::closesocket(m_socket);
		m_socket = INVALID_SOCKET;
		//设置用户界面
		GetDlgItem(IDC_CONNECT)->SetWindowTextA("连接服务器");
		m_bar.SetText("空闲", 0, 0);
		GetDlgItem(IDC_ADDR)->EnableWindow(TRUE);
		GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
		GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
		GetDlgItem(IDC_TEXT)->EnableWindow(FALSE);
	}
	
	//this->Connect(szAddr, )
}
void CMainDialog::OnSend()
{
	CString strSendContent;
	GetDlgItem(IDC_TEXT)->GetWindowTextA(strSendContent);
	::send(m_socket, strSendContent, strSendContent.GetLength(), 0);
	GetDlgItem(IDC_TEXT)->SetWindowTextA("");
}

TCPClient.h

#include <afxwin.h>
#include <afxext.h>  //CStatusBar
#include <WinSock2.h>
#include <afxcmn.h>

#pragma comment(lib, "WS2_32.lib")
#define  MAX_SOCKET 56 //最大客户量

class CMyApp:public	 CWinApp
{
public:
	BOOL InitInstance();
};


//CMainDialog
class CMainDialog:public CDialog
{
public:
	CMainDialog(CWnd* pParentWnd=NULL);

protected:
	virtual BOOL OnInitDialog();
	virtual void OnCancel();
	////开启或停止服务
	//afx_msg void OnStart();
	afx_msg void OnSend();
	afx_msg long OnSocket(WPARAM wParam, LPARAM lParam);
	void OnConnect();

	BOOL Connect(LPCTSTR pszRemoteAddr, u_short nPort);
	SOCKET m_socket;

	// 控件
	CStatusBarCtrl m_bar;
	CEdit m_edit_text;

	void AddStringToList(CString strText);
	//BOOL CreateAndListen(int nPort);

	////向客户连接列表中加一个客户
	//BOOL AddClient(SOCKET s);
	////从客户连接列表中移除一个客户
	//void RemoveClient(SOCKET s);
	////关闭所有连接
	//void CloseAllSocket();


	DECLARE_MESSAGE_MAP()
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值