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、运行调试,也可以加入一些相应的控件属性控制,更方便处理
原文:https://www.cnblogs.com/maxonzou/p/10638168.html