• 基于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 +="
    ";                              //因为是编辑框控件,增加换行结尾符
    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+" 关闭(退出)聊天室
    ";
    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)+"进入聊天室
    ";    //生成通知消息
    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+",欢迎加入!
    ";    //生成欢迎消息
    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)+"
    ";
    66         CString str=nickname+"   "+temp+"
    "+"  "+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+"离开聊天室!
    ";
    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、运行调试,也可以加入一些相应的控件属性控制,更方便处理

  • 相关阅读:
    各种集群服务
    cdn
    网页请求的完整过程
    html
    ajax异步请求技术
    浅谈前端渲染与后端渲染的区别
    html与php
    Ubuntu安装anaconda3
    win10安装Ubuntu系统
    删除排序数组中的重复项
  • 原文地址:https://www.cnblogs.com/maxonzou/p/10638168.html
Copyright © 2020-2023  润新知