// ChatServer1.h: interface for the CChatServer class. // // #if !defined(AFX_CHATSERVER1_H__F6365C1E_C996_4A39_A4BE_505DA39112AC__INCLUDED_) #define AFX_CHATSERVER1_H__F6365C1E_C996_4A39_A4BE_505DA39112AC__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include <afxtempl.h> #include "IOCPServer.h" #include "servercommand.h" class CChatServer : public CIOCPServer { public: CChatServer(); virtual ~CChatServer(); protected: virtual BOOL ClientExit(LPCLIENTCONTEXT lpContext); virtual void ProcessReceiveData(LPCLIENTCONTEXT lpContext, CBuffer &buffer); //call back void UserMessageFunc(USERINFO &userSrc,USERINFO &userDest, CString strSpeak); void LoginFunc(USERINFO &user); private: //保存了用户和用户数据结构的映射 MAPUSER m_mapUsers; //负责创建字符串等XML命令 CServerCommand<CChatServer> m_cmd; //合法指令 //这个互斥变量主要是为了防止线程同时修改mapuser数据结构的!! CRITICAL_SECTION m_cs; //互斥变量 }; #endif // !defined(AFX_CHATSERVER1_H__F6365C1E_C996_4A39_A4BE_505DA39112AC__INCLUDED_) // ChatServer1.cpp: implementation of the CChatServer class. // // #include "stdafx.h" #include "ChatServer.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // // Construction/Destruction // CChatServer::CChatServer() { m_cmd.SetType(this); //设置登陆后的响应函数为LoginFunc m_cmd.SetLoginFunc(LoginFunc); //设置消息处理函数为UserMessageFunc m_cmd.SetUserMessageFunc(UserMessageFunc); //初始化临界区 InitializeCriticalSection(&m_cs); } CChatServer::~CChatServer() { //析构函数中删除临界区 DeleteCriticalSection(&m_cs); } //对退出的客户的预处理 BOOL CChatServer::ClientExit(LPCLIENTCONTEXT lpContext) { CoInitialize(NULL); LPUSERINFO lpUser; //如果首先查找该用户 //m_mapUsers保存了用户和用户的数据结构的映射 if ( !m_mapUsers.Lookup((LONG)lpContext, lpUser) ) { //找不到该用户,说明已经用户已经退出了 CoUninitialize(); return TRUE; } //如果找到该用户了,则首先在mapuser数据结构中删除之 EnterCriticalSection(&m_cs); m_mapUsers.RemoveKey((LONG)lpContext); LeaveCriticalSection(&m_cs); //发送退出的用户名称给其他用户 //创建一个用户离开的字符串 CString strCmd = m_cmd.CreateUserLogout(*lpUser); //在服务器端输出“ *** Leave ” printf("%s LEAVE/n", lpUser->m_name); delete lpUser; POSITION pos = m_mapUsers.GetStartPosition(); for ( ; pos != NULL; ) { LPUSERINFO lpUser; LONG pContext; //pos指向下一个用户,获得用户的属性到上面创建的lpUser和pContext中 m_mapUsers.GetNextAssoc(pos, pContext, lpUser); //在pContext中发送前面创建好的用户离开的字符串strCmd //由于是oicpserver的子类,因此可以直接调用父类的方法Send Send((LPCLIENTCONTEXT)pContext, strCmd); } CoUninitialize(); return FALSE; } void CChatServer::ProcessReceiveData(LPCLIENTCONTEXT lpContext, CBuffer &buffer) { /* Initializes the COM library on the current thread and identifies the concurrency model as single-thread apartment (STA). Applications must initialize the COM library before they can call COM library functions other than CoGetMalloc and memory allocation functions. 他的参数 must be NULL. */ CoInitialize(NULL); try { //循环取得有效指令 CString strXML; while ( m_cmd.HasNetPacket(buffer, strXML) ) { if ( !m_cmd.DoProcessNetPacket((LONG)lpContext, strXML) ) { printf("Error xml format: %s/n", (LPCSTR)strXML); } } } catch(_com_error &e) { printf("%s", (char *)e.Description()); } /* Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other resources that the thread maintains, and forces all RPC connections on the thread to close. */ CoUninitialize(); } void CChatServer::LoginFunc(USERINFO &user) { //用户登陆 //将用户信息发送给用户,以及将服务器上用户列表发送给用户 sprintf(user.m_ip, ((LPCLIENTCONTEXT)(user.m_id))->m_ip); user.m_nPort = ((LPCLIENTCONTEXT)(user.m_id))->m_nPort; //创建一个用户登录成功的字符串 CString strCmd = m_cmd.CreateLoginSuccess(user, &m_mapUsers); //在用户的mid发送出去也就是刚登陆进来的那个user的lpcontext Send((LPCLIENTCONTEXT)user.m_id, strCmd); //将新用户发送所有他用户 //首先创建一个表示新用户的字符串,根据user创建 strCmd = m_cmd.CreateNewUser(user); //遍历整个的map在除不是user的mid的所有mid中发送出去 POSITION pos = m_mapUsers.GetStartPosition(); for ( ; pos != NULL; ) { LPUSERINFO lpUser; LONG lpContext; m_mapUsers.GetNextAssoc(pos, lpContext, lpUser); if ( user.m_id != (DWORD)lpContext ) { Send((LPCLIENTCONTEXT)lpContext, strCmd); } } //添加到列表 //把当前新加入的这个用户加入到mapuser中 EnterCriticalSection(&m_cs); m_mapUsers[user.m_id] = &user; LeaveCriticalSection(&m_cs); } //发送某用户的发言其他用户:区别私聊 void CChatServer::UserMessageFunc(USERINFO &userSrc,USERINFO &userDest,CString strSpeak) { if ( userDest.m_id == 0 ) // = 0表示是发送给所有用户 { //创建一个普通的用户消息 CString strCmd = m_cmd.CreateUserMessage(userSrc, userDest, strSpeak); //发送给所有用户 POSITION pos = m_mapUsers.GetStartPosition(); for ( ; pos != NULL; ) { LPUSERINFO lpUser; LONG lpContext; m_mapUsers.GetNextAssoc(pos, lpContext, lpUser); Send((LPCLIENTCONTEXT)lpContext, strCmd); } } else { //发送给单一用户:私聊 这里需要发送给目的用户和起始用户 CString strCmd = m_cmd.CreateUserMessage(userSrc, *m_mapUsers[userDest.m_id], strSpeak); Send((LPCLIENTCONTEXT)userDest.m_id, strCmd); //如果不是发送给自己的,则还要发送给自己 //即,私聊自己也能看见 if ( userDest.m_id != userSrc.m_id ) { Send((LPCLIENTCONTEXT)userSrc.m_id, strCmd); } } } http://veniriver.blog.sohu.com/33165735.html