MFC写的在线客服

来源:互联网 发布:淘宝客户手机号 编辑:程序博客网 时间:2024/04/29 07:10

    本文是对为期两个礼拜的实习做一个简单的总结。

    本次实习的主要内容是用MFC写一个在线客服,类似于即时聊天程序,要用Socket通信和数据库,整个软件采用C/S模式,现在先对软件做一个简单的介绍。


首先,数据库部分我们用ODBC实现,配置方法非常简单,我们用的SQL2000具体方法我就不在这里写了。软件分为服务端和客户端两部分,服务端用来连接数据库和为客户转发消息,客户端则是发送和接消息的另一部分,连接客户端和服务端的通道就是Socket,下面具体介绍每一部分的实现。


一.MFC窗体应用程序的两个初始化


   1.通过F10跟踪我们会发现MFC每一个窗体都是一个类得实例化,定义一个窗口先要引入窗体的类,然后定义这个类得对象,在窗体对象的众多方法中有个DoModal()方法,当程序执行这一句的时候新的窗体就是现实出来。


   2.在主窗体中有一个初始化函数,窗体的初始化工作从这里开始BOOL CServerDlg::OnInitDialog(),用户的初始化工作都要在这里进行

 

   3.其他的就是各个控件的点击事件了,没有什么特别的

 

二.Socket通信过程

 

   1.MFC下面Socket大概有两种做法,第一种是WIN API函数来做,但是感觉比较复杂,我们采用第二种就是用MFC的一个类CSocket类来做,过程如下:首先CSocket的头文件是#include <afxsock.h>。socket通信分为两部分,接收和发送,在发送的时候必须指定IP和端口号,而在接收的时候则只需要指定端口

 

unsigned int StartServer(LPVOID lParam)//接收部分

{

if (!AfxSocketInit())//初始化环境

{

AfxMessageBox("初始化错误!");

return 1;

}

m_exit = false;

CDialog *aDlg = (CDialog *)lParam;

 

int nPort=123;//端口号

//socket------------------------------------------------

CSocket aSocket, serverSocket;

if (!aSocket.Socket())//建立通信

{

char szError[256] = {0};

sprintf(szError, "Create Faild: %d", GetLastError());

AfxMessageBox(szError);

return 1; 

}

BOOL bOptVal = TRUE;

int bOptLen = sizeof(BOOL);

aSocket.SetSockOpt(SO_REUSEADDR, (void *)&bOptVal, bOptLen, SOL_SOCKET);

if (!aSocket.Bind(nPort))//绑定端口

{

char szError[256] = {0};

sprintf(szError, "Bind Faild: %d", GetLastError());

AfxMessageBox(szError);

return 1; 

}

if(!aSocket.Listen(10))//监听

{

char szError[256] = {0};

sprintf(szError, "Listen Faild: %d", GetLastError());

AfxMessageBox(szError);

return 1;

}

while(!m_exit)//循环等待

{

if(!aSocket.Accept(serverSocket))

{

continue;

}

else

{

char szOutMsg[2048] = {0};

serverSocket.Receive(szRecvMsg, 2048);//szRecvMsg接收传递的消息

 

serverSocket.Close();

}

}

aSocket.Close();

 

return 0;

}

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////

//发送部分

 

void CClientDlg::OnButton_Send() //发送按钮

{

// TODO: Add your control notification handler code here

 

CSocket aSocket;

 

if(!aSocket.Create())

{

char szMsg[1024] = {0};

sprintf(szMsg, "create faild: %d", aSocket.GetLastError());

AfxMessageBox(szMsg);

return;

}

int nPort = atoi(strPort);

if(aSocket.Connect(strIP, nPort))//通过IP和端口号进行连接

{

aSocket.Send(strText, strText.GetLength());//发送strText

}

else

{

sprintf(szMsg, "create faild: %d", aSocket.GetLastError());

AfxMessageBox(szMsg);

}

 

aSocket.Close();

}

 

 

以上就是socket的基本实现,通过对象的两个方法aSocket.Send()和aSocket.Receive()实现发送和接收

 

  2.第二个重要问题,多线程的问题,因为作为在线客服,服务端没接到一个用户发送的消息就要建立一次通信,如果采用单线程就很难实现多个用户同时访问服务端,会出现异常退出或程序卡死,所以最好的解决方法就是多线程,服务端在监听的时候,每接到一个用户信息就开启一个服务子线程,具体做法就是在上面StartServer(LPVOID lParam)的循环等待中加入:

 

SOCKET   hSocket=NULL; 

serverSocket.FromHandle(hSocket); //获得句柄 

AfxBeginThread(Listened,(LPVOID)&serverSocket,THREAD_PRIORITY_NORMAL);//开启线程

 

 

线程的执行部分是

 

UINT   Listened   (LPVOID   pParam) 

{

       //消息的处理过程

}

 

 

这样当执行到开启线程的时候,主线程会创建一个子线程执行Listened  ()函数,至于里面传参,这个函数没有用到,但是服务的主线程用到了,具体还看我的源代码http://download.csdn.net/source/3111417

 

如果希望主线程给子线程传参的话,我的服务监听子线程是一个比较好的例子。

在按钮的点击事件中加入如下代码:

pThread = new CWinThread(StartServer,(LPVOID)this);

        pThread->CreateThread(CREATE_SUSPENDED);

        pThread->ResumeThread();

     这样点击该按钮会启动一个线程去执行StartServer函数,需要注意的是new CWinThread(StartServer, (LPVOID)this);中第二个参数this,是把本主线程中的this指针传给子线程了,那么在子线程中就可以直接调用主线程的资源,比如:

unsigned int StartServer(LPVOID lParam)

{

      CDialog*aDlg = (CDialog *)lParam;//指针的类型转换

      //...

      aDlg->SetDlgItemText(IDC_EDIT5,strText);//给主线程中的文本框赋值

}


通过这种方式可以达到传参的效果.

 

  3.第三个问题是界面美化,我直接用的外部的界面库,通过导入外部界面库直接替换掉MFC本身的界面,不用修改程序本身的代码,直接就能达到很好的美化效果,具体做法如下,下载SKIN++的安装包,http://download.csdn.net/source/3106012

 

MFC下的使用方法:

 

1、在工程文件中,如stdafx.h中包含SkinPPWTL.h文件:#include “SkinPPWTL.h”;

 

2、拷贝下列文件到当前工程目录下

 

SkinPPWTL.h、SkinPPWTL.lib、SkinPPWTL.dll、Royale.ssk (皮肤文件);

 

3、在应用程序入口处,添加Skin++的加载加载皮肤文件函数。

 

 BOOL CTestSkinPPDialogApp::InitInstance(){

 

skinppLoadSkin(_T("Royale.ssk"));

 

 

}

 

4、在VC++中点击 工程->设置->连接选卡->对象/库模块 中输入SkinPPWTL.lib

 

5、编译运行,调用成功。

 

 

以上就是整个工程用到的比较关键的技术要点,还有一些小的东西,比如传参,还有一些系统调用就不在这里写了。下面说一点这次实习的感受吧,这次带我们实习的单位是武汉易思博公司,他们的规模这不重要,重要的是他们在教我们软件开发的流程时我觉得这才是一个软件开发的正规步骤,感触最深的是各种文档和进度汇报机制,而我们本科学的这点编程的东西恐怕也就只能做一个代码写手。在做这个软件的过程中,因为没有一个明确的构架,我们的代码上了规模以后就变得特别的乱,这一点严重限制了开发的速度,我想着应该就像现在盖楼一样,采用框架式结构,一开始搭框架,框架起来了只要往上填砖就可以了,所以一个好的构架对于一个软件产品的作用我也算初有体会。

      还有一点,在实习的尾声对方公司对我们做的东西进行评比,据杨经理说我们的软件做的是最好的,但是在最后评比演示上,我准备的不够充分,没有演示好,结构屈居第三,他说 你的技术绝对是没问题,但是准备还是不够充分。看了别的组的作品虽然都有亮点,但是从服务器信息实时更新和多线程编程上面,没有一个组能和我的作品相比,这次失败给我的启示是,无论做什么事,无论有多么大的把握,一定要准备到万全的地步才能放手一搏,不做冒险主义

原创粉丝点击