3.具體代碼:
3.1聲明全局變量
struct socket_info {……} ; //如前所示
extern CList
extern CClientSocket* lSocket; //用于標識當前正在通信的連接
extern int id; //用于指示用戶選擇的要進行通信的連接
extern char pBuf[100]; //接收緩沖區
3.2實現CClientDlg.cpp中的響應函數:
void CClientDlg::OnServerConflogin()//"用戶登陸"菜單響應函數
{ CLoginDlg dlg;
int t=0; //記錄失敗的連接數
int size=s_info.GetSize(); //查看當前連接鏈表的長度
if(dlg.DoModal()==IDOK)
{ socket_info* pInfo; //聲明結構體
for(int i=0;i { pInfo = new socket_info; pInfo->s_client=new CClientSocket(); if(!(pInfo->s_client->Create())) //創建socket { delete pInfo->s_client; pInfo->s_client=NULL; } if(!(pInfo->s_client->Connect(m_strIpaddress,m_Port))) //連接 { t++; //如果失敗,t增加,釋放空間 delete pInfo->s_client; pInfo->s_client=NULL; } else{ //如果成功 pInfo->id =size+i; //設置當前連接的id pInfo->username=dlg.m_strUsername; s_info.AddTail(*pInfo); //將成功的連接加入鏈表 } } int c=dlg.m_nUserCount-t; //得到成功的連接數 char message[10]; ::sprintf(message,"%d",c); //將數字轉換成字符串 strcat(message,"個連接成功"); if(c>=0) AfxMessageBox(message); //彈出提示對話框 } } 3.3實現CommunicationDlg.cpp中的響應函數: void CCommunicationDlg::OnBnClickedQuery()//"發送"按鈕的響應函數 { lSocket=NULL; UpdateData(); id=atoi(m_strQueryId); //獲得用戶輸入的連接號 POSITION pos; if(!s_info.IsEmpty()) { socket_info info=s_info.GetHead(); if(id>=s_info.GetCount()) //可選擇的id必須小于鏈表的大小 MessageBox("the data is larger than the count of the list","Alert",MB_OK); else{ for(pos=s_info.GetHeadPosition();;) //遍歷整個鏈表 { if(info.id==id&&!info.s_client==NULL) { lSocket=info.s_client; //將用戶指定的連接的socket賦予lSocket lSocket->Send(m_strSendData,m_strSendData.GetLength()); //發送m_strSendData文本框中的文本 break; } if(pos==NULL) break; else info =s_info.GetNext(pos); } } }else AfxMessageBox("the queer is empty!"); //鏈表為空 } void CCommunicationDlg::OnBnClickedAdd() //"接收"按鈕的響應函數 { UpdateData(); BOOL MsgEnd=TRUE; int iRecv; //每次讀取的字符數 if(!lSocket==NULL) { memset(pBuf,0,100); //清空緩沖區 do{ iRecv=lSocket->Receive(pBuf,100);//接收數據 if(iRecv<100&&iRecv>0) { MsgEnd=TRUE;} pBuf[iRecv]=0; //給緩沖區結尾,即賦'\0' }while(!MsgEnd); m_ReceData.SetSel(0,-1); m_ReceData.ReplaceSel(pBuf); //在m_ReceData文本框中顯示接收的字符 } else AfxMessageBox("the socket was disconnected"); } void CCommunicationDlg::OnBnClickedCancel()//"斷開該連接"按鈕的響應函數 與"退出"菜單響應函數類似,不同之處在于,退出菜單要清空整個隊列,而"斷開該連接"函數僅僅是找到當前的正在通信的連接并將其斷開。 …… socket_info info=s_info.GetAt(s_info.FindIndex(id)); //找到id對應的結構體 if(!info.s_client==NULL){ if(info.s_client->ShutDown(2)){ //斷開該連接 info.s_client->Close(); delete info.s_client; info.s_client=NULL; s_info.RemoveAt(s_info.FindIndex(id)); //從鏈表中刪除該結構體 AfxMessageBox("Disconnect suclearcase/" target="_blank" >ccessfully!"); 4.設計技巧 在設計中,我們要注意幾個問題,這些問題的解決直接影響到程序的性能。 4.1對于一個基于對話框的應用程序,Visual MFC應用程序向導不會給對話框創建菜單。如果要在對話框中顯示菜單,必須把它作為一個 資源,并連接到對話框窗口。具體步驟: 右擊資源試圖的"菜單"選項,創建一個菜單IDR_MENU1,添加菜單項; 打開資源試圖的"對話框"選項,右擊對話框(IDD_CLIENT_DIALOG),選擇"屬性",在彈出的屬性表中找到"Menu",將它的值設為IDR_MENU1; 4.2用戶要建立連接,就要指定連接數,問題是,用戶不一定一次指定所有的連接。比如說,第一次,用戶指定了50個連接,程序將50個連接加入到連接隊列中。經過測試,用戶發現50個連接運行情況良好,于是,用戶想要測試100個連接的運行情況,這時,我們不能要求用戶退出并重新運行程序,然后指定100個連接重新進行測試。我們要做的就是讓用戶能夠再次指定50個連接,并且將這50個連接加入到前50個連接的后面。所以,在設計時,每次建立指定數目的連接前,必須查詢隊列的長度,然后將建立的連接加入到隊列中正確的位置上。正如"用戶登陸"菜單響應函數所示: int size=s_info.GetSize();//查看當前連接鏈表的長度 pInfo->id =size+i; //設置當前連接的id 4.3每次用戶指定某個連接進行測試時,程序都要自動搜索連接隊列找到指定的連接,并發送信息。問題是,我們的發送和接收是不同的響應函數,如何保證接收信息的連接是用戶指定的連接呢?例如,用戶建立了100個連接,指定Id為59的連接進行通信,當發送數據時,程序自動找到Id=59的socket發送數據,可是,當接收數據時,程序怎么知道是哪個連接負責接收數據呢?我們可以在接收響應函數中再次查找隊列,但是,這樣不但增加了系統資源的消耗,而且增加了系統的延遲。我們采用全局變量lSocket來解決這個問題。 lSocket=info.s_client;//將用戶指定的連接的socket賦予lSocket lSocket->Send(m_strSendData,m_strSendData.GetLength());//發文本框中的文本 iRecv=lSocket->Receive(pBuf,100); 4.4每次用戶退出程序之前,必須斷開所有連接,并清空隊列。如"退出"菜單響應函數所示。 5.結束語 經過實際驗證,該程序能夠很好的測試服務器的連接承受能力,從理論上來說,用戶可以指定任意多的連接,實際上,連接數受到計算機資源的限制。在通信過程中,各個連接能夠良好的進行,不會互相干擾。 原文轉自:http://www.anti-gravitydesign.com