1. 程式人生 > >基於C/S 結構的IM即時通訊軟件--下篇

基於C/S 結構的IM即時通訊軟件--下篇

rom 運行 開啟 room 接受 截取 cti amp roo

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、運行調試,也可以加入一些相應的控件屬性控制,更方便處理

基於C/S 結構的IM即時通訊軟件--下篇