国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

基于.NET的多用戶客戶端設(shè)計

2019-11-17 04:39:47
字體:
供稿:網(wǎng)友


  摘要: 在C/S 模式中,服務(wù)器端往往是設(shè)計的重點。為了測試服務(wù)器的性能,不得不使用大量的計算機作為客戶端。在實際情況中,往往無法提供大量的計算機用以測試,同時,這也是資源的浪費,為了解決這個問題,本文提出了模擬多用戶客戶端的設(shè)計方法,并給出具體代碼說明。


  要害字:多用戶 客戶端 連接隊列

  1. 概述

  在C/S 模式中,服務(wù)器端往往是設(shè)計的重點。一般來說,服務(wù)器的能夠承受的連接數(shù)量是衡量一個服務(wù)器性能好壞的重要標(biāo)準(zhǔn),為了測試服務(wù)器能夠承受的連接數(shù),我們必須使用多臺客戶機來測試他的性能.可是,很多情況下,我們沒有那么多的機器,同時使用多臺機器進(jìn)行測試也是浪費資源,為此,我們設(shè)計了模擬多用戶客戶端程序來解決這個問題。

  本文采用MFC的CSocket類在.NET平臺上進(jìn)行設(shè)計.所謂的模擬多用戶就是用一個客戶端程序來建立多個與服務(wù)器的連接,就似乎多個客戶端與服務(wù)器進(jìn)行連接一樣。設(shè)計的重點是:

   程序能夠生成用戶指定的數(shù)目的連接;

   用戶可以在建立的連接中任意指定某個連接進(jìn)行通信;

   用戶可以隨意更換連接進(jìn)行通信測試,每個連接不會互相混淆,尤其是在讀寫數(shù)據(jù)的時候,不能張冠李戴;

   用戶可以隨意指定斷開某個連接,而不會影響其他連接。

  那么這么多的連接究竟如何治理呢?

  首先,我們要有一種數(shù)據(jù)結(jié)構(gòu)來描述每個連接的具體情況。本文采用了結(jié)構(gòu)體。

  自定義結(jié)構(gòu)體strUCt socket_info
{ CSocket* s_client; //保存用戶的SOCKET值
u_long client_addr; //保存用戶網(wǎng)絡(luò)地址
CString username; //用戶昵稱
int id; //連接號
} ;  然后,使用C++的模板類CList來治理這些連接。以后所做的所有事情就是對這個鏈表的操作。

  2.設(shè)計步驟:

  2.1創(chuàng)建一個基于對話框的工程CClientDlg.在MFC應(yīng)用程序向?qū)е羞x中windows 套接字。

  2.2給對話框添加菜單,并添加菜單項,包括配置服務(wù)器、用戶登陸、退出、通信、斷開連接。

  2.3添加"配置服務(wù)器"響應(yīng)函數(shù)OnServerConfserver(),調(diào)出服務(wù)器配置對話框Server Configure。輸入服務(wù)器的
  2.5 添加"通信"響應(yīng)函數(shù)OnCommunication(),調(diào)出通信對話框,如圖1所示。點擊"發(fā)送",發(fā)送數(shù)據(jù);點擊"接收",接收數(shù)據(jù);點擊"斷開該連接",關(guān)閉socket,并從鏈表中刪除該連接。

  2.6 添加"退出"菜單響應(yīng)函數(shù)。遍歷整個連接隊列,將所有的連接斷開并刪除隊列中所有結(jié)構(gòu)體,將隊列清空。

進(jìn)入討論組討論。
  3.具體代碼:

  3.1 聲明全局變量
struct socket_info {……} ; //如前所示
extern CList<socket_info,socket_info&> s_info; //鏈表類
extern CClientSocket* lSocket; //用于標(biāo)識當(dāng)前正在通信的連接
extern int id; //用于指示用戶選擇的要進(jìn)行通信的連接
extern char pBuf[100]; //接收緩沖區(qū)  3.2 實現(xiàn)CClientDlg.cpp中的響應(yīng)函數(shù):
void CClientDlg::OnServerConflogin()//"用戶登陸"菜單響應(yīng)函數(shù)
{ CLoginDlg dlg;
int t=0; //記錄失敗的連接數(shù)
int size=s_info.GetSize(); //查看當(dāng)前連接鏈表的長度
if(dlg.DoModal()==IDOK)
{ socket_info* pInfo; //聲明結(jié)構(gòu)體
for(int i=0;i<dlg.m_nUserCount;i++)
{ pInfo = new socket_info;
pInfo->s_client=new CClientSocket();
if(!(pInfo->s_client->Create())) //創(chuàng)建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; //設(shè)置當(dāng)前連接的id
pInfo->username=dlg.m_strUsername;
s_info.AddTail(*pInfo); //將成功的連接加入鏈表
}
}
int c=dlg.m_nUserCount-t; //得到成功的連接數(shù)
char message[10];
::s
strcat(message,"個連接成功");
if(c>=0) AfxMessageBox(message); //彈出提示對話框
}
}  3.3 實現(xiàn)CommunicationDlg.cpp中的響應(yīng)函數(shù):
void CCommunicationDlg::OnBnClickedQuery()//"發(fā)送"按鈕的響應(yīng)函數(shù)
{ 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());
//發(fā)送m_strSendData文本框中的文本
break; }
if(pos==NULL) break;
else info =s_info.GetNext(pos);
}
}
}else AfxMessageBox("the queer is empty!"); //鏈表為空
}void CCommunicationDlg::OnBnClickedAdd() //"接收"按鈕的響應(yīng)函數(shù)
{ UpdateData();
BOOL MsgEnd=TRUE;
int iRecv; //每次讀取的字符數(shù)
if(!lSocket==NULL)
{ memset(pBuf,0,100); //清空緩沖區(qū)
do{ iRecv=lSocket->Receive(pBuf,100);//接收數(shù)據(jù)
if(iRecv<100&&iRecv>0) { MsgEnd=TRUE;}
pBuf[iRecv]=0; //給緩沖區(qū)結(jié)尾,即賦'/0'
}while(!MsgEnd);
m_ReceData.SetSel(0,-1);
m_ReceData.ReplaceSel(pBuf); //在m_ReceData文本框中顯示接收的字符
} else AfxMessageBox("the socket was disconnected");
}void CCommunicationDlg::OnBnClickedCancel()//"斷開該連接"按鈕的響應(yīng)函數(shù)  與"退出"菜單響應(yīng)函數(shù)類似,不同之處在于,退出菜單要清空整個隊列,而"斷開該連接"函數(shù)僅僅是找到當(dāng)前的正在通信的連接并將其斷開。
……
socket_info info=s_info.GetAt(s_info.FindIndex(id)); //找到id對應(yīng)的結(jié)構(gòu)體
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)); //從鏈表中刪除該結(jié)構(gòu)體
AfxMessageBox("Disconnect successfully!");   4. 設(shè)計技巧

  在設(shè)計中,我們要注重幾個問題,這些問題的解決直接影響到程序的性能。

  4.1 對于一個基于對話框的應(yīng)用程序,Visual MFC應(yīng)用程序向?qū)Р粫o對話框創(chuàng)建菜單。假如要在對話框中顯示菜單,必須把它作為一個資源,并連接到對話框窗口。具體步驟:

   右擊資源試圖的"菜單"選項,創(chuàng)建一個菜單IDR_MENU1,添加菜單項;

   打開資源試圖的"對話框"選項,右擊對話框(IDD_CLIENT_DIALOG),選擇"屬性", 在彈出的屬性表中找到"Menu",將它的值設(shè)為IDR_MENU1;
  4.2 用戶要建立連接,就要指定連接數(shù),問題是,用戶不一定一次指定所有的連接。比如說,第一次,用戶指定了50個連接,程序?qū)?0個連接加入到連接隊列中。經(jīng)過測試,用戶發(fā)現(xiàn)50個連接運行情況良好,于是,用戶想要測試100個連接的運行情況,這時,我們不能要求用戶退出并重新運行程序,然后指定100個連接重新進(jìn)行測試。我們要做的就是讓用戶能夠再次指定50個連接,并且將這50個連接加入到前50個連接的后面。所以,在設(shè)計時,每次建立指定數(shù)目的連接前,必須查詢隊列的長度,然后將建立的連接加入到隊列中正確的位置上。正如"用戶登陸"菜單響應(yīng)函數(shù)所示:
int size=s_info.GetSize();//查看當(dāng)前連接鏈表的長度

pInfo->id =size+i; //設(shè)置當(dāng)前連接的id  4.3 每次用戶指定某個連接進(jìn)行測試時,程序都要自動搜索連接隊列找到指定的連接,并發(fā)送信息。問題是,我們的發(fā)送和接收是不同的響應(yīng)函數(shù),如何保證接收信息的連接是用戶指定的連接呢?例如,用戶建立了100個連接,指定Id為59的連接進(jìn)行通信,當(dāng)發(fā)送數(shù)據(jù)時,程序自動找到Id=59的socket發(fā)送數(shù)據(jù),可是,當(dāng)接收數(shù)據(jù)時,程序怎么知道是哪個連接負(fù)責(zé)接收數(shù)據(jù)呢?我們可以在接收響應(yīng)函數(shù)中再次查找隊列,但是,這樣不但增加了系統(tǒng)資源的消耗,而且增加了系統(tǒng)的延遲。我們采用全局變量lSocket來解決這個問題。
lSocket=info.s_client;//將用戶指定的連接的socket賦予lSocket
lSocket->Send(m_strSendData,m_strSendData.GetLength());//發(fā)文本框中的文本
iRecv=lSocket->Receive(pBuf,100);   4.4 每次用戶退出程序之前,必須斷開所有連接,并清空隊列。 如"退出"菜單響應(yīng)函數(shù)所示。

  5. 結(jié)束語

  經(jīng)過實際驗證,該程序能夠很好的測試服務(wù)器的連接承受能力,從理論上來說,用戶可以指定任意多的連接,實際上,連接數(shù)受到計算機資源的限制。 在通信過程中,各個連接能夠良好的進(jìn)行,不會互相干擾。 進(jìn)入討論組討論。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 克拉玛依市| 莱阳市| 繁昌县| 莱州市| 六枝特区| 贵德县| 屏边| 隆回县| 漳州市| 温州市| 沈丘县| 张家港市| 辉南县| 安达市| 利津县| 启东市| 姚安县| 永新县| 昭通市| 余姚市| 宾川县| 焉耆| 日照市| 南宫市| 江孜县| 托里县| 富阳市| 台南县| 望都县| 锦州市| 平顶山市| 明光市| 巴林右旗| 昌黎县| 东光县| 巢湖市| 绥宁县| 江达县| 阜阳市| 香河县| 吉隆县|