基于C/S 结构的IM即时通讯软件--下篇

3、实现界面事件函数

    客户端:单击" 进入" 按钮发送请求,如果要与服务器通信,必须要同时发送结构体信息描述发送内容,便于服务器处理。

void CCase010Dlg::OnBnClickedBnIn()
{
    // TODO: 在此添加控件通知处理程序代码
    UpdateData();
    clientsocket=new CClientSocket;
    clientsocket->GetDlg(this);

    BYTE nfield[4];
    CString strIP;
    m_edit_IP.GetAddress(nfield[0],nfield[1],nfield[2],nfield[3]);
    strIP.Format("%d.%d.%d.%d",nfield[0],nfield[1],nfield[2],nfield[3]);

    if(m_str_name.IsEmpty())
    {
        AfxMessageBox("请先登记管理员名!");
        return ;
    }
    if(strIP.IsEmpty())
    {
        AfxMessageBox("请配置聊天室IP");
        return ;
    }
    if(m_str_port.IsEmpty())
    {
        AfxMessageBox("请配置要开放的端口");
        return ;
    }
    if(!clientsocket->Create())
    {
        AfxMessageBox("网络创建错误!!");
        return ;
    }
    if(!clientsocket->Connect(strIP,atoi(m_str_port)))
    {
        AfxMessageBox("服务器连接错误");
        clientsocket->Close();
        return ;
    }

    Header head;      //定义头文件
    head.type = LOGIN_IO;         //定义为登录类型
    head.len = m_strName.GetLength();
    clientsocket->Send((char*)&head,sizeof(Header));   //发送头文件
    clientsocket->Send( m_strName, m_strName.GetLength());
    
    theApp.m_str_name=m_str_name;    
    m_editbox.SetWindowText("");
    this->SetWindowText(m_str_name+"客户端");
}    

        客户端发送、接收、更新用户列表信息

 1 void CCase010Dlg::OnBnClickedBnSend()
 2 {
 3     // TODO: 在此添加控件通知处理程序代码
 4     UpdateData();
 5     if(m_str_words=="")
 6     {
 7         AfxMessageBox("请输入要发送的信息");
 8         return ;
 9     }
10     Header head;
11     head.type=SEND_MESSAGE;    //聊天信息
12     head.len=m_str_words.GetLength();
13     clientsocket->Send((char*)&head,sizeof(Header));      //发送结构体信息
14     if(clientsocket->Send(m_str_words,m_str_words.GetLength()))
15     {
16         m_str_words="";
17         UpdateData(FALSE);
18         m_edit_words.SetFocus();
19         return;
20     }
21     else
22     {
23         AfxMessageBox("网络传输错误!");
24                 return ;
25     }
26 }
27 
28 BOOL CCase010Dlg::GetmsgFromRoom()         //信息处理函数
29 {
30 
31     char buff[100];
32     memset(buff,0,sizeof(buff));                               //采用Send/Receive方式对于字符的输入要有初始化设置,相比使用串行化方式不方便
33     clientsocket->Receive(buff,sizeof(buff));        //接收信息;
34     clientsocket->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
35 
36     CString strTemp=buff;
37     strTemp +="\r\n";                              //因为是编辑框控件,增加换行结尾符
38     m_editbox.ReplaceSel(strTemp);        //直接显示在界面上
39     return TRUE;
40 }
41 
42 void CCase010Dlg::Updateuser()             //用户列表更新函数
43 {
44     char buff[1000];
45     memset(buff,0,sizeof(buff));
46     clientsocket->Receive(buff,sizeof(buff));        //接收信息
47         clientsocket->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
48 
49     CString user_info=buff;
50     CString array[100];
51     int b=0;
52     for(int i=0;i<user_info.GetLength();i++)        //遍历名单中所有用户
53     {
54         if(i!=(user_info.GetLength()-1))
55         {
56             if(user_info[i]=='&')    b++;            //用户均采用&进行连接
57             else     array[b]=array[b]+user_info[i];   //也可采用left/right/mid函数进行截取
58         }
59     }
60     m_listbox.ResetContent();                      //刷新用户列表
61     for(int j=0;j<=b;j++)        //显示更新用户列表
62     {
63         m_listbox.AddString(array[j]);
64     }
65 }

     其他通用处理函数完善

 1 void CXXXXDlg::OnBnClickedBnLeave()
 2 {
 3     // TODO: 在此添加控件通知处理程序代码
 4     if(clientsocket!=NULL)
 5         {
 6                clientsocket->Close();        //关闭对象
 7            delete clientsocket;
 8               clientsocket=NULL;
 9          }    
10     CTime time=CTime::GetCurrentTime();
11     CString temp = time.Format("%H:%M:%S");
12     CString strTemp=temp+theApp.m_str_name+" 关闭(退出)聊天室\r\n";
13     m_editbox.ReplaceSel(strTemp);
14     m_listbox.ResetContent();
15     this->SetWindowText("聊天室管理");
16 }
17 
18 void CXXXXDlg::OnBnClickedBnExit()          //采用指针机制,需要释放
19 {
20     // TODO: 在此添加控件通知处理程序代码
21     Reset();
22     OnCancel();
23 }
24 
25 void CXXXXDlg::Reset()
26 {
27     if(clientsocket!=NULL)
28     {
29         delete clientsocket;
30         clientsocket=NULL;
31     }
32 
33 }

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

在执行相应按钮操作后,系统会根据程序运行自动触发响应。在socket实例对象中重写相应的处理函数。客户端系统发起连接触发connect进行跟进,服务器端系统接收到connect请求触发accept响应,此时建立起连接,通过receive接收程序发送的数据,最后close关闭释放套接字。

1)服务器端: 服务器端开启监听后, 接收到连接请求触发OnAccept

1 void CServerSocket::OnAccept(int nErrorCode)
2 {
3     // TODO: 在此添加专用代码和/或调用基类    
4     CClientSocket *clientsocket= new CClientSocket(&connectList); //创建socket队列结构
5     Accept(*clientsocket);        //接收连接
6     clientsocket->m_dlgserver=(CCase011Dlg*)AfxGetMainWnd();
7     connectList.AddTail(clientsocket);        //在队列尾中添加新成员socket
8     CSocket::OnAccept(nErrorCode);
9 }

       服务器Socket队列中收到对应客户端套接字后触发OnReceive,对信息头进行解析后分别处理

 1 void CClientSocket::OnReceive(int nErrorCode)
 2 {
 3     // TODO: 在此添加专用代码和/或调用基类
 4     char buff1[sizeof(Header)];
 5     memset(buff1,0,sizeof(buff1));
 6     Receive(buff1,sizeof(buff1));            //先接收头部信息
 7 
 8     this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
 9     Header *header = (Header*)buff1;
10     int length= header ->len;
11     char type=header->type;        //解析头部内容
12     if(type==LOGIN_IO)    //头部类型为LOGIN_IO
13     {
14         char buff[100];
15         memset(buff,0,sizeof(buff));
16         Receive(buff,length);        //继续接受这条信息的数据部分(成员名)
17         this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
18     
19         m_dlgserver->UpdateData();        
20         CTime time=CTime::GetCurrentTime();
21         CString temp=time.Format("%H:%M:%S");
22         CEdit* p_Edit=(CEdit*)::AfxGetMainWnd()->GetDlgItem(IDC_LISTBOX);
23 
24         CString strTemp=temp+" "+CString(buff)+"进入聊天室\r\n";    //生成通知消息
25         p_Edit->ReplaceSel(strTemp);
26         m_strName=buff;            //将新加成员的用户名登记在服务器对应的socket中
27     
28         Header head;                            //生成新的通知消息群发给用户
29         head.type=SEND_MESSAGE;
30         head.len=strTemp.GetLength();
31     
32         Header head_history;    
33         head_history.type=SEND_MESSAGE;
34         m_dlgserver->m_str_words+=m_str_name+",欢迎加入!\r\n";    //生成欢迎消息
35         head_history.len=m_dlgserver->m_str_words.GetLength();
36     
37         CClientSocket *curr=NULL;           
38         POSITION pos=clist->GetHeadPosition();    //获取表头
39         while(pos!=NULL)
40         {
41             curr=(CClientSocket*)clist->GetNext(pos);
42             if(curr->m_str_name==m_str_name)        //给新加入的成员发送欢迎消息
43             {
44                 curr->Send((char*)&head_history,sizeof(Header));
45                 curr->Send(m_dlgserver->m_str_words,m_dlgserver->m_str_words.GetLength());
46             }
47             else                //向其他老成员发送通知消息,告知有新成员加入
48             {
49                 curr->Send((char*)&head,sizeof(Header));
50                 curr->Send(strTemp,strTemp.GetLength());
51             }
52         }
53         m_dlgserver->UpdateUser(this);        //更新用户列表
54 
55     }
56     if(type==SEND_MESSAGE)            //聊天信息
57     {
58         char buff[1000];
59         memset(buff,0,sizeof(buff));
60         Receive(buff,sizeof(buff));
61         this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
62         CTime time=CTime::GetCurrentTime();
63         CString temp=time.Format("%H:%M:%S");
64         CString nickname=this->m_strName;
65         CString strTemp=temp+" "+nickname+"说:"+CString(buff)+"\r\n";
66         CString str=nickname+"   "+temp+"\r\n"+"  "+CString(buff);
67         CEdit *p_Edit=(CEdit*)::AfxGetMainWnd()->GetDlgItem(IDC_EDITBOX);
68         p_Edit->ReplaceSel(strTemp);
69         CClientSocket*curr =NULL;
70         POSITION pos=clist->GetHeadPosition();
71         while(pos!=NULL)        //向所有成员转发聊天信息
72         {
73             curr=(CClientSocket*)clist->GetNext(pos);
74             curr->Send((char*)header,sizeof(Header));
75             curr->Send(str,str.GetLength());
76         }
77     }
78     CSocket::OnReceive(nErrorCode);
79 }

     如客户端退出关闭本地Socket, 就会触发服务器socket队列中对应对象的OnClose事件

 1 void CClientSocket::OnClose(int nErrorCode)
 2 {
 3     // TODO: 在此添加专用代码和/或调用基类
 4     POSITION pos = clist ->Find(this);            
 5     if(pos!=NULL)
 6     {
 7         clist->RemoveAt(pos);                //移除服务器socket队列中的套接字
 8         CTime time=CTime::GetCurrentTime();
 9         CString temp=time.Format("%H:%M:%S");
10         CEdit *p_Edit=(CEdit*)m_dlgserver->GetDlgItem(IDC_EDITBOX);    //定义用户标识框
11         CString strTemp=temp+" "+this->m_strName+"离开聊天室!\r\n";
12         p_Edit->ReplaceSel(strTemp);
13 
14         Header head;                                      //生成通知类消息
15         head.type=SEND_MESSAGE;
16         head.len=strTemp.GetLength();        //头部信息更新
17 
18         CClientSocket *curr=NULL;
19         POSITION pos=clist->GetHeadPosition();
20         while(pos!=NULL)        //将此用户离开信息告知其他成员
21         {
22             curr=(CClientSocket*)clist->GetNext(pos);
23             curr->Send((char*)&head,sizeof(Header));
24             curr->Send(strTemp,strTemp.GetLength());
25         }
26         m_dlgserver->UpdateUser(this);        //更新服务器用户列表
27         this->Close();
28         delete this;
29     }
30 
31     CSocket::OnClose(nErrorCode);
32 }

2) 客户端:客户端接收到信息后对信息中结构体先进行解析,然后分别调用相应的成员函数处理。

 1 void CClientSocket::OnReceive(int nErrorCode)
 2 {
 3     // TODO: 在此添加专用代码和/或调用基类
 4     char buff[sizeof(Header)];
 5     memset(buff,0,sizeof(buff));
 6     Receive(buff,sizeof(buff));        //先接收头部信息
 7     this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
 8 
 9     Header *header =(Header *)buff;
10     int length = header->len;
11     char type=header->type;
12 
13     if(type==SEND_MESSAGE)        //解析信息头部,如果信息过多,可以使用swich/case进行判定
14     {
15         m_dlg->GetmsgFromRoom();        //聊天内容则直接接收
16     }
17     if(type==LOGIN_IO)
18     {
19         m_dlg->Updateuser();            //否则更新用户列表
20     }
21     CSocket::OnReceive(nErrorCode);
22 }

5、运行调试,也可以加入一些相应的控件属性控制,更方便处理

猜你喜欢

转载自www.cnblogs.com/maxonzou/p/10638168.html