程序设计实践
课题名称: 设 计 报 告
类qq聊天软件
课题概述
1.1课题目标和主要内容
采用vc6.0,c++语言利用Winsock函数编写聊天软件的服务器程序和客户端程序,
客户端可以和任意与它连接在同一服务器上的客户端交流。
1.2系统的主要功能
功能列表或功能框图,以及功能的简要说明。
服务器端功能:
1、 接受用户传送来的连接判断命令,并向用户发出响应命令。 2、 当有成员登陆,更新自身成员列表,并且同步更新客户端成员列表 3、 接受用户传送来的聊天信息,然后向链表中的用户转发。
客户端功能:
1、输入用户名,和服务器的连接。
2、接受来自服务器转发的消息及消息来源,并且进行显示。 3、点击在线成员图标会弹出发送消息窗口。 4、接受服务器消息,更新自身成员列表。
2. 系统设计
2.1 系统总体框架
应用Winsock,TCP/IP网络编程。
服务器端:
客户端:
2.2 系统详细设计
应用Winsock,TCP/IP网络编程。
CAboutDlg:自动生成的关于对话框的类,继承于CDialog类 CQQ_serverApp: CWinApp的共有继承类
client_info: 结构体,用来存储成员登陆情况,姓名,socket
CQQ_serverDlg: CDialog的共有继承类,包含服务器端的主要实现代码。 自定义消息格式:
“To:”:标识接受信息的客户端名称 “\r\n”表示结束 “From:”:标识发送此信息的客户端名称 “\r\n”表示结束
“List:”:服务器向客户端发送信息所包含的标识字段,不同客户端用“,”隔开,
“\r\n”表示结束
“Context:”:标识信息的主要内容 “\r\n.\r\n”表示结束
2.3 关键算法分析 服务器端:
算法1:CQQ_serverDlg::OnStartrun() //“开启服务器”的消息响应函数 {
//加载套接字库
WORD ver = MAKEWORD(2,0); //版本号2.0 WSADATA wsadata; …..
//创建基于TCP的套接字
socket_server = socket(AF_INET,SOCK_STREAM,NULL); //TCP/IP地址家族、流式套接字
….
//获取主机的ip地址 ……
//绑定端口号和ip
sockaddr_in addr; //sock地址结构体 addr.sin_family = AF_INET; //地址家族为TCP/IP addr.sin_port= htons(137); addr.sin_addr
= ip_addr;
//默认设置的端口号
“List:”:服务器向客户端发送信息所包含的标识字段,不同客户端用“,”隔开,
“\r\n”表示结束
“Context:”:标识信息的主要内容 “\r\n.\r\n”表示结束
2.3 关键算法分析
服务器端:
算法1:CQQ_serverDlg::OnStartrun() //“开启服务器”的消息响应函数 {
//加载套接字库 WORD ver = MAKEWORD(2,0); //版本号2.0 WSADATA wsadata;
…..
//创建基于TCP的套接字 socket_server = socket(AF_INET,SOCK_STREAM,NULL);
//TCP/IP地址家族、流式套接字
….
//获取主机的ip地址 …… //绑定端口号和ip sockaddr_in addr; //sock地址结构体 addr.sin_family = AF_INET; //地址家族为TCP/IP addr.sin_port= htons(137); addr.sin_addr = ip_addr; //默认设置的端口号
if( bind(socket_server,(sockaddr *)&addr,sizeof(sockaddr)) ) { } //监听 listen(socket_server,5); //最大监听数为5 //设置异步套接字 ifAfxMessageBox("绑定端口号和ip出错"); return;
(SOCKET_ERROR==WSAAsyncSelect(socket_server,this->m_hWnd,WM_SOCKET,FD_ACCEPT | FD_CLOSE | FD_READ) )
{ } //提示信息 AfxMessageBox("服务器启动成功"); GetDlgItem(IDC_STARTRUN)->EnableWindow(false); //禁用“开启服务AfxMessageBox("异步套接字设置出错"); //提示消息 return; 器”按钮
}
********************************************************************* ********************************************************************* 算法2://向列表框控件中添加成员
创建LVITEM结构,赋予图像和名字,以及相应的索引,调用m_memlist.InsertItem()进行添加。
void CQQ_serverDlg::AddListMem(CString name,int index)
{
}
******************************************************************** ********************************************************************* 算法三:
//对接收到的数据依据标识字段进行筛选
(说明:根据自己的消息格式构造进行消息的筛选)
CString CQQ_serverDlg::getKeyMsg(CString recv_msg,CString keyword)
{
int index_start = 0,index_end = 0; //标识消息整体的开始与结束 int index = 0;
LVITEM lvitem; memset(&lvitem,0,sizeof(LVITEM)); lvitem.mask = LVIF_IMAGE | LVIF_TEXT; lvitem.iItem = index; lvitem.pszText = name.GetBuffer(0); lvitem.iImage = index; m_memlist.InsertItem(&lvitem);
CString str_temp = ""; //存储消息主要内容:成员名 if("To" == keyword) { index_start = recv_msg.Find("To:"); index_end = recv_msg.Find("\r\n",index_start);
for(index = index_start+3;index
}
if("From" == keyword)
。。。。。。。。
} { } return str_temp; str_temp += recv_msg.GetAt(index);
******************************************************************** ********************************************************************* 算法四:
//向所有列表成员发送成员名信息,更新成员名列表
void CQQ_serverDlg::SendAllMem()
{
CString name_list = ""; int count = m_memlist.GetItemCount(); //获取在线成员数目
} for(int i=0;i
客户端:
客户端与服务器端在编写上有很多相似之处,相似函数处理这里不再做赘述。 算法一:
//双击成员是调用的响应函数,显示对话框
void CQQ_clientDlg::OnDblclkListmem(NMHDR* pNMHDR, LRESULT* pResult)
{
//获取用户双击选择的成员的位置
POSITION pos = m_listmem.GetFirstSelectedItemPosition(); if (pos == NULL) { } else { AfxMessageBox("No items were selected!\n"); //提示消息 return;
//获取列表成员的索引
int nItem = m_listmem.GetNextSelectedItem(pos);
//获取制定索引的成员名
to_name = m_listmem.GetItemText(nItem,0);
//显示发送信息对话框
} *pResult = 0; } m_dlg.DoModal();
2.4 其他
CAboutDlg:自动生成的关于对话框的类,继承于CDialog类
CQQ_serverApp: CWinApp的共有继承类
CQQ_serverDlg: CDialog的共有继承类,包含服务器端的主要实现代码
继承程序框架本身就有的类,极大辅助了程序的编写。
3. 程序运行结果分析
运行效果图:
第10页
第11页
连接错误时:
4. 总结
4.1课题的难点和关键点
1、对于socket网络编程需要学习理解,尤其需要从最基础的开始学习。
本程序中应用了winsock进行编写,需要对如send(),recv(),等函数了解,尤其要掌握其参数类型,这样才能在应用使得心应手。比如在这次编程中因为参数类型的原因出了好几个错误。
第12页
2.程序涉及到成员名及消息的处理,所以需要自定义消息格式才能解决,尽管处理起来非常简单,但是在自己想出思路前还是用了一些时间。
3.调试上因为有了第一次实验的基础,这一次相对要好得多,对于常规处理还是能够解决,但是有时还是会出现不知所措的情况。
4.2 本课题的评价
通过本次试验,基本掌握了MFC编程的知识及WinSock的基本知识,对于Winsock函数有了较好地掌握,能够熟练使用,并且对于窗口化程序中各种函数的调用、响应原理也有很好的理解。
不足:界面上需要更加美化、同时也可以增加消息记录、文件传输等功能。
4.3心得体会
这次编程可以说是第一次编写大程序,也认识到真正实践时是各种知识的综合使用,要求知识面更为全面。而且也学习了winsock编程的知识,对消息响应原理有了更进一步的理解。
同时相应的也是花费时间最多的程序,一方面是程序本身较之前要复杂一些,另一方面是需要学习新的知识。期间遇到很多问题但最终通过努力,通过查资料、上网解决了,还是挺高兴的。
但是也不得不看到程序还有很多需要完善的地方,编写的较为简陋,界面上需要更加美化、同时也可以增加消息记录、文件传输等功能,而这方面我将会在日后加以完善。
5. 参考文献
[1] 罗莉琴 詹祖桥 《Windows 网络编程》 人民邮电出版社 2011版
[2] 孙浩 《Visual C++ 范例大全》 机械工业出版社 2009版
第13页
程序设计实践
课题名称: 设 计 报 告
类qq聊天软件
课题概述
1.1课题目标和主要内容
采用vc6.0,c++语言利用Winsock函数编写聊天软件的服务器程序和客户端程序,
客户端可以和任意与它连接在同一服务器上的客户端交流。
1.2系统的主要功能
功能列表或功能框图,以及功能的简要说明。
服务器端功能:
1、 接受用户传送来的连接判断命令,并向用户发出响应命令。 2、 当有成员登陆,更新自身成员列表,并且同步更新客户端成员列表 3、 接受用户传送来的聊天信息,然后向链表中的用户转发。
客户端功能:
1、输入用户名,和服务器的连接。
2、接受来自服务器转发的消息及消息来源,并且进行显示。 3、点击在线成员图标会弹出发送消息窗口。 4、接受服务器消息,更新自身成员列表。
2. 系统设计
2.1 系统总体框架
应用Winsock,TCP/IP网络编程。
服务器端:
客户端:
2.2 系统详细设计
应用Winsock,TCP/IP网络编程。
CAboutDlg:自动生成的关于对话框的类,继承于CDialog类 CQQ_serverApp: CWinApp的共有继承类
client_info: 结构体,用来存储成员登陆情况,姓名,socket
CQQ_serverDlg: CDialog的共有继承类,包含服务器端的主要实现代码。 自定义消息格式:
“To:”:标识接受信息的客户端名称 “\r\n”表示结束 “From:”:标识发送此信息的客户端名称 “\r\n”表示结束
“List:”:服务器向客户端发送信息所包含的标识字段,不同客户端用“,”隔开,
“\r\n”表示结束
“Context:”:标识信息的主要内容 “\r\n.\r\n”表示结束
2.3 关键算法分析 服务器端:
算法1:CQQ_serverDlg::OnStartrun() //“开启服务器”的消息响应函数 {
//加载套接字库
WORD ver = MAKEWORD(2,0); //版本号2.0 WSADATA wsadata; …..
//创建基于TCP的套接字
socket_server = socket(AF_INET,SOCK_STREAM,NULL); //TCP/IP地址家族、流式套接字
….
//获取主机的ip地址 ……
//绑定端口号和ip
sockaddr_in addr; //sock地址结构体 addr.sin_family = AF_INET; //地址家族为TCP/IP addr.sin_port= htons(137); addr.sin_addr
= ip_addr;
//默认设置的端口号
“List:”:服务器向客户端发送信息所包含的标识字段,不同客户端用“,”隔开,
“\r\n”表示结束
“Context:”:标识信息的主要内容 “\r\n.\r\n”表示结束
2.3 关键算法分析
服务器端:
算法1:CQQ_serverDlg::OnStartrun() //“开启服务器”的消息响应函数 {
//加载套接字库 WORD ver = MAKEWORD(2,0); //版本号2.0 WSADATA wsadata;
…..
//创建基于TCP的套接字 socket_server = socket(AF_INET,SOCK_STREAM,NULL);
//TCP/IP地址家族、流式套接字
….
//获取主机的ip地址 …… //绑定端口号和ip sockaddr_in addr; //sock地址结构体 addr.sin_family = AF_INET; //地址家族为TCP/IP addr.sin_port= htons(137); addr.sin_addr = ip_addr; //默认设置的端口号
if( bind(socket_server,(sockaddr *)&addr,sizeof(sockaddr)) ) { } //监听 listen(socket_server,5); //最大监听数为5 //设置异步套接字 ifAfxMessageBox("绑定端口号和ip出错"); return;
(SOCKET_ERROR==WSAAsyncSelect(socket_server,this->m_hWnd,WM_SOCKET,FD_ACCEPT | FD_CLOSE | FD_READ) )
{ } //提示信息 AfxMessageBox("服务器启动成功"); GetDlgItem(IDC_STARTRUN)->EnableWindow(false); //禁用“开启服务AfxMessageBox("异步套接字设置出错"); //提示消息 return; 器”按钮
}
********************************************************************* ********************************************************************* 算法2://向列表框控件中添加成员
创建LVITEM结构,赋予图像和名字,以及相应的索引,调用m_memlist.InsertItem()进行添加。
void CQQ_serverDlg::AddListMem(CString name,int index)
{
}
******************************************************************** ********************************************************************* 算法三:
//对接收到的数据依据标识字段进行筛选
(说明:根据自己的消息格式构造进行消息的筛选)
CString CQQ_serverDlg::getKeyMsg(CString recv_msg,CString keyword)
{
int index_start = 0,index_end = 0; //标识消息整体的开始与结束 int index = 0;
LVITEM lvitem; memset(&lvitem,0,sizeof(LVITEM)); lvitem.mask = LVIF_IMAGE | LVIF_TEXT; lvitem.iItem = index; lvitem.pszText = name.GetBuffer(0); lvitem.iImage = index; m_memlist.InsertItem(&lvitem);
CString str_temp = ""; //存储消息主要内容:成员名 if("To" == keyword) { index_start = recv_msg.Find("To:"); index_end = recv_msg.Find("\r\n",index_start);
for(index = index_start+3;index
}
if("From" == keyword)
。。。。。。。。
} { } return str_temp; str_temp += recv_msg.GetAt(index);
******************************************************************** ********************************************************************* 算法四:
//向所有列表成员发送成员名信息,更新成员名列表
void CQQ_serverDlg::SendAllMem()
{
CString name_list = ""; int count = m_memlist.GetItemCount(); //获取在线成员数目
} for(int i=0;i
客户端:
客户端与服务器端在编写上有很多相似之处,相似函数处理这里不再做赘述。 算法一:
//双击成员是调用的响应函数,显示对话框
void CQQ_clientDlg::OnDblclkListmem(NMHDR* pNMHDR, LRESULT* pResult)
{
//获取用户双击选择的成员的位置
POSITION pos = m_listmem.GetFirstSelectedItemPosition(); if (pos == NULL) { } else { AfxMessageBox("No items were selected!\n"); //提示消息 return;
//获取列表成员的索引
int nItem = m_listmem.GetNextSelectedItem(pos);
//获取制定索引的成员名
to_name = m_listmem.GetItemText(nItem,0);
//显示发送信息对话框
} *pResult = 0; } m_dlg.DoModal();
2.4 其他
CAboutDlg:自动生成的关于对话框的类,继承于CDialog类
CQQ_serverApp: CWinApp的共有继承类
CQQ_serverDlg: CDialog的共有继承类,包含服务器端的主要实现代码
继承程序框架本身就有的类,极大辅助了程序的编写。
3. 程序运行结果分析
运行效果图:
第10页
第11页
连接错误时:
4. 总结
4.1课题的难点和关键点
1、对于socket网络编程需要学习理解,尤其需要从最基础的开始学习。
本程序中应用了winsock进行编写,需要对如send(),recv(),等函数了解,尤其要掌握其参数类型,这样才能在应用使得心应手。比如在这次编程中因为参数类型的原因出了好几个错误。
第12页
2.程序涉及到成员名及消息的处理,所以需要自定义消息格式才能解决,尽管处理起来非常简单,但是在自己想出思路前还是用了一些时间。
3.调试上因为有了第一次实验的基础,这一次相对要好得多,对于常规处理还是能够解决,但是有时还是会出现不知所措的情况。
4.2 本课题的评价
通过本次试验,基本掌握了MFC编程的知识及WinSock的基本知识,对于Winsock函数有了较好地掌握,能够熟练使用,并且对于窗口化程序中各种函数的调用、响应原理也有很好的理解。
不足:界面上需要更加美化、同时也可以增加消息记录、文件传输等功能。
4.3心得体会
这次编程可以说是第一次编写大程序,也认识到真正实践时是各种知识的综合使用,要求知识面更为全面。而且也学习了winsock编程的知识,对消息响应原理有了更进一步的理解。
同时相应的也是花费时间最多的程序,一方面是程序本身较之前要复杂一些,另一方面是需要学习新的知识。期间遇到很多问题但最终通过努力,通过查资料、上网解决了,还是挺高兴的。
但是也不得不看到程序还有很多需要完善的地方,编写的较为简陋,界面上需要更加美化、同时也可以增加消息记录、文件传输等功能,而这方面我将会在日后加以完善。
5. 参考文献
[1] 罗莉琴 詹祖桥 《Windows 网络编程》 人民邮电出版社 2011版
[2] 孙浩 《Visual C++ 范例大全》 机械工业出版社 2009版
第13页