您的位置:首页 > 其它

MFC 基于消息机机制的WSAAsyncSelect demo

2011-09-13 17:36 197 查看
1. 添加头文件

#include <winsock.h>

#pragma comment(lib, "ws2_32.lib")

服务端:

1. 定义变量

SOCKET sSocket;

2. 定义消息常量

#define MSG_SOCKET (WM_USER + 1000)

添加消息映射

BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)

//{{AFX_MSG_MAP(CXXXDlg)

//}}AFX_MSG_MAP

ON_MESSAGE(MSG_SOCKET, OnSockEvent)

END_MESSAGE_MAP()

3. 初始化SOCKET

bool CXXXDlg::InitWSASocket()

{

char szError[256] = {0};

WORD wVersionRequested;

WSADATA wsaData;

wVersionRequested = MAKEWORD(2, 2);

int ReturnInfo = WSAStartup(wVersionRequested, &wsaData);

if (ReturnInfo != 0)

{

return false;

}

if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)

{

WSACleanup();

sprintf(szError, "初始化SOCKET错误,%d", WSAGetLastError());

AfxMessageBox(szError);

return false;

}

return true;

}

3. 初始化的时候调用InitWSASocket,如果初始化成功则开始监听。

BOOL CXXXDlg::OnInitDialog()

{

CDialog::OnInitDialog();

// Set the icon for this dialog. The framework does this automatically

// when the application's main window is not a dialog

SetIcon(m_hIcon, TRUE); // Set big icon

SetIcon(m_hIcon, FALSE); // Set small icon

// TODO: Add extra initialization here

GetDlgItem(IDC_EDIT_PORT)->SetWindowText("8977");

if (!InitWSASocket())

{

return FALSE;

}

if (!StartSocketListen())

{

OnCancel();

}

return TRUE; // return TRUE unless you set the focus to a control

}

4. 监听模块,注册事件

bool CXXXDlg::StartSocketListen()

{

GetDlgItemText(IDC_EDIT_PORT, m_StrPort);

if (m_StrPort.IsEmpty())

{

return false;

}

char szError[256] = {0};

//创建套接字

m_sSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if(INVALID_SOCKET == m_sSocket)

{

sprintf(szError, "创建SOCKET错误,%d", WSAGetLastError());

AfxMessageBox(szError);

return false;

}

//服务器地址信息

SOCKADDR_IN addrSock;

memset(&addrSock, 0x0, sizeof addrSock);

addrSock.sin_addr.S_un.S_addr = inet_addr("127.0.0.1")/*htonl(INADDR_ANY)*/;

addrSock.sin_family = AF_INET;

addrSock.sin_port = htons(atoi(m_StrPort));

//成功返回0 ,否则返回SOCKET_ERROR

if (SOCKET_ERROR == ::bind(m_sSocket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR)))

{

sprintf(szError, "绑定SOCKET错误,%d", WSAGetLastError());

AfxMessageBox(szError);

return false;

}

//注册网络事件

if (SOCKET_ERROR == WSAAsyncSelect(m_sSocket, m_hWnd,
MSG_SOCKET, FD_ACCEPT | FD_READ | FD_CLOSE))

{

::closesocket(m_sSocket);

sprintf(szError, "注册网络读取事件失败,%d", WSAGetLastError());

AfxMessageBox(szError);

return false;

}

//成功返回0 ,否则返回SOCKET_ERROR

if (SOCKET_ERROR == ::listen(m_sSocket, 100))

{

sprintf(szError, "监听SOCKET错误,%d", WSAGetLastError());

AfxMessageBox(szError);

return false;

}

return true;

}

5. 当有客户端来连接的时候,系统发送消息触发以下事件。

说明:当客户端connect的时候触发服务端的FD_ACCEPT。客户端send数据的时候触发服务端的FD_READ

,客户端关闭的时候触发服务端的FD_CLOSE。

void CXXXDlg::OnSockEvent(WPARAM wParam,LPARAM lParam)

{

SOCKET sSock= static_cast<SOCKET>(wParam);

if(WSAGETSELECTERROR(lParam))

{

AfxMessageBox("OnSockEvent()接收到错误消息");

::closesocket(wParam);

return;

}

switch(WSAGETSELECTEVENT(lParam))

{

case FD_ACCEPT:

{

SOCKADDR_IN addrSock;

memset(&addrSock, 0x0, sizeof addrSock);

int Len = sizeof(addrSock);

//当客户端connect的时候全局的m_sSocket和wParam传进来的SOCKET是相同的,

//当服务端调用accept去获取客户端的SOCKET传入下一次的OnSockEvent中wParam参数中

m_cSocket = ::accept(m_sSocket, (SOCKADDR*)&addrSock, &Len);

if (INVALID_SOCKET == m_cSocket)

{

char szError[256] = {0};

sprintf(szError, "接收SOCKET错误,%d", WSAGetLastError());

AfxMessageBox(szError);

}

}

break;

case FD_READ:

{

char buff[1024] = {0};

SOCKADDR_IN addrSock;

memset(&addrSock, 0x0, sizeof addrSock);

int Len = sizeof(addrSock);

//recv不等待是因为你使用的是非阻塞socket,换而你使用阻塞socket一样需要等待。

//recv的recvfrom是可以替换使用的,只是recvfrom多了两个参数,可以用来接收对端的地址信息,

//这个对于udp这种无连接的,可以很方便地进行回复。

//而换过来如果你在udp当中也使用recv,那么就不知道该回复给谁了,如果你不需要回复的话,也是可以使用的。

//另外就是对于tcp是已经知道对端的,就没必要每次接收还多收一个地址,

//没有意义,要取地址信息,在accept当中取得就可以加以记录了。

::recv(sSock, buff, 1024, 0);

// ::recvfrom(m_cSocket, buff, 1024, 0, (SOCKADDR*)&addrSock, &Len);

static_cast<CListBox *>(GetDlgItem(IDC_LIST_LOG))->AddString(buff);

::send(sSock/*m_cSocket*/, buff, 1024, 0);

}

break;

case FD_CLOSE:

{

::shutdown(sSock, 2);

::closesocket(sSock);

}

break;

}

}

5. 关闭程序的时候

CXXXDlg::~CXXXDlg()

{

WSACleanup();

::shutdown(m_sSocket, 2);

::closesocket(m_sSocket);

}

客户端:

与服务端一样。定义SOCKET变量,添加消息常量,添加消息映射。初始化SOCKET,关闭的时候清理SOCKET(WSACleanup)

void CXXXDlg::OnButtonSend()

{

char szError[256] = {0};

SOCKET m_Socket;

//创建套接字

if (m_IsTCP)

{

m_Socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

}

else

{

m_Socket = ::socket(AF_INET, SOCK_STREAM, 0);

}

if(INVALID_SOCKET == m_Socket)

{

sprintf(szError, "创建SOCKET错误,%d", WSAGetLastError());

AfxMessageBox(szError);

return;

}

//服务器地址信息

SOCKADDR_IN addrSock;

addrSock.sin_addr.S_un.S_addr = inet_addr(m_StrIP)/*htonl(INADDR_ANY)*/;

addrSock.sin_family = AF_INET;

addrSock.sin_port = htons(atoi(m_StrPort));

//连接服务器

//成功返回0 ,否则返回SOCKET_ERROR

if(SOCKET_ERROR == ::connect(m_Socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR)))

{

::closesocket(m_Socket);

sprintf(szError, "连接服务端错误,%d", WSAGetLastError());

AfxMessageBox(szError);

return;

}

else

{

CString SendBuff;

char RecvBuff[1024] = {0};

GetDlgItemText(IDC_EDIT_MSG, SendBuff);

::send(m_Socket, SendBuff, SendBuff.GetLength(), 0);

::recv(m_Socket, RecvBuff, 1024, 0);

static_cast<CListBox *>(GetDlgItem(IDC_LIST_TEXT))->AddString(RecvBuff);

}

closesocket(m_Socket);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: