VC++课程设计下了就能用Word格式.docx
《VC++课程设计下了就能用Word格式.docx》由会员分享,可在线阅读,更多相关《VC++课程设计下了就能用Word格式.docx(27页珍藏版)》请在冰豆网上搜索。
CSocket对象提供阻塞模式,这对于Carchive的同步操作是至关重要的。
阻塞函数(如Receive()、Send()、ReceiveFrom()、SendTo()和Accept())直到操作完成后才返回控制权。
三、总体设计
服务器端核心:
等待客户连接;
通知所有在线用户新登录用户信息;
中转消息;
当用户离线时通知所有在线用户。
客户端核心:
获得连接参数后与服务器建立连接;
发送/接收信息;
断开时通知服务器。
模块流程图:
四、详细设计
1.服务器端
(1)新建一个基于对话框的MFC项目,命名为ChatRoom,在接下来的“高级功能”选项卡选择“WindowsSockets”复选框。
(2)在对话框上按照表放置相应的控件。
ID
控件类型
标题
风格/变量
IDC_EDIT_INFO
EditBox
ReadOnly||Multiline||VerticalScroll
IDC_STATIC
GroupBox
在线用户
IDC_LIST_USER
ListBox
CListBoxm_UserList
IDOK
ButtonControl
退出
放置控件后的界面如图服务器端。
(3)添加一个新类,命名为CserverSocket,用于接收客户端的连接。
•ServerSocket.h中的定义如下:
#pragmaonce
#include"
ClientSocket.h"
//CServerSocket命令目标
classCServerSocket:
publicCSocket
{
public:
CPtrListconnectList;
//维护客户端连接的socket链表
CServerSocket();
virtual~CServerSocket();
virtualvoidOnAccept(intnErrorCode);
//服务器接收客户端的连接
};
•ServerSocket.cpp中的实现,这里只重写了OnAccept()函数:
voidCServerSocket:
:
OnAccept(intnErrorCode)
//TODO:
在此添加专用代码和/或调用基类
CClientSocket*clientSocket=newCClientSocket(&
connectList);
//创建socket用于接收客户端连接
Accept(*clientSocket);
//接收新用户连接
clientSocket->
m_parent=(CChatRoomDlg*):
AfxGetMainWnd();
//窗口指针赋值
connectList.AddTail(clientSocket);
//加入到链表尾部
CSocket:
OnAccept(nErrorCode);
}
(4)再添加一个新类,命名为CClientSocket,用于和客户端进行数据交换。
•ClientSocket.h中的定义:
ChatRoomDlg.h"
tagHeader.h"
//CClientSocket命令目标
classCClientSocket:
CStringm_strName;
//连接的用户名
CPtrList*clist;
//与服务器连接的socket链表指针,所有的socket共享
CChatRoomDlg*m_parent;
//维护窗口指针
CClientSocket(CPtrList*list);
//构造函数用于将链表共享
CClientSocket();
virtual~CClientSocket();
virtualvoidOnReceive(intnErrorCode);
//接收到消息的相关处理
virtualvoidOnClose(intnErrorCode);
•ClientSocket.cpp实现
//ClientSocket.cpp:
实现文件
//系统生成的代码略
stdafx.h"
ChatRoom.h"
.\clientsocket.h"
CClientSocket:
CClientSocket(CPtrList*list):
m_parent(NULL)
clist=list;
//链表指针赋值
•CClientSocket成员函数
voidCClientSocket:
OnReceive(intnErrorCode)
charbuff1[sizeof(Header)];
memset(buff1,0,sizeof(buff1));
Receive(buff1,sizeof(buff1));
Header*header=(Header*)buff1;
intlength=header->
len;
chartype=header->
type;
if(type==LOGIN_IO)//将接收到的信息是用户的登录或退出信息
{
charbuff[RECV_LEN];
memset(buff,0,sizeof(buff));
Receive(buff,length);
CTimetime=CTime:
GetCurrentTime();
//得到系统时间
CStringt=time.Format("
%Y-%m-%d%H:
%M:
%S"
);
//格式化成字符串
CEdit*p_Edit=(CEdit*):
AfxGetMainWnd()->
GetDlgItem(IDC_EDIT_INFO);
CStringstrTemp=t+"
"
+CString(buff)+"
进入聊天室...\r\n"
;
p_Edit->
ReplaceSel(strTemp);
//在界面上显示
m_strName=buff;
//用户名赋值
m_parent->
UpdateUser(this);
}
if(type==SEND_MESSAGE)//将要接收的信息是用户发送的信息
charbuf[RECV_LEN];
memset(buf,0,sizeof(buf));
Receive(buf,sizeof(buf));
//接收信息
CClientSocket*curr=NULL;
POSITIONpos=clist->
GetHeadPosition();
while(pos!
=NULL)//发送给每一个在线用户
{
curr=(CClientSocket*)clist->
GetNext(pos);
curr->
Send((char*)header,sizeof(Header));
Send(buf,sizeof(buf));
}
OnReceive(nErrorCode);
OnClose(intnErrorCode)
Find(this);
//找到要退出的Socket对象
if(pos!
=NULL)
clist->
RemoveAt(pos);
//移除
//得到时间等信息再在界面上显示
+this->
m_strName+"
离开聊天室\r\n"
//把退出用户从界面上移掉
this->
Close();
//Socket对象关闭
deletethis;
OnClose(nErrorCode);
(5)用到的头文件tagHeader.h是这样定义的(后面的客户端也会用到):
typedefstructtagHeader
chartype;
intlen;
}Header,*pHeader;
//发送的信息头
constintSERVER_PORT=9999;
//服务器监听端口
constintLOGIN_IO=1;
//登录或退出标记
constintSEND_MESSAGE=3;
//发送信息标记
constintRECV_LEN=256;
//接收信息长度
constintCLIENT_NUM=50;
//聊天室的人数上线
(6)下面是服务器的初始化代码:
BOOLCChatRoomDlg:
OnInitDialog()
//系统自动生成的代码略
在此添加额外的初始化代码
CServerSocket*m_pSocket;
m_pSocket=newCServerSocket;
if(!
m_pSocket->
Create(SERVER_PORT))//创建服务器的监听Socket
AfxMessageBox("
创建socket失败!
"
returnFALSE;
Listen())//监听
监听发生错误!
((CEdit*)GetDlgItem(IDC_EDIT_INFO))->
ReplaceSel("
服务器初始化成功...\r\n"
//界面显示
returnTRUE;
//除非设置了控件的焦点,否则返回TRUE
(7)用户登录或者退出时界面上的显示:
//用户的更新,包括用户登录和退出等
voidCChatRoomDlg:
UpdateUser(CClientSocket*pSocket)
CStringuser_name;
//用户名字符串
if(pSocket!
m_UserList.ResetContent();
//清空过时内容
CClientSocket*pSock=NULL;
POSITIONpos=pSocket->
clist->
//取得socket链表头
while(pos!
=NULL)//在界面上显示每一个在线用户
pSock=(CClientSocket*)pSocket->
//只要链表不空,依次取下一个位置
m_UserList.AddString(pSock->
m_strName);
//显示
user_name+=(pSock->
&
Headerhead;
//信息头
head.type=LOGIN_IO;
//本信息是用户登录或退出信息
head.len=user_name.GetLength();
POSITIONpo=pSocket->
while(po!
=NULL)//向其他的在线用户发送新用户登录信息
GetNext(po);
pSock->
Send((char*)&
head,sizeof(Header));
//发送信息头
Send((LPCTSTR)user_name,user_name.GetLength());
//发送新用户的名字
2.客户端
(1)添加一个基于对话框的MFC项目,命名为ChatClient,同样要在“高级功能”选项卡选择“WindowsSockets”复选框。
(2)在对话框上放置如表控件。
聊天信息
IDC_EDIT_LIST
Mutiline||VerticalScroll
CEditm_MessageList;
在线列表
IDC_EDIT_MESSAGE
CStringm_strMessage
ID_SEND
发送
DefaultButton
放置好控件的界面如图客户端所示。
注意:
去掉“退出”按钮缺省属性,给“发送”设置缺省属性。
(3)考虑到用户登录时需要添加诸如服务器地址,自己的用户名等信息,我们需要再加入一个对话框资源,命名为IDD_DIALOG_LOGIN,并为该对话框添加类CLoginDlg(基类是CDialog),然后再对话框上按照表放置以下控件。
登录
StaticText
昵称:
服务器IP:
IDC_EDIT_NAME
CStringm_strName
IDC_EDIT_SERVER
CStringm_strServer
放置控件后的界面如下图所示。
(4)在ChatClient项目中仍然需要一个Socket类CClientSocket来完成数据的发送和接受,具体封装和ChatRoom项目中的CClientSocket封装类似(也可以拷贝并加入到ChatClient项目中修改),下面是具体的封装:
classCChatClientDlg;
CChatClientDlg*chatDlg;
//窗口指针,便于在界面上显示信息
•ClientSocket.cpp中的实现:
//CClientSocket成员函数
{//根据type字段决定是接收信息或者是用户登录/退出消息
charbuf[sizeof(Header)];
//用于接收消息头的缓冲区
memset(buf,0,sizeof(buf));
Receive(buf,sizeof(buf));
//接收消息头
Header*header=(Header*)buf;
//强制转换成消息头结构
if(type==SEND_MESSAGE)//发送的是信息
chatDlg->
GetMessage();
if(type==LOGIN_IO)//是用户登录/退出消息
UpdateUser();
//处理用户登录或退出消息
(5)因为在两个对话框(即“登录”对话框和主聊天对话框)中都用到同一个Socket对象,所以有必要在CChatClientApp主程序中定义一个全局的客户端Socket对象,同时修改两个对话框的构造函数,具体如下:
•ClientSocket.h中对CClientSocket类的定义:
ChatClient.h"
ChatClientDlg.h"
•CLoginDlg构造函数的改写:
为了便于本地测试,程序默认服务器IP地址为本地IP地址。
//CLoginDlg对话框
IMPLEMENT_DYNAMIC(CLoginDlg,CDialog)
CLoginDlg:
CLoginDlg(CClientSocket*p_Socket,CWnd*pParent/*=NULL*/)
:
CDialog(CLoginDlg:
IDD,pParent)
m_strName(_T("
))
m_strServer(_T("
//获取本地IP地址并默认指示为服务器IP
{
charHostName[128];
structhostent*pHost;
CStringstr;
if(gethostname(HostName,128)==0)
pHost=gethostbyname(HostName);
for(inti=0;
pHost!
=NULL&
pHost->
h_addr_list[i]!
=NULL;
++i)
str=inet_ntoa(*(structin_addr*)pHost->
h_addr_list[i]);
m_pSocket=p_Socket;
m_strServer=str;
str.Empty();
•CChatClientDlg构造函数的改写:
//CChatClientDlg对话框
CChatClientDlg:
CChatClientDlg(CClientSocket*p_Socket,CWnd*pParent/*=NULL*/)
CDialog(CChatClientDlg:
m_strMessage(_T("
)