基于C/S 结构的IM即时通讯软件--下篇
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即时通讯软件--下篇的更多相关文章
- 基于C/S 结构的IM即时通讯软件--上篇
目的:实现类似QQ群聊的聊天室,可以看到好友列表及互相传送信息. 分析:可基于C/S结构实现即时通讯 1.创建基于对话框的MFC程序(支持windows套接字),并增加相应的类与结构体,完善对话框界面 ...
- 基于存储过程的MVC开源分页控件--LYB.NET.SPPager
摘要 现在基于ASP.NET MVC的分页控件我想大家都不陌生了,百度一下一大箩筐.其中有不少精品,陕北吴旗娃杨涛大哥做的分页控件MVCPager(http://www.webdiyer.com/)算 ...
- 基于存储过程的MVC开源分页控件
基于存储过程的MVC开源分页控件--LYB.NET.SPPager 摘要 现在基于ASP.NET MVC的分页控件我想大家都不陌生了,百度一下一大箩筐.其中有不少精品,陕北吴旗娃杨涛大哥做的分页控件M ...
- Web应用程序开发,基于Ajax技术的JavaScript树形控件
感谢http://www.cnblogs.com/dgrew/p/3181769.html#undefined 在Web应用程序开发领域,基于Ajax技术的JavaScript树形控件已经被广泛使用, ...
- FineUI 基于 ExtJS 的专业 ASP.NET 控件库
FineUI 基于 ExtJS 的专业 ASP.NET 控件库 http://www.fineui.com/
- 基于Bootstrap的JQuery TreeView树形控件,数据支持json字符串、list集合(MVC5)<二>
上篇博客给大家介绍了基于Bootstrap的JQuery TreeView树形控件,数据支持json字符串.list集合(MVC5)<一>, 其中的两种方式都显得有些冗余.接着上篇博客继续 ...
- 为什么要用hibernate 与基于数据库表结构的项目开发
最近开始学习hibernate,其实并不知道要学习什么,有什么用.后来问了一下同事,他就说快捷方便简单,很多事情不用自己做他会帮你做好,但是我觉得不应该是这样的,于是我就去搜了一下,就搜到了一篇帖子, ...
- 基于Qt的第三方库和控件
====================== 基于Qt的第三方库和控件 ====================== libQxt -------- http://dev.libqxt.o ...
- java开源即时通讯软件服务端openfire源码构建
java开源即时通讯软件服务端openfire源码构建 本文使用最新的openfire主干代码为例,讲解了如何搭建一个openfire开源开发环境,正在实现自己写java聊天软件: 编译环境搭建 调试 ...
随机推荐
- vim-go 安装
vim-go 安装 https://studygolang.com/articles/3229
- 明令禁止下,哪些APP在违规获取用户信息?
2019年4月28日消息 移动互联网时代各大APP大行其道,用户为了更便捷地享受互联网服务,常常需要让渡部分个人信息.在信息获取不透明的情况下,不少APP运营企业出现过度获取乃至违规获取用户信息的情 ...
- docker(基础篇)
http://naotu.baidu.com/file/f02773930afb2d3d9e71621249099d31 centos7安装 https://yq.aliyun.com/articl ...
- 多个字符串有相同的hashcode(没见到大于8的时候转成红黑树)
public static void main(String[] a){ byte[] b1 = {33 , 123 ,124}; byte[] b2 = {33 , 124 , 93}; byte[ ...
- magento 1.9 上传后图片前后台无法正常显示
1.上传后图片不显示,设置 允许 flash 2.保证php 执行是内存大小至少为为128M,多种方式设置,这里以init_set为例子,在index.php 加入下面一行代码,根据情况而定 ini_ ...
- Python3源码学习-requests
源码:https://github.com/small99/AutoLink 本地安装包 version.txt记录版本号. 通过requests GET最新git上版本号. import codec ...
- KiCad 一款强大的 BOM 和 装配图生成插件
KiCad 一款强大的 BOM 和 装配图生成插件 可以生成 BOM 和在线的图形. https://github.com/openscopeproject/InteractiveHtmlBom In ...
- 简简单单美化你Mac os x的终端配色
Mac OS x虽然是以图形界面出名的,但是作为一个类Unix系统,还是离不开终端(shell)的,尤其是对于开发人员来说,Mac OS x默认状态的终端给人的感觉总是不那么舒服,所以很有必要对它进行 ...
- 安卓APP环境搭建
https://www.cocos.com/creator 下载2.0.8 安装的时候选择原生环境 下载SDK:http://tools.android-studio.org/index.php/sd ...
- 工控随笔_09_西门子_S7-200 Smart与V20 USS通信USS_RPM_R利用轮询的方式通讯异常
前两天处理过一个故障,是S7-200 Smart与V20的USS通讯,设备厂家在程序里面利 用USS_RPM _R程序循环轮询5个V20设备读取频率和电流值等信息. 图 USS_RPM_R读取信息 上 ...