//----学孙鑫VC第16课 异步套接字-聊天室示例--------------------//
(1)建立一个对话框工程,工程名为Chat,添加接收编辑框控件IDC_EDIT_RECV,发送信息编辑框控件IDC_EDIT_SEND,IP地址控件IDC_IPADDRESS1,发送按钮IDC_BTN_SEND,主机名IDC_EDIT_HOSTNAME
(2) 在Stdfx.h文件中加入"#include <winsock2.h>"语句
(3) 在使用之前须链接库函数:工程->设置->Link->输入ws2_32.lib,OK!
(4) 编辑BOOL CChatApp::InitInstance()函数,加入加载2.2版本套接字库代码
BOOL CChatApp::InitInstance()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return FALSE;
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
WSACleanup( );
return FALSE;
}
AfxEnableControlContainer();
}
(5)在BOOL CChatApp类头文件中增加一个析构函数,用于调用WSACleanup()终止对套接字库的使用
class CChatApp : public CWinApp
{
public:
CChatApp();
~CChatApp();//(5)增加一个析构函数
}
(6)编辑析构函数代码
CChatApp::~CChatApp()
{
WSACleanup();//调用此函数终止对套接字库的使用
}
(7)在CChatDlg类中增加SOCKET类型私有变量m_socket,在构造函数中对这个变量进行初始化,m_socket=0;//进行初始化
class CChatDlg : public CDialog
{
SOCKET m_socket;
}
(8)在class CChatDlg类头文件中增加一个析构函数,
class CChatDlg : public CDialog
{
// Construction
public:
BOOL InitSocket();//(11)调用创建套接字函数
CChatDlg(CWnd* pParent = NULL); // standard constructor
~CChatDlg();//(8)类头文件中增加一个析构函数,用于判断并关闭套接字
}
(9)编辑析构函数代码,判断并关闭套接字
CChatDlg::~CChatDlg()
{
if(m_socket)
closesocket(m_socket);//(8)判断并关闭套接字
}
(10)在class CChatDlg类上点右键,增加一个BOOL类的初始化套接字的函数InitSocket()
BOOL CChatDlg::InitSocket()
{
m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);//WSASocket()----扩展创建套接字函数
if(INVALID_SOCKET==m_socket)
{
MessageBox("创建套接字失败!");//如果返回出错标记,提示出错信息
return FALSE;
}
SOCKADDR_IN addrSock;//定义地址结构体变量
addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//设置模式
addrSock.sin_family=AF_INET;//设置地址族
addrSock.sin_port=htons(6000);//设置端口
if(SOCKET_ERROR==bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR)))//绑定并得到返回值
{
MessageBox("绑定失败!");
return FALSE;
}
if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))
{
MessageBox("注册网络读取事件失败!");//WSAAsyncSelect()异步选择函数,注册网络读取事件,请求一个基于消息的网络事件通知
return FALSE;
}
return TRUE;
}
(10)在BOOL CChatDlg::OnInitDialog()函数中,调用
{
// TODO: Add extra initialization here
InitSocket();
}
(11)在CChatDlg类头文件中,调用创建套接字函数,并在函数上面增加自定义消息
#define UM_SOCK WM_USER+1 //(11)定义自定义消息
class CChatDlg : public CDialog
{
// Construction
public:
BOOL InitSocket();//(11)调用创建套接字函数
CChatDlg(CWnd* pParent = NULL); // standard constructor
~CChatDlg();//(8)类头文件中增加一个析构函数,用于判断并关闭套接字
}
(12)在头文件消息列表中增加消息响应函数原型声明
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CChatDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBtnSend();
//}}AFX_MSG
afx_msg void OnSock(WPARAM,LPARAM);//(12)消息响应函数原型声明
DECLARE_MESSAGE_MAP()
(13)在void CChatDlg::DoDataExchange(CDataExchange* pDX)函数下面的消息映射列表中,增加消息映射
void CChatDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CChatDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CChatDlg, CDialog)
//{{AFX_MSG_MAP(CChatDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_SEND, OnBtnSend)
//}}AFX_MSG_MAP
ON_MESSAGE(UM_SOCK,OnSock)//(13)增加消息映射
(14)接收网络消息响应函数实现
void CChatDlg::OnSock(WPARAM wParam,LPARAM lParam)
{
//---接收网络消息响应函数实现
switch(LOWORD(lParam))//取出低字,判断什么网络消息类型
{
case FD_READ:
WSABUF wsabuf;
wsabuf.buf=new char[200];
wsabuf.len=200;
DWORD dwRead;
DWORD dwFlag=0;
SOCKADDR_IN addrFrom;
int len=sizeof(SOCKADDR);
CString str;
CString strTemp;
HOSTENT *pHost;
if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,
(SOCKADDR*)&addrFrom,&len,NULL,NULL))
{
MessageBox("接收数据失败!");//WSARecvFrom()----扩展接收数据函数
return;
}
pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);//将IP地址转换成主机名
//str.Format("%s说 :%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);
str.Format("%s说 :%s",pHost->h_name,wsabuf.buf);
str+="/r/n";
GetDlgItemText(IDC_EDIT_RECV,strTemp);//得到接收控件内容
str+=strTemp;
SetDlgItemText(IDC_EDIT_RECV,str);//设置接收接收控件内容
break;
}
}
(15)编辑[发送]按钮的代码
void CChatDlg::OnBtnSend()
{
// 编辑[发送]按钮的代码TODO: Add your control notification handler code here
DWORD dwIP;
CString strSend;
WSABUF wsabuf;
DWORD dwSend;
int len;
CString strHostName;//定义一个字符串变量用于存放主机名
SOCKADDR_IN addrTo;
HOSTENT* pHost;
if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="")
{
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);//得到IP地址
addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
}
else
{
pHost=gethostbyname(strHostName);//gethostbyname(strHostName);将一个主机名转换成IP地址
addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]);
}
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(6000);
GetDlgItemText(IDC_EDIT_SEND,strSend);
len=strSend.GetLength();
wsabuf.buf=strSend.GetBuffer(len);
wsabuf.len=len+1;
SetDlgItemText(IDC_EDIT_SEND,"");
if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0,
(SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))
{
MessageBox("发送数据失败!");//WSASendTo()----发送数据
return;
}
}
//----异步选择函数-套接字-聊天室示例结束--------------------//