VC++课程设计报告书
姓名:123
学号:123
专业:网络工程
前言
当今社会,随着科学技术的飞跃发展,计算机已经进入了千家万户,互联网上的各种聊天工具也层出不穷。比如大家所熟知的腾讯QQ、微软的MSN、移动的Fetion等,都是做的比较成功的实时聊天工具。当我们沉浸在这些聊天工具花花绿绿的界面和与好友畅聊的时候,殊不知,界面之下程序开发的难度。
本程序是我和456同学合作编成,构思期间花费了很多心血,自认为考虑还算周到,可是到了编成过程中就遇到了很多困难,例如:服务器无法登录,数据库加载失败,聊天信息无法发送等现象。经过老师指点才知道一些细节没有考虑到,经过修改多次终于成功。
在程序中运用到的知识有文件操作,注册表,动态链接库,多线程编程,数据库的应用技术,网络套接字的编程。
编者
2013年7月7日
一、题目及需求 ……………………………………………………………………………… 4
二、需求分析 ………………………………………………………………………………… 4
2.1 设计目的 ……………………………………………………………………………... 4
2.2 设计要求 ……………………………………………………………………………... 4
2.3 程序功能 ……………………………………………………………………………… 4
三、实验流程图 ……………………………………………………………………………… 5
四、程序代码 ………………………………………………………………………………… 7
五、总结 ……………………………………………………………………………………… 13
六、参考文献 ………………………………………………………………………………… 13
一、题目及需求
客户端能够登录服务器,通过服务器向客户发消息,可显示好友信息,选择向好友发送消息。
服务器设有数据库,纪录客户的账号密码IP地址端口号等信息。
二、需求分析
2.1 设计目的
使用vc++ 6.0平台实现网络应用编程,通过实践复习巩固课堂所学的知识。
2.2 设计要求
采用客户/服务器模式,分为客户端程序和服务器程序。客户端需登录服务器,并能够注册账号,写入服务器数据库。服务器支持多个用户同时在线聊天,在客户之间充当客户信息中心和转发中心
2.3 程序功能
2.3.1 聊天服务器主要功能要在待定端口等待聊天客户连接请求,如果是注册则要更新数据库;是登录则检测是否有此用户存在,不存在返回给此客户端错误。
2.3.2 服务器接收来自客户端的信息,检测对应ip和与服务器建立连接的socketIp相同的,通过此向目的客户端发送信息。
2.3.3 如果客户端有上线或者下线,那么就更新用户信息数据库文件,并向所有在线用户发送新在线用户表。
三、实验流程图
图3-1 客户端流程图
图 3-2 服务器端流程图
四、程序代码
以下是程序的代码部分:
客户端:
发送:
void CChatDlg::OnSend()
{
// TODO: Add your control notification handler code here
int ilen;
int isent;
UpdateData(true);
if(m_msg!="")
{
ilen=m_msg.GetLength();
isent=m_socket.Send(LPCTSTR(m_msg),ilen);//发送数据
if(isent==SOCKET_ERROR) //发送失败
{
MessageBox("链接失败,请重试!");
}
else
{
m_recmsg+="客户机:"+m_msg;
m_recmsg+="\r\n";
UpdateData(false);
}
}
}
屏幕刷新:
void CChatDlg::RefreshScreen()
{
UpdateData(0);
}
连接:
void CChatDlg::OnConnect()
{
// TODO: Add your control notification handler code here
if(!AfxSocketInit()) //初始化
{
AfxMessageBox("IDC_SOCKETS_INIT_FALLED");
return;
}
GetDlgItemText(IDC_IPADDRESS1,m_ipstr);
m_socket.m_hSocket=INVALID_SOCKET;
UpdateData(true);
bool flag=m_socket.Create(); //创建套接字
if(!flag)
{
AfxMessageBox("SOCKETS ERROR");
return;
}
m_socket.Connect(m_ipstr,m_port); //进行连接
}
接收消息:
void Mysocket::OnReceive(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
//获取对话框指针
CChatApp*pApp=(CChatApp*)AfxGetApp();
CChatDlg*pDlg=(CChatDlg*)pApp->m_pMainWnd;
//往编辑框中插入消息
char *pbuf=new char[4096];
int ibufsize=4096;
int ircvd;
CString strrecvd;
//接收数据
ircvd=Receive(pbuf,ibufsize);
if(ircvd==SOCKET_ERROR)
{
pDlg->MessageBox("SOCKET_ERROR");
}
else
{
pbuf[ircvd]=NULL;
pDlg->m_recmsg+="服务器:";
pDlg->m_recmsg+=pbuf;
pDlg->m_recmsg+="\r\n";
pDlg->RefreshScreen();
}
delete pbuf;
CAsyncSocket::OnReceive(nErrorCode);
}
发送消息:
void Mysocket::OnSend(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
CAsyncSocket::OnSend(nErrorCode);
}
连接:
void Mysocket::OnConnect(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class获取对话框指针
CChatApp*pApp=(CChatApp*)AfxGetApp(); //得到主框架句柄 应用对象
CChatDlg*pDlg=(CChatDlg*)pApp->m_pMainWnd; //得到句柄
int iResult=nErrorCode;
CString buffer;
int namelen;
if(iResult!=0) //连接失败
{
AfxMessageBox("链接服务器失败。");
//buffer.Format("链接服务器失败。\r\n");
pDlg->m_recmsg+=buffer; //消息
}
else
{
namelen=sizeof(sockaddr_in);
buffer.Format ("成功链接服务器%s:%d.\r\n",pDlg->m_ipstr,pDlg->m_port);
pDlg->m_recmsg+=buffer;
pDlg->GetDlgItem(IDC_SEND)->EnableWindow(true);
}
pDlg->RefreshScreen(); //刷新窗口
CAsyncSocket::OnConnect(nErrorCode);
}
服务器端:
侦听:
void CServerDlg::OnListen()
{
// TODO: Add your control notification handler code here
if(!AfxSocketInit())
{
AfxMessageBox("IDP_SOCKETS_INIT_FAILED");
return;
}
UINT m_port = 8888;
bool flag=m_serversocket.Create(m_port);
//创建套接字
if(!flag)
{
AfxMessageBox("SOCKET ERROR");
return;
}
flag=m_serversocket.Listen(1); //进行侦听
if(!flag)
{
AfxMessageBox("SOCKET ERROR");
return;
}
SetDlgItemText(IDC_LISTEN,"正在监听"); //按钮上显示文字
}
接收消息:
void MyServerSocket::OnAccept(int nErrorCode)
{
MySocket *psocket = new MySocket();
if(Accept(*psocket))
{
psocket->AsyncSelect(FD_READ);
m_socket[i++ %10] = psocket;
record_socket[i++%10] = psocket->m_hSocket;
psocket->GetPeerName(ip_address[i++%10], ip_port[i++%10]);
}
else
{
delete psocket;
}
CAsyncSocket::OnAccept(nErrorCode);
}
接收:
void MySocket::OnReceive(int nErrorCode)
{
int receive_ret;
Client_into m_database;
CString name_cstring;
CString password_cstring ;
char if_register[2];
receive_ret = Receive(if_register, 2);
if( receive_ret == SOCKET_ERROR)
{
AfxMessageBox("Reiceive error");
exit(-1);
}
if(if_register[0] = '0')
{
char name_buf[20];
receive_ret = Receive(name_buf, 20);
if( receive_ret == SOCKET_ERROR)
{
AfxMessageBox("Reiceive error");
exit(-1);
}
char password_buf[20];
receive_ret = Receive(name_buf, 20);
if( receive_ret == SOCKET_ERROR)
{
AfxMessageBox("Reiceive error");
exit(-1);
}
name_cstring = name_buf;
password_cstring = password_buf;
if(m_database.IsOpen())
{
m_database.Close();
}
m_database.m_strFilter.Format("昵称 = '%s'", name_cstring);
if(m_database.Open(CRecordset::snapshot, NULL, CRecordset::none == 0))
{
char send_value[2] = {'0'};
send(record_socket[i - 1], send_value, 2, 0);
exit(-1);
}
else
{
if(m_database.Open())
{
m_database.Close();
}
m_database.m_strFilter.Format("密码 = %s ", password_cstring);
if(m_database.Open(CRecordset::snapshot, NULL, CRecordset::none))
{
char send_value[2] = {"0"};
send(record_socket[i - 1], send_value, 2, 0);
exit(-1);
}
}
}
else
{
m_database.m_column1 = name_cstring;
m_database.m_column2 = password_cstring;
m_database.m_column3 = (long) ip_port[ i - 1];
m_database.m_IP__ = ip_address[i - 1];
}
// if(! getpeername(
CAsyncSocket::OnReceive(nErrorCode);
}
五、总结
实验中,客户端本来利用的底层套接字进行的连接服务器的工作,不过此套接字和MFC封装的不是匹配的。又重新改正的。还有发送用户名和密码等信息是多次发送的,不过有TCP接受与发送之间的速度差距大,修改成了一次发送用户名和密码。不足,程序没有完成全部功能,由于考试临近加上本人技术有限,在短时间内完成全部不太可能。程序的不足,在匹配目的IP和昵称完全可以使用HASH表,或者树等一些数据结构,循环遍历匹配效率低下。
六、参考文献
【1】Visual C++从入门到实践 清华大学出版社 2009
【2】从零开始学Visual C++ 电子工业出版社 2011
【3】SQL SERVER数据库管理与应用 清华大学出版社 2011