服务端
消息通知和WSAAsyncSeect
WSAAsyncSelect函数自动把套接字设为非阻塞模式,并且为套接字绑定一个窗口句柄,当有网络事件发送时,便向这个窗口发送消息。
FD_READ 套接字接收到对方发送过来的数据包,表明这时可以去读套接字了(recv用)
FD_WRITE 数据缓冲区满后再次变空时,WinSock接口通关该通知码通知应用程序。表示可以继续发送数据了(短时间内发送数据过多,便会造成数据缓冲区变满)
FD_ACCEPT 监听中的套接字检测到有连接进入
FD_CONNECT如果用套接字去连接对方的主机,当连接动作完成以后会接收到这个通知码
FD_CLOSE 检测到套接字对应的链接被关闭
#define WSAGETSELECTERROR(lParam) HIWORD(lParam) //高字为出错代码
#define WSAGETSELCTEVENT(lParam) LOWORD(lParam) //低字为通知码
stdafx.h
#ifndef MFCTEST_HEAD
#define MFCTEST_HEAD
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <afxwin.h>
#include <afxframewndex.h>
#include <Winsock2.h>
#include <assert.h>
#pragma comment (lib,"ws2_32.lib")
class CMyApp:public CWinApp
{
public:
//构造函数
CMyApp();
//析构函数
virtual ~CMyApp();
//重载函数
public:
//初始函数
virtual BOOL InitInstance();
};
class CMyWindow : public CFrameWndEx
{
public:
CButton m_button; //按钮
SOCKET m_socket; //连接句柄
SOCKET m_ClientSocket; //客户端句柄
public:
//构造函数
CMyWindow();
//析构函数
virtual ~CMyWindow();
//虚函数
public:
//命令响应消息
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
// 消息函数
protected:
// 网络读取
LRESULT OnSocketNotifyRead(WPARAM wParam, LPARAM lParam);
// 网络关闭
LRESULT OnSocketNotifyClose(WPARAM wParam, LPARAM lParam);
// 网络连接
LRESULT OnSocketNotifyAccept(WPARAM wParam, LPARAM lParam);
//消息映射
public:
//创建消息
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//网络消息
afx_msg LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
#endif
afxmain.cpp
#include "stdafx.h"
CMyApp myapp;
#define WM_SOCKET WM_USER+1 //网络消息
#define IDC_START 1012 //按钮定义
//构造函数
CMyApp::CMyApp()
{
}
//析构函数
CMyApp::~CMyApp()
{
}
//初始函数
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMyWindow();
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMyWindow, CFrameWndEx)
ON_WM_CREATE()
ON_MESSAGE(WM_SOCKET, OnSocket)
END_MESSAGE_MAP()
//构造函数
CMyWindow::CMyWindow()
{
//设置变量
m_ClientSocket = INVALID_SOCKET;
m_socket= INVALID_SOCKET;
//创建窗口
Create(NULL, TEXT("My Window"));
}
//析构函数
CMyWindow::~CMyWindow()
{
WSACleanup();
}
//命令响应消息
BOOL CMyWindow::OnCommand( WPARAM wParam, LPARAM lParam )
{
int wmId=LOWORD(wParam);
switch (wmId)
{
case IDC_START:
{
MessageBox(TEXT("测试一下哈"),TEXT("提示"),MB_OK);
return true;
}
break;
default:
return true;
}
return __super::OnCommand(wParam,lParam);
}
//创建事件
int CMyWindow::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
__super::OnCreate(lpCreateStruct);
//居中窗口
SetActiveWindow();
SetForegroundWindow();
CenterWindow(GetParent());
//创建按钮
RECT rect={0,0,50,30};
m_button.Create(TEXT("按钮"),WS_CHILD|WS_VISIBLE,rect,this,IDC_START);
//启动使用ws2.dll的
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA lpWSAData = { 0 };
WSAStartup(wVersionRequested, &lpWSAData);
//创建socket
m_socket = socket(AF_INET, SOCK_STREAM, 0);
//绑定端口
SOCKADDR_IN addr = { 0 };
addr.sin_addr.S_un.S_addr =INADDR_ANY;
addr.sin_family = AF_INET;
addr.sin_port = 8012;
if(bind(m_socket, (SOCKADDR*)&addr, sizeof(addr))== SOCKET_ERROR)
throw TEXT("绑定端口失败");
//绑定窗口
int iErrorCode = WSAAsyncSelect(m_socket, this->m_hWnd, WM_SOCKET, FD_READ | FD_ACCEPT |FD_CLOSE);
if (iErrorCode == SOCKET_ERROR) throw TEXT("绑定内部窗口错误");
//监听
listen(m_socket, 5);
return true;
}
//网络消息
LRESULT CMyWindow::OnSocket(WPARAM wParam, LPARAM lParam)
{
switch (WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT: //网络连接
{
return OnSocketNotifyAccept(wParam, lParam);
}
case FD_READ: // 数据读取
{
return OnSocketNotifyRead(wParam, lParam);
}
case FD_CLOSE: // 网络关闭
{
return OnSocketNotifyClose(wParam, lParam);
}
}
return 0;
}
// 网络读取
LRESULT CMyWindow::OnSocketNotifyRead(WPARAM wParam, LPARAM lParam)
{
try
{
ASSERT(m_ClientSocket != INVALID_SOCKET);
if (m_ClientSocket == INVALID_SOCKET)return false;
// 读取数据
char szText[1024] = { 0 };
int iRetCode = recv(m_ClientSocket, szText, 1024, 0);
if (iRetCode == SOCKET_ERROR) throw TEXT("网络连接关闭,读取数据失败");
char szSendText[64] = { "这是服务端发送的消息" };
send(m_ClientSocket, szSendText, 64, 0);
}
catch (...)
{
closesocket(m_ClientSocket);
}
return true;
}
// 网络关闭
LRESULT CMyWindow::OnSocketNotifyClose(WPARAM wParam, LPARAM lParam)
{
closesocket(m_ClientSocket);
return true;
}
// 网络连接
LRESULT CMyWindow::OnSocketNotifyAccept(WPARAM wParam, LPARAM lParam)
{
SOCKADDR_IN clientaddr = { 0 };
INT nBufferSize = sizeof(clientaddr);
m_ClientSocket = accept(m_socket, (SOCKADDR*)&clientaddr, &nBufferSize);
//退出判断
if (m_ClientSocket == INVALID_SOCKET) return false;
return true;
}
客户端
WHSocketHead.h
#pragma once
#pragma pack(1)
#include <Winsock2.h>
#include <assert.h>
#pragma comment (lib,"ws2_32.lib")
#pragma pack(1)
//网络命令
struct TCP_Command
{
WORD wMainCmdID; //主命令码
WORD wSubCmdID; //子命令码
};
#pragma pack()
//网络状态
#define SOCKET_STATUS_IDLE 0 //空闲状态
#define SOCKET_STATUS_WAIT 1 //等待状态
#define SOCKET_STATUS_CONNECT 2 //连接状态
//关闭原因
#define SHUT_REASON_INSIDE 0 //内部原因
#define SHUT_REASON_NORMAL 1 //正常关闭
#define SHUT_REASON_REMOTE 2 //远程关闭
#define SHUT_REASON_TIME_OUT 3 //网络超时
#define SHUT_REASON_EXCEPTION 4 //异常关闭
//网络接口
__interface ITCPSocketSink
{
//网络事件
public:
//连接事件
virtual bool OnEventTCPSocketLink(WORD wSocketID, INT nErrorCode) = NULL;
//关闭事件
virtual bool OnEventTCPSocketShut(WORD wSocketID, BYTE cbShutReason) = NULL;
//读取事件
virtual bool OnEventTCPSocketRead(WORD wSocketID, TCP_Command Command, VOID * pData, WORD wDataSize) = NULL;
};
stdafx.h
#ifndef MFCTEST_HEAD
#define MFCTEST_HEAD
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <afxwin.h>
#include <afxframewndex.h>
#include "WHSocketHead.h"
//
class CMyApp:public CWinApp
{
public:
//构造函数
CMyApp();
//析构函数
virtual ~CMyApp();
//重载函数
public:
//初始函数
virtual BOOL InitInstance();
};
class CMyWindow : public CFrameWndEx
{
public:
CButton m_button; //按钮
SOCKET m_socket; //连接句柄
BYTE m_cbSocketStatus; //网络状态
WORD m_wSocketID; //网络标识
public:
//构造函数
CMyWindow();
//析构函数
virtual ~CMyWindow();
//虚函数
public:
//命令响应消息
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
//辅助函数
protected:
// 关闭连接
void CloseSocket(BYTE cbShutReason);
// 消息函数
protected:
// 网络读取
LRESULT OnSocketNotifyRead(WPARAM wParam, LPARAM lParam);
// 网络发送
LRESULT OnSocketNotifyWrite(WPARAM wParam, LPARAM lParam);
// 网络关闭
LRESULT OnSocketNotifyClose(WPARAM wParam, LPARAM lParam);
// 网络连接
LRESULT OnSocketNotifyConnect(WPARAM wParam, LPARAM lParam);
//消息映射
public:
//创建消息
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//网络消息
afx_msg LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
<pre name="code" class="cpp">#include "stdafx.h"
CMyApp myapp;
#define WM_SOCKET WM_USER+1 //网络消息
#define IDC_START 1012 //按钮定义
//构造函数
CMyApp::CMyApp()
{
}
//析构函数
CMyApp::~CMyApp()
{
}
//初始函数
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMyWindow();
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMyWindow, CFrameWndEx)
ON_WM_CREATE()
ON_MESSAGE(WM_SOCKET, OnSocket)
END_MESSAGE_MAP()
//构造函数
CMyWindow::CMyWindow()
{
//设置变量
m_socket= INVALID_SOCKET;
m_wSocketID = NULL;
m_cbSocketStatus = SOCKET_STATUS_IDLE;
//创建窗口
Create(NULL, TEXT("My Window"));
}
//析构函数
CMyWindow::~CMyWindow()
{
WSACleanup();
}
//命令响应消息
BOOL CMyWindow::OnCommand( WPARAM wParam, LPARAM lParam )
{
int wmId=LOWORD(wParam);
switch (wmId)
{
case IDC_START:
{
MessageBox(TEXT("测试一下哈"),TEXT("提示"),MB_OK);
return true;
}
break;
default:
return true;
}
return __super::OnCommand(wParam,lParam);
}
//创建事件
int CMyWindow::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
__super::OnCreate(lpCreateStruct);
//居中窗口
SetActiveWindow();
SetForegroundWindow();
CenterWindow(GetParent());
//创建按钮
RECT rect={0,0,50,30};
m_button.Create(TEXT("按钮"),WS_CHILD|WS_VISIBLE,rect,this,IDC_START);
//启动使用ws2.dll的
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA lpWSAData = { 0 };
WSAStartup(wVersionRequested, &lpWSAData);
//创建socket
m_socket = socket(AF_INET, SOCK_STREAM, 0);
m_wSocketID = (WORD)m_socket;
int iErrorCode = WSAAsyncSelect(m_socket, this->m_hWnd, WM_SOCKET, FD_READ | FD_CONNECT | FD_CLOSE);
if (iErrorCode == SOCKET_ERROR) throw TEXT("绑定内部窗口错误");
//连接服务器
SOCKADDR_IN addr = { 0 };
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = 8012;
iErrorCode = connect(m_socket, (SOCKADDR*)&addr, sizeof(addr));
if (iErrorCode == SOCKET_ERROR)
{
iErrorCode = WSAGetLastError();
TCHAR szBuffer[64] = { 0 };
if (iErrorCode != WSAEWOULDBLOCK)
{
_sntprintf_s(szBuffer, sizeof(szBuffer), TEXT("连接发生错误,错误代码 %d"), iErrorCode);
throw szBuffer;
}
else if (iErrorCode == WSAEWOULDBLOCK)
{
_sntprintf_s(szBuffer, sizeof(szBuffer), TEXT("从操作返回的时候没有数据正在排队等待从套接字读取非阻塞无法立即完成的插座 %d"), iErrorCode);
OutputDebugString(szBuffer);
}
}
// 设置变量
m_cbSocketStatus = SOCKET_STATUS_WAIT;
char szText[64] = { "这是客户端发送的消息" };
send(m_socket, szText, 64, 0);
return true;
}
//网络消息
LRESULT CMyWindow::OnSocket(WPARAM wParam, LPARAM lParam)
{
switch (WSAGETSELECTEVENT(lParam))
{
case FD_CONNECT: // 网络连接
{
return OnSocketNotifyConnect(wParam, lParam);
}
case FD_READ: // 数据读取
{
return OnSocketNotifyRead(wParam, lParam);
}
case FD_CLOSE: // 网络关闭
{
return OnSocketNotifyClose(wParam, lParam);
}
}
return 0;
}
// 网络读取
LRESULT CMyWindow::OnSocketNotifyRead(WPARAM wParam, LPARAM lParam)
{
try
{
// 读取数据
char szRecvText[1024] = { 0 };
int iRetCode = recv(m_socket, szRecvText, 1024, 0);
if (iRetCode == SOCKET_ERROR) throw TEXT("网络连接关闭,读取数据失败");
// 处理数据
//bool bSuccess = m_pITCPSocketSink->OnEventTCPSocketRead(m_wSocketID, Command, pDataBuffer, wDataSize);
//if (bSuccess == false) throw TEXT("网络数据包处理失败");
}
catch (...)
{
closesocket(m_socket);
}
return true;
}
// 网络关闭
LRESULT CMyWindow::OnSocketNotifyClose(WPARAM wParam, LPARAM lParam)
{
// 关闭连接
ASSERT(m_socket != INVALID_SOCKET);
if (m_socket == INVALID_SOCKET)return true;
m_cbSocketStatus = SOCKET_STATUS_IDLE;
WSAAsyncSelect(m_socket, GetSafeHwnd(), WM_SOCKET, 0);
closesocket(m_socket);
m_socket = INVALID_SOCKET;
m_cbSocketStatus = SOCKET_STATUS_IDLE;
//通知事件
//try { m_pITCPSocketSink->OnEventTCPSocketShut(m_wSocketID, SHUT_REASON_NORMAL); }
//catch (...) {}
return true;
}
// 网络连接
LRESULT CMyWindow::OnSocketNotifyConnect(WPARAM wParam, LPARAM lParam)
{
// 判断状态
int iErrorCode = WSAGETSELECTERROR(lParam);
if (iErrorCode == 0) m_cbSocketStatus = SOCKET_STATUS_CONNECT;
else CloseSocket(SHUT_REASON_TIME_OUT);
// 发送通知
//m_pITCPSocketSink->OnEventTCPSocketLink(m_wSocketID, iErrorCode);
return true;
}
// 关闭连接
void CMyWindow::CloseSocket(BYTE cbShutReason)
{
// 关闭连接
ASSERT(m_socket != INVALID_SOCKET);
if (m_socket == INVALID_SOCKET)return;
m_cbSocketStatus = SOCKET_STATUS_IDLE;
WSAAsyncSelect(m_socket, GetSafeHwnd(), WM_SOCKET, 0);
closesocket(m_socket);
m_socket = INVALID_SOCKET;
m_cbSocketStatus = SOCKET_STATUS_IDLE;
return;
}
#endif