在网络传输中,不仅要不把文字数据、视频数据和音频数据接收过来,还要对这些数据进行显示和保存,本章通过方案来介绍如何显示和存储多媒体文件。
8.1 文字显示方案
文字的接收显示相对简单,但是只能显示单调的字体文字的通信软件是很难吸引用户的,而聊天记录在通信软件中也是及其重要的,本节来介绍一下如何显示个性字体和保存聊天记录。
8.1.1 个性文字同步显示方案
在使用聊天软件进行聊天时,常常被那些绚丽的字体所吸引,那么如何才能实现个性文字同步显示呢?
首先,可以确定的是,要使用RichEdit控件,该控件可以显示不同的字体。然后,要把每行显示的字体都交给用户控制就可以了,这时就有了一个问题,因为RichEdit控件中并没有能单独设置每行字体的方法,可是如果自己继承一个类编写起来又很麻烦,这要怎么办呢?笔者采用一个简单的方法,那就是通过剪贴板来实现,因为在复制文本的同时会将该文本的字体信息一起复制过来,只要直接粘贴到RichEdit控件中就可以了。下面将创建一个可以同步显示个性文字的聊天室程序。运行程序如图8.1 、图8.2所示。

图8.1 个性字体同步显示(服务器端)

图8.2 个性字体同步显示(客户端)
服务器端程序设计如下:
实例位置:光盘\mr\8\8.1\8.1.1\01
(1)创建一个基于对话框的应用程序,向对话框中添加一个图像控件、两个静态文本控件、一个群组控件和一个按钮控件。
(2)向工程中导入一幅图片,打开Picture控件的属性窗口,在General选项卡中,在Type组合框中选择Bitmap,在Image组合框中选择IDB_BITMAP1,就可以用Picture控件将图片显示出来。对话框设计如图8.3 所示。

图8.3 对话框设计
(3)在应用程序的InitInstance方法中初始化套接字。
WSADATA wsd;
WSAStartup(MAKEWORD(2,2),&wsd);
(4)从CSocket类派生新类CClientSocket和CServerSocket,在头文件中引用对话框的头文件和afxsock.h头文件,并对对话框类进行前导声明。
(5)绑定套接字,并开始监听客户端。
void CServerDlg::OnOK()
{
this->UpdateData();
m_pSocket = new CServerSocket(this);
if (!m_pSocket->Create(70))
{
MessageBox("套接字创建失败");
delete m_pSocket;
m_pSocket = NULL;
return;
}
if (!m_pSocket->Listen())
MessageBox("监听失败");
}
(6)在对话框中添加AcceptConnect方法,用于接受客户端的连接。
void CServerDlg::AcceptConnect()
{
CClientSocket* socket = new CClientSocket(this);
//接受客户框的连接
if (m_pSocket->Accept(*socket))
m_socketlist.AddTail(socket);
else
delete socket;
}
(7)在对话框中添加ReceiveData方法,用于接收客户端传来的字体信息和数据,并将这些字体信息和数据发送到各个客户端。
void CServerDlg::ReceiveData(CClientSocket* socket)
{
if(font)
{
LOGFONT temp;
//接收传来的字体数据
int factdata = socket->Receive(&temp,sizeof(temp));
font = FALSE;
POSITION pos = m_socketlist.GetHeadPosition();
//将数据发送给每个客户端
while (pos!=NULL)
{
CClientSocket* socket = (CClientSocket*)m_socketlist.GetNext(pos);
if (socket != NULL)
socket->Send(&temp,sizeof(temp));
}
}
else
{
char bufferdata[BUFFERSIZE];
//接收客户端传来的数据
int result = socket->Receive(bufferdata,BUFFERSIZE);
bufferdata[result] = 0;
if(bufferdata[0] == '~')
{
font = TRUE;
POSITION pos = m_socketlist.GetHeadPosition();
//将数据发送给每个客户端
while (pos!=NULL)
{
CClientSocket* socket = (CClientSocket*)m_socketlist.GetNext(pos);
if (socket != NULL)
{
socket->Send(bufferdata,result);
}
Sleep(100);
}
}
else
{
POSITION pos = m_socketlist.GetHeadPosition();
//将数据发送给每个客户端
while (pos!=NULL)
{
CClientSocket* socket = (CClientSocket*)m_socketlist.GetNext(pos);
if (socket != NULL)
socket->Send(bufferdata,result);
}
}
}
}
客户端程序设计步骤如下:
实例位置:光盘\mr\8\8.1\8.1.1\02
(1)创建一个基于对话框的应用程序,向对话框中添加一个图像控件、2个静态文本控件、3个编辑框控件、2个RichEdit控件和3个按钮控件。
(2)向工程中导入一幅图片,用Picture控件将图片显示出来。对话框设计如图8.4 所示。

图8.4 对话框设计
(3)将1个RichEdit控件隐藏,在程序运行时使其不可见。
(4)从CSocket类中派生一个子类CMysocket。在头文件中引用Afxsock.h头文件,目的是使用CSocket类;引用主对话框的头文件,并对主对话框进行前导声明,因为在CMysocket类中需要定义主对话框类指针。
(5)在主对话框中定义一个CMysocket对象指针,创建套接字。
BOOL CClientDlg::OnInitDialog()
{
……//此处代码省略
pMysocket = new CMysocket(this); //创建套接字对象
if (!pMysocket->Create()) //创建套接字
{
delete pMysocket;
MessageBox("套接字创建失败.");
return false;
}
font = FALSE;
return TRUE;
}
(6)处理“连接”按钮的单击事件,连接服务器。
void CClientDlg::OnButtonjoin()
{
UpdateData(true);
CString servername = m_servername; //读取服务器名称
int port;
port = 70; //获取端口
if(!pMysocket->Connect(servername,port)) //连接服务器
{
MessageBox("连接服务器失败!");
return;
}
CString str;
str.Format("%s----->%s",m_name,"进入聊天室");
pMysocket->Send(str.GetBuffer(0),str.GetLength());
GetDlgItem(IDC_BUTTONJOIN)->EnableWindow(FALSE);
}
(7)处理“字体”按钮的单击事件,设置信息的字体。
void CClientDlg::OnButtonfont()
{
CFontDialog dlg; //创建字体对话框对象
if(dlg.DoModal()==IDOK)
{
m_Font.Detach();
dlg.GetCurrentFont(&logfont); //获取当前字体信息
}
}
(8)处理“发送”按钮的单击事件,把字体信息和数据发送到服务器中。
void CClientDlg::OnButtonsend()
{
char a[1]={'~'};
int res = pMysocket->Send(a,1);
if(res == SOCKET_ERROR)
{
CString str;
str.Format("发送数据失败,错误码:%d",WSAGetLastError());
MessageBox(str);
return;
}
Sleep(100);
int ret = pMysocket->Send(&logfont,sizeof(logfont));
if(ret == SOCKET_ERROR)
{
CString str;
str.Format("发送数据失败,错误码:%d",WSAGetLastError());
MessageBox(str);
return;
}
CString str,temp;
m_info.GetWindowText(str);
if(str.IsEmpty()|m_name.IsEmpty())
return;
temp.Format("%s说: %s",m_name,str);
pMysocket->Send(temp.GetBuffer(temp.GetLength()),temp.GetLength());
m_info.SetWindowText("");
m_info.SetFocus();
}
(9)添加ReceiveData成员方法,用于接收服务器传来的字体信息和数据,并根据字体信息设置显示的字体。
void CClientDlg::ReceiveData()
{
if(font)
{
LOGFONT temp;
//接收传来的字体数据
int factdata = pMysocket->Receive((LOGFONT*)&temp,sizeof(temp));
m_Font.Detach();
m_Font.CreateFontIndirect(&temp); //直接创建字体
m_RichFont.SetFont(&m_Font); //设置字体
font = FALSE;
}
else
{
char buffer[200];
//接收传来的数据
int factdata = pMysocket->Receive(buffer,200);
buffer[factdata] = 0;
if(buffer[0] == '~')
{
font = TRUE;
return;
}
else
{
CString str;
str.Format("%s",buffer);
m_RichFont.SetWindowText(str);
m_RichFont.SetSel(0,-1);
m_RichFont.Copy();
m_RichEdit.Paste();
}
}
}
8.1.2 聊天记录设计方案
对于聊天室程序来说聊天记录是非常重要的功能,而且实现起来并不复杂,在接收到信息以后将信息保存到文件中,当用户单击“聊天记录”按钮时在读取文件中的信息并显示出来。
下面就来创建一个带聊天记录功能的聊天室程序。运行程序如图8.5 、图8.6所示。

图8.5 局域网聊天室(服务器端)

图8.6 局域网聊天室(客户端)
服务器端程序设计如下:
实例位置:光盘\mr\8\8.1\8.1.2\01
(1)创建一个基于对话框的应用程序,向对话框中添加一个图像控件、两个静态文本控件、一个群组控件和一个按钮控件。
(2)向工程中导入一幅图片,打开图像控件的属性窗口,在General选项卡中,在Type组合框中选择Bitmap,在Image组合框中选择IDB_BITMAP1,就可以用图像控件将图片显示出来。对话框设计如图8.7 所示。

图8.7 对话框设计
(3)在应用程序的InitInstance方法中初始化套接字。
WSADATA wsd;
WSAStartup(MAKEWORD(2,2),&wsd);
(4)从CSocket类派生新类CClientSocket和CServerSocket,在头文件中引用对话框的头文件和afxsock.h头文件,并对对话框类进行前导声明。
(5)绑定套接字,并开始监听客户端。
void CServerDlg::OnOK()
{
this->UpdateData();
m_pSocket = new CServerSocket(this);
if (!m_pSocket->Create(70))
{
MessageBox("套接字创建失败");
delete m_pSocket;
m_pSocket = NULL;
return;
}
if (!m_pSocket->Listen())
MessageBox("监听失败");
}
(6)在对话框中添加AcceptConnect方法,用于接受客户端的连接。
void CServerDlg::AcceptConnect()
{
CClientSocket* socket = new CClientSocket(this);
//接受客户框的连接
if (m_pSocket->Accept(*socket))
m_socketlist.AddTail(socket);
else
delete socket;
}
(7)在对话框中添加ReceiveData方法,用于接收客户端传来的数据。
void CServerDlg::ReceiveData(CClientSocket* socket)
{
char bufferdata[BUFFERSIZE];
//接收客户端传来的数据
int result = socket->Receive(bufferdata,BUFFERSIZE);
bufferdata[result] = 0;
POSITION pos = m_socketlist.GetHeadPosition();
//将数据发送给每个客户端
while (pos!=NULL)
{
CClientSocket* socket = (CClientSocket*)m_socketlist.GetNext(pos);
if (socket != NULL)
socket->Send(bufferdata,result);
}
}
客户端程序设计步骤如下:
实例位置:光盘\mr\8\8.1\8.1.2\02
(1)创建一个基于对话框的应用程序,向对话框中添加一个图像控件、3个静态文本控件、3个编辑框控件、2个ListBox控件和3个按钮控件。
(2)向工程中导入一幅图片,并用Picture控件将图片显示出来。对话框设计如图8.8 所示。

图8.8 对话框设计
(3)从CSocket类中派生一个子类CMysocket。在头文件中引用Afxsock.h头文件,目的是使用CSocket类;引用主对话框的头文件,并对主对话框进行前导声明,因为在CMysocket类中需要定义主对话框类指针。
(4)在主对话框中定义一个CMysocket对象指针,创建套接字,并使对话框中聊天记录的部分隐藏。
BOOL CClientDlg::OnInitDialog()
{
……//此处代码省略
pMysocket = new CMysocket(this); //创建套接字对象
if (!pMysocket->Create()) //创建套接字
{
delete pMysocket;
MessageBox("套接字创建失败.");
return FALSE;
}
m_bExpand=FALSE;
CRect rcDlg, rcMarker;
GetWindowRect(rcDlg);
m_nExpandedHeight = rcDlg.Height();
GetDlgItem(IDC_STATIC1)->GetWindowRect(rcMarker);
m_nNormalHeight = (rcMarker.top - rcDlg.top);
Display();
::GetCurrentDirectory(256,buf); //获取程序根目录路径
strcat(buf,"\\聊天记录.txt");
return TRUE;
}
(5)处理“连接”按钮的单击事件,连接服务器。
void CClientDlg::OnButtonjoin()
{
UpdateData(true);
CString servername = m_servername; //读取服务器名称
int port;
port = 70; //获取端口
if (! pMysocket->Connect(servername,port)) //连接服务器
{
MessageBox("连接服务器失败!");
return;
}
CString str;
str.Format("%s----->%s",m_name,"进入聊天室");
int num = pMysocket->Send(str.GetBuffer(0),str.GetLength());
}
(6)处理“发送”按钮的单击事件,发送数据到服务器。
void CClientDlg::OnButtonsend()
{
CString str,temp;
m_info.GetWindowText(str);
if (str.IsEmpty()|m_name.IsEmpty())
return;
temp.Format("%s说: %s",m_name,str);
int num = pMysocket->Send(temp.GetBuffer(temp.GetLength()),temp.GetLength());
m_info.SetWindowText("");
m_info.SetFocus();
}
(7)添加ReceiveData成员方法,用于接收服务器传来的数据,并将聊天信息保存到文本文件中。
void CClientDlg::ReceiveData()
{
char buffer[200];
//接收传来的数据
int factdata = pMysocket->Receive(buffer,200);
buffer[factdata] = '\0';
CString str;
str.Format("%s",buffer);
int i = m_list.GetCount();
//将数据添加到列表框中
m_list.InsertString(m_list.GetCount(),str);
str += "\r\n~";
m_File.Open(buf,CFile::modeWrite);
m_File.SeekToEnd();
m_File.Write(str,str.GetLength());
m_File.Close();
}
(8)处理“聊天记录”按钮的单击事件,控制显示或隐藏聊天记录。
void CClientDlg::OnNote()
{
m_bExpand = !m_bExpand;
Display();
}
(9)添加Display方法,当显示聊天记录时将文本文件中的数据添加到列表控件中。
void CClientDlg::Display()
{
m_note.ResetContent();
CRect rcDlg;
GetWindowRect(rcDlg);
if(m_bExpand)
{
rcDlg.SetRect( rcDlg.left, rcDlg.top,
rcDlg.left + rcDlg.Width(),
rcDlg.top + m_nExpandedHeight);
}
else
{
rcDlg.SetRect(rcDlg.left, rcDlg.top,
rcDlg.left + rcDlg.Width(),
rcDlg.top + m_nNormalHeight);
}
MoveWindow(rcDlg);
if(m_bExpand)
{
m_File.Open(buf,CFile::modeRead);
DWORD size = m_File.GetLength();
char read[256];
m_File.Read(read,size);
CString str="";
for(int i=0;i<size;i++)
{
if(read[i]!='~')
{
str += read[i];
}
else
{
m_note.AddString(str);
str = "";
}
}
m_File.Close();
}
}






