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 }

其他通用处理函数完善

 void CXXXXDlg::OnBnClickedBnLeave()
{
// TODO: 在此添加控件通知处理程序代码
if(clientsocket!=NULL)
{
clientsocket->Close(); //关闭对象
delete clientsocket;
clientsocket=NULL;
}
CTime time=CTime::GetCurrentTime();
CString temp = time.Format("%H:%M:%S");
CString strTemp=temp+theApp.m_str_name+" 关闭(退出)聊天室\r\n";
m_editbox.ReplaceSel(strTemp);
m_listbox.ResetContent();
this->SetWindowText("聊天室管理");
} void CXXXXDlg::OnBnClickedBnExit() //采用指针机制,需要释放
{
// TODO: 在此添加控件通知处理程序代码
Reset();
OnCancel();
} void CXXXXDlg::Reset()
{
if(clientsocket!=NULL)
{
delete clientsocket;
clientsocket=NULL;
} }

4 、实现网络事件响应函数

在执行相应按钮操作后,系统会根据程序运行自动触发响应。在socket实例对象中重写相应的处理函数。客户端系统发起连接触发connect进行跟进,服务器端系统接收到connect请求触发accept响应,此时建立起连接,通过receive接收程序发送的数据,最后close关闭释放套接字。

1)服务器端: 服务器端开启监听后, 接收到连接请求触发OnAccept

 void CServerSocket::OnAccept(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
CClientSocket *clientsocket= new CClientSocket(&connectList); //创建socket队列结构
Accept(*clientsocket); //接收连接
clientsocket->m_dlgserver=(CCase011Dlg*)AfxGetMainWnd();
connectList.AddTail(clientsocket); //在队列尾中添加新成员socket
CSocket::OnAccept(nErrorCode);
}

服务器Socket队列中收到对应客户端套接字后触发OnReceive,对信息头进行解析后分别处理

 void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
char buff1[sizeof(Header)];
memset(buff1,,sizeof(buff1));
Receive(buff1,sizeof(buff1)); //先接收头部信息 this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
Header *header = (Header*)buff1;
int length= header ->len;
char type=header->type; //解析头部内容
if(type==LOGIN_IO) //头部类型为LOGIN_IO
{
char buff[];
memset(buff,,sizeof(buff));
Receive(buff,length); //继续接受这条信息的数据部分(成员名)
this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE); m_dlgserver->UpdateData();
CTime time=CTime::GetCurrentTime();
CString temp=time.Format("%H:%M:%S");
CEdit* p_Edit=(CEdit*)::AfxGetMainWnd()->GetDlgItem(IDC_LISTBOX); CString strTemp=temp+" "+CString(buff)+"进入聊天室\r\n"; //生成通知消息
p_Edit->ReplaceSel(strTemp);
m_strName=buff; //将新加成员的用户名登记在服务器对应的socket中 Header head; //生成新的通知消息群发给用户
head.type=SEND_MESSAGE;
head.len=strTemp.GetLength(); Header head_history;
head_history.type=SEND_MESSAGE;
m_dlgserver->m_str_words+=m_str_name+",欢迎加入!\r\n"; //生成欢迎消息
head_history.len=m_dlgserver->m_str_words.GetLength(); CClientSocket *curr=NULL;
POSITION pos=clist->GetHeadPosition(); //获取表头
while(pos!=NULL)
{
curr=(CClientSocket*)clist->GetNext(pos);
if(curr->m_str_name==m_str_name) //给新加入的成员发送欢迎消息
{
curr->Send((char*)&head_history,sizeof(Header));
curr->Send(m_dlgserver->m_str_words,m_dlgserver->m_str_words.GetLength());
}
else //向其他老成员发送通知消息,告知有新成员加入
{
curr->Send((char*)&head,sizeof(Header));
curr->Send(strTemp,strTemp.GetLength());
}
}
m_dlgserver->UpdateUser(this); //更新用户列表 }
if(type==SEND_MESSAGE) //聊天信息
{
char buff[];
memset(buff,,sizeof(buff));
Receive(buff,sizeof(buff));
this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);
CTime time=CTime::GetCurrentTime();
CString temp=time.Format("%H:%M:%S");
CString nickname=this->m_strName;
CString strTemp=temp+" "+nickname+"说:"+CString(buff)+"\r\n";
CString str=nickname+" "+temp+"\r\n"+" "+CString(buff);
CEdit *p_Edit=(CEdit*)::AfxGetMainWnd()->GetDlgItem(IDC_EDITBOX);
p_Edit->ReplaceSel(strTemp);
CClientSocket*curr =NULL;
POSITION pos=clist->GetHeadPosition();
while(pos!=NULL) //向所有成员转发聊天信息
{
curr=(CClientSocket*)clist->GetNext(pos);
curr->Send((char*)header,sizeof(Header));
curr->Send(str,str.GetLength());
}
}
CSocket::OnReceive(nErrorCode);
}

如客户端退出关闭本地Socket, 就会触发服务器socket队列中对应对象的OnClose事件

 void CClientSocket::OnClose(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
POSITION pos = clist ->Find(this);
if(pos!=NULL)
{
clist->RemoveAt(pos); //移除服务器socket队列中的套接字
CTime time=CTime::GetCurrentTime();
CString temp=time.Format("%H:%M:%S");
CEdit *p_Edit=(CEdit*)m_dlgserver->GetDlgItem(IDC_EDITBOX); //定义用户标识框
CString strTemp=temp+" "+this->m_strName+"离开聊天室!\r\n";
p_Edit->ReplaceSel(strTemp); Header head; //生成通知类消息
head.type=SEND_MESSAGE;
head.len=strTemp.GetLength(); //头部信息更新 CClientSocket *curr=NULL;
POSITION pos=clist->GetHeadPosition();
while(pos!=NULL) //将此用户离开信息告知其他成员
{
curr=(CClientSocket*)clist->GetNext(pos);
curr->Send((char*)&head,sizeof(Header));
curr->Send(strTemp,strTemp.GetLength());
}
m_dlgserver->UpdateUser(this); //更新服务器用户列表
this->Close();
delete this;
} CSocket::OnClose(nErrorCode);
}

2) 客户端:客户端接收到信息后对信息中结构体先进行解析,然后分别调用相应的成员函数处理。

 void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
char buff[sizeof(Header)];
memset(buff,,sizeof(buff));
Receive(buff,sizeof(buff)); //先接收头部信息
this->AsyncSelect(FD_CLOSE|FD_READ|FD_WRITE); Header *header =(Header *)buff;
int length = header->len;
char type=header->type; if(type==SEND_MESSAGE) //解析信息头部,如果信息过多,可以使用swich/case进行判定
{
m_dlg->GetmsgFromRoom(); //聊天内容则直接接收
}
if(type==LOGIN_IO)
{
m_dlg->Updateuser(); //否则更新用户列表
}
CSocket::OnReceive(nErrorCode);
}

5、运行调试,也可以加入一些相应的控件属性控制,更方便处理

基于C/S 结构的IM即时通讯软件--下篇的更多相关文章

  1. 基于C/S 结构的IM即时通讯软件--上篇

    目的:实现类似QQ群聊的聊天室,可以看到好友列表及互相传送信息. 分析:可基于C/S结构实现即时通讯 1.创建基于对话框的MFC程序(支持windows套接字),并增加相应的类与结构体,完善对话框界面 ...

  2. 基于存储过程的MVC开源分页控件--LYB.NET.SPPager

    摘要 现在基于ASP.NET MVC的分页控件我想大家都不陌生了,百度一下一大箩筐.其中有不少精品,陕北吴旗娃杨涛大哥做的分页控件MVCPager(http://www.webdiyer.com/)算 ...

  3. 基于存储过程的MVC开源分页控件

    基于存储过程的MVC开源分页控件--LYB.NET.SPPager 摘要 现在基于ASP.NET MVC的分页控件我想大家都不陌生了,百度一下一大箩筐.其中有不少精品,陕北吴旗娃杨涛大哥做的分页控件M ...

  4. Web应用程序开发,基于Ajax技术的JavaScript树形控件

    感谢http://www.cnblogs.com/dgrew/p/3181769.html#undefined 在Web应用程序开发领域,基于Ajax技术的JavaScript树形控件已经被广泛使用, ...

  5. FineUI 基于 ExtJS 的专业 ASP.NET 控件库

    FineUI 基于 ExtJS 的专业 ASP.NET 控件库 http://www.fineui.com/

  6. 基于Bootstrap的JQuery TreeView树形控件,数据支持json字符串、list集合(MVC5)<二>

    上篇博客给大家介绍了基于Bootstrap的JQuery TreeView树形控件,数据支持json字符串.list集合(MVC5)<一>, 其中的两种方式都显得有些冗余.接着上篇博客继续 ...

  7. 为什么要用hibernate 与基于数据库表结构的项目开发

    最近开始学习hibernate,其实并不知道要学习什么,有什么用.后来问了一下同事,他就说快捷方便简单,很多事情不用自己做他会帮你做好,但是我觉得不应该是这样的,于是我就去搜了一下,就搜到了一篇帖子, ...

  8. 基于Qt的第三方库和控件

    ====================== 基于Qt的第三方库和控件 ======================     libQxt --------   http://dev.libqxt.o ...

  9. java开源即时通讯软件服务端openfire源码构建

    java开源即时通讯软件服务端openfire源码构建 本文使用最新的openfire主干代码为例,讲解了如何搭建一个openfire开源开发环境,正在实现自己写java聊天软件: 编译环境搭建 调试 ...

随机推荐

  1. 获取【请求体】数据的3种方式(精)(文末代码) request.getInputStream() request.getInputStream() request.getReader()

    application/x- www-form-urlencoded是Post请求默认的请求体内容类型,也是form表单默认的类型.Servlet API规范中对该类型的请求内容提供了request. ...

  2. tomcat之虚拟目录

    一般我们都是直接引用webapps下面的web项目,如果我们要部署一个在其它地方的WEB项目,这就要在TOMCAT中设置虚拟路径了,Tomcat的加载web顺序是先加载 $Tomcat_home$\c ...

  3. tomcat 常用命令 网上找的资料

    打开终端 cd /java/tomcat #执行 bin/startup.sh #启动tomcat bin/shutdown.sh #停止tomcat tail -f logs/catalina.ou ...

  4. Python 3 教程

    http://www.runoob.com/python3/python3-tutorial.html https://www.jianshu.com/p/f1332c58ca86

  5. Handlebars.js registerHelper

    Handlebars.registerHelper('link', function (text, url) { text = Handlebars.Utils.escapeExpression(te ...

  6. c# 执行javascript 脚本

    /// <summary> /// 执行JS /// this.ExecuteScript("get('{0}')".FormatWith(token0), File. ...

  7. DDS生成正弦波

    DDS生成正弦波 `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////// ...

  8. Cheat Engine(简称CE)初体验

    经常玩一些单机游戏,如CS.植物大战僵尸,,这几天刚好网上看到有人用CE修改内存,直接修改内存数据. 之前没有玩过此类东西,激动不已,于是自己也动手写了几行代码, 实现了CS子弹无限制,血量无限,阳光 ...

  9. SerialPort项目配置

    app的build.gradle下: apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultCon ...

  10. spark2.1源码分析3:spark-rpc如何实现将netty的Channel隐藏在inbox中

    class TransportServer bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Overri ...