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、运行调试,也可以加入一些相应的控件属性控制,更方便处理
浙公网安备 33010602011771号