异步与同步通信相比较,前者是非阻塞模式,后者是阻塞模式。有关两者差异在此博主中有详细讲解,推荐:https://www.cnblogs.com/wzsblogs/p/4671559.html。

        采用同步socket,同时可与CArchive、CSocketFile 配合使用(这两者能否与异步socket配合使用呢?还待验证)。两者的运行机制基本相同,但是在同步机制中OnConnect与OnSend永远不会被系统调用。(为啥?CSocket在Connect()返回WSAEWOULDBLOCK错误时,不是在OnConnect(),OnReceive()这些事件终端函数里去等待。它马上调用一个用于提取消息的函数PumpMessage(...),就是从当前线程的消息队列里取关心的消息。原文:https://www.cnblogs.com/yuanzfy/archive/2011/08/26/2155189.html)

        如果不使用CArchive/CSocketFile,则同步与异步最大的区别在于没有调用系统通知的OnConnect与OnSend。下例采用串行化进行说明。

 

1、创建基于对话框的MFC项目,包含Windows套接字。在工程中创建基于CSocket的类MySocket用于通信。

1)客户端:在MySocket类中新增函数pGetDlg用户快速获取主窗口指针,并声明一个Dlg类的指针用于绑定,CXXXDlg.h中声明指针对象m_ClientSocket;

2)服务端:在MySocket类中新增函数pGetDl用户快速获取主窗口指针,并声明一个Dlg类的指针用于实现函数快速获取指针。CXXXDlg.h中声明指针对象m_ListenSocket/m_ServerSocket。

 

Tips:相比异步通信,新增了三个指针对象,分别用于收发和缓冲。

 1 // XXXSocke.h中
 2 
 3 class CXXXDlg;     //类声明,创建指针对象
 4 class XXXSocket : public CSocket
 5 {
 6 public:
 7     CXXXXDlg *m_dlg;
 8     void pGetDlg(CXXXXDlg*dlg);
 9 
10     CArchive *m_archiveIn;
11     CArchive *m_archiveOut;
12     CSocketFile *m_socketFile;
13        ......
14 }
15 
16 void CxxxxSocket::pGetDlg(CxxxxDlg* dlg)
17 {
18     m_dlg=dlg;
19 }

 

2、在Dlg类中对指针对象初始化,并声明通信处理函数

        因指定为指针型,在Dlg.cpp的初始化InitInstance函数中中进行指针初始化(=NULL),并新增一个CString变量用于接收信息. 

 1 class CXXXXDlg : public CDialog
 2  {
 3  public:
 4      CXXXXSocket * m_xxxxsocket;  //客户端一个,服务器端两个,一个用于监听,一个用于服务
 5      CArchive *m_archiveIn;
 6      CArchive *m_archiveOut;
 7      CSocketFile *m_socketFile;
CString recvfile; //用于临时接收文件
8 void OnReceive(); 9 void OnClose(); 10 // void OnConnect();不需要 11 void Reset(); //用于释放套接字对象 12 ...... 13 } 14 //在Dlg.cpp中实现Reset函数,即删除套接字对象,并将指针赋空 15 void CXXXDlg::Reset() 16 {
  m_XXXXXsocket->Close(); //如果不关闭的话,直接点击中断会引发程序崩溃
 m_ArchiveIn->Close();
  m_ArchiveOut->Close();
 m_socketFile->Close();
17 if(m_xxxxSocket!=NULL) //注意用于监听的套接字不能释放,因为监听处于打开状态,与连接是并列关系 18 { 19 delete m_xxxxSocket; 20 m_xxxxSocket=NULL; 21 } 22 if(m_archiveIn!=NULL) 23 { 24 delete m_archiveIn; 25 m_archiveIn=NULL; 26 } 27 if(m_archiveOut!=NULL) 28 { 29 delete m_archiveOut; 30 m_archiveOut=NULL; 31 } 32 if(m_socketFile!=NULL) 33 { 34 delete m_socketFile; 35 m_socketFile=NULL; 36 37 } 38 39 } 40 41 void CXXXXDlg::OnBnClickedCancel() //采用指针机制,在退出时需确保指针释放 42 { 43 // TODO: 在此添加控件通知处理程序代码 44 Reset(); 45 OnCancel(); 46 }

 

 3、实例化套接字对象,并更新Dlg.cpp中的函数

1)客户端:在连接时实例化一个Socket对象,并绑定指针到主窗口,创建串行化对象用于接发写;

 1 void Ccase005Dlg::OnBnClickedBnConnect()
 2 {
 3     // TODO: 在此添加控件通知处理程序代码
 4     if(!AfxSocketInit())   //套接字初始化失败提示
 5     {
 6         MessageBox("windowsocket initial failed ","Receive",MB_ICONSTOP);
 7         return;
 8     }
 9     m_clientsocket=new CMySocket;
10     m_clientsocket->pGetDlg(this);
11     m_clientsocket->Create();
12 
13     BYTE nFild[4];
14     CString strIP;
15     UpdateData();
16 
17     m_edit_ip.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);
18     strIP.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);
19 
20     if(!m_clientsocket->Connect(strIP,atoi(m_str_port)))   //创建失败提示,异步通信是在网络事件响应时触发nErrorCoe
21 
22     {
23         AfxMessageBox("连接失败,请您重试!");
24         return ;
25     }
26     else
27     {
28         m_listbox.AddString("连接成功!");
29 //        m_listbox.SetTopIndex(m_listbox.GetCount()-1);
30         m_socketFile=new CSocketFile(m_clientsocket);
31         m_ArchiveIn=new CArchive(m_socketFile,CArchive::load);
32         m_ArchiveOut=new CArchive(m_socketFile,CArchive::store);    //用于发送写
33 
34         m_edit_ip.EnableWindow(FALSE);
35         m_edit_port.EnableWindow(FALSE);
36         m_bn_connect.EnableWindow(FALSE);
37         m_bn_disconnect.EnableWindow(TRUE);
38         m_bn_clear.EnableWindow(TRUE);
39         m_bn_send.EnableWindow(TRUE);
40         m_bn_rewrite.EnableWindow(TRUE);
41         m_editbox.EnableWindow(TRUE);
42     }
43 }
44 
45 void Ccase005Dlg::OnBnClickedBnDisconnect()
46 {
47     // TODO: 在此添加控件通知处理程序代码
48     m_listbox.AddString("断开连接!");
49     Reset();
50 
51     m_edit_ip.EnableWindow(TRUE);
52     m_edit_port.EnableWindow(TRUE);
53     m_bn_connect.EnableWindow(TRUE);
54     m_bn_disconnect.EnableWindow(FALSE);
55     m_bn_clear.EnableWindow(TRUE);
56     m_bn_send.EnableWindow(FALSE);
57     m_bn_rewrite.EnableWindow(FALSE);
58     m_editbox.EnableWindow(FALSE);
59 }

 

2)服务器端:监听在获取IP地址后,调用Create创建套接字,并侦听连接请求。

 1 void Ccase006Dlg::OnBnClickedBnListen()
 2 {
 3     // TODO: 在此添加控件通知处理程序代码
 4     if(!AfxSocketInit())      
 5     {
 6         MessageBox("Windowsocket initial failed!","Send",MB_ICONSTOP);
 7         return ;
 8     }
 9     m_listensocket=new CMySocket;        //创建套接字对象
10     m_listensocket->pGetDlg(this);
11     BYTE nFild[4];
12     CString strIP;
13     UpdateData();                 //更新获取数据
14     m_edit_ip.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);
15     strIP.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);
16     m_listensocket->Create(atoi(m_str_port),1,strIP);      //此处的Create是三参数
17     m_listensocket->Listen(1);
18     m_listbox.AddString("监听开始");
19     m_listbox.AddString("地址"+strIP+" 端口"+m_str_port);
20     m_listbox.AddString("等待客户端连接....");
21 
22 }
23 
24 void Ccase006Dlg::OnBnClickedBnStoplisten()
25 {
26     // TODO: 在此添加控件通知处理程序代码
27     m_listensocket->Close();
28     if(m_listensocket!=NULL)
29     {
30         delete m_listensocket;
31         m_listensocket=NULL;
32     }
33     m_listbox.AddString("停止监听");
34 }

 完成OnAccept函数,并调用AsyncSelect准备随时接收信息。

 1 void Ccase006Dlg::OnAccept(void)
 2 {
 3     m_serversocket=new CMySocket;
 4     m_serversocket->pGetDlg(this);
 5     m_listensocket->Accept(*m_serversocket);
 6     m_serversocket->AsyncSelect(FD_READ|FD_CLOSE);
 7     
 8     m_socketfile=new CSocketFile(m_serversocket);
 9     m_archiveIn=new CArchive(m_socketfile,CArchive::load);
10     m_archiveOut=new CArchive(m_socketfile,CArchive::store);
11     m_listbox.AddString("接收到连接请求");
12     m_listbox.SetTopIndex(m_listbox.GetCount()-1);
13 }

 

 

3)完成其他对应的功能模块:发送信息、接收信息、断开连接、清空列表、重新输入、OnClose、OnReceive。可以通用。

 1 void Ccase005Dlg::OnBnClickedBnSend()
 2 {
 3     // TODO: 在此添加控件通知处理程序代码
 4     UpdateData();
 5     *m_ArchiveOut<<m_str_words;
 6     m_ArchiveOut->Flush();
 7     m_listbox.AddString("发送: "+m_str_words);
 8     m_listbox.SetTopIndex(m_listbox.GetCount()-1);
 9 
10     m_editbox.SetWindowText("");    //注意发送后清空输入内容
11     m_editbox.SetFocus();    //发送后焦点指定在编辑栏
12 }
13 void Ccase005Dlg::OnBnClickedBnRewrite()
14 {
15     // TODO: 在此添加控件通知处理程序代码
16     m_editbox.SetWindowText("");
17     m_editbox.SetFocus();
18 }
19 
20 void Ccase005Dlg::OnBnClickedBnClear()
21 {
22     // TODO: 在此添加控件通知处理程序代码
23     m_listbox.ResetContent();
24 }
25 
26 
27 void Ccase005Dlg::OnReceive(void)
28 {
29     *m_ArchiveIn>>recvfile;
30     m_ArchiveIn->Flush();
31     m_listbox.AddString("收到: "+recvfile);
32     m_listbox.SetTopIndex(m_listbox.GetCount()-1);
33 }
34 
35 void Ccase005Dlg::OnClose(void)
36 {
37     Reset();
38     m_listbox.AddString("服务器断开了");
39 //    m_listbox.SetTopIndex(m_listbox.GetCount()-1);
40 
41     m_edit_ip.EnableWindow(TRUE);
42     m_edit_port.EnableWindow(TRUE);
43     m_bn_connect.EnableWindow(TRUE);
44     m_bn_disconnect.EnableWindow(FALSE);
45     m_bn_clear.EnableWindow(TRUE);
46     m_bn_send.EnableWindow(FALSE);
47     m_bn_rewrite.EnableWindow(FALSE);
48     m_editbox.EnableWindow(FALSE);
49 }

 

4 、实现网络事件响应函数

在执行相应按钮操作后,系统会根据程序运行自动触发响应。因采用指针调用机制。所有处理事件的实现已经在主程序体中完成, 使用指针调回主程序接口即可. 

 1 void CMySocket::OnClose(int nErrorCode)
 2 {
 3     // TODO: 在此添加专用代码和/或调用基类
 4     m_dlg->OnClose();
 5     CSocket::OnClose(nErrorCode);
 6 }
 7 
 8 void CMySocket::OnReceive(int nErrorCode)
 9 {
10     // TODO: 在此添加专用代码和/或调用基类
11     m_dlg->OnReceive();
12     AsyncSelect(FD_READ|FD_CLOSE|FD_WRITE);    //需使用此条用于随时接收信息
13     CSocket::OnReceive(nErrorCode);
14 }

 

5、大功告成。

 

小结: 

1) 同步通信与异步通信各有千秋,理解其机制运行;

2)多保存,以免网页程序崩溃;

3)基本操作要烂熟于心。

posted on 2019-03-31 22:58  tobyte  阅读(513)  评论(0编辑  收藏  举报