基於C/S 結構的IM即時通訊軟件--下篇
阿新 • • 發佈:2019-04-01
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即時通訊軟件--下篇