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.结束语 经过实际验证,该程序能够很好的测试服务器的连接承受能力,从理论上来说,用户可以指定任意多的连接,实际上,连接数受到计算机资源的限制。在通信过程中,各个连接能够良好的进行,不会互相干扰。