65.9K
CodeProject 正在变化。 阅读更多。
Home

Barry的聊天系统

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (8投票s)

2002 年 11 月 25 日

5分钟阅读

viewsIcon

143532

downloadIcon

7515

Barry的聊天系统

Sample Image - BarrysChatSystem.gif

引言

本文演示了创建聊天系统的技术。本文包含一个聊天服务器和一个聊天客户端,它们执行不同的任务,不能互换使用。客户端和服务器都使用 Windows 套接字来连接和发送文本行。您可以在本地计算机、私有局域网或网络上使用服务器和客户端进行聊天。

注意:我无法访问局域网或网络,因此此软件仅在我本地机器上的“本地循环”模式下进行了测试。任何在网络上测试此软件的用户都可以通过电子邮件将测试结果发送给我()。

如何使用

准备使用 ChatServer & ChatClient

  • 下载 ChatServer & ChatClient 源代码
  • 解压 ChatServer & ChatClient 源代码
  • 启动 Visual Studio
  • 构建 ChatServer & ChatClient 可执行文件
  • 退出 Visual Studio

要在您的计算机上使用“本地循环”模式的 ChatServerChatClient,请执行以下操作:

启动 ChatServer

  • 启动 ChatServer(可执行文件位于 ChatServer\Debug 文件夹)
  • 端口号保持为 0,然后单击“确定”(注意:如果您更改端口号,连接 ChatClient 时必须使用相同的端口号)
  • 端口号可以介于 0 到 1499 之间(这在端口号被服务器系统上的另一个应用程序占用时非常有用)
  • 注意:即使输入的端口号是 0,实际端口号也是 1500 + x(x 是您输入的端口号,本例中为 0)

启动 ChatClient

  1. 启动 ChatClient(可执行文件位于 ChatClient\Debug 文件夹)
  2. 从菜单中选择 *Connection->Connect*(或单击 *Connect* 按钮)
  3. 将显示登录表单
  4. 在服务器框中键入 127.0.0.1
  5. 在昵称框中键入任何名称(每次启动 ChatClient 时都应使用不同的名称)
  6. 在端口号框中键入 0(请记住,我们启动 ChatServer 时端口号为 0)
  7. 点击“确定”
  8. 重复以上步骤 1 至 7,启动另一个 ChatClient,但使用不同的昵称。您可以根据可用内存启动任意数量的 ChatClient
  9. 随意聊天,当然是和自己聊天。

要在网络上使用 ChatServerChatClient,请执行以下操作,并按照“准备使用 ChatServer & ChatClient”中的步骤进行操作。

  • ChatServer 可执行文件复制到服务器系统
  • ChatClient 可执行文件复制到您希望用户从中聊天的计算机上
  • 在服务器系统上启动 ChatServer,请参阅上面的“启动 ChatServer”部分
  • 在复制了 ChatClient 的计算机上启动 ChatClient,请参阅上面的“启动 ChatClient”部分,但请记住,在登录表单的服务器框中,您必须键入运行 ChatServer 的服务器系统的名称或 IP 地址(而不是 127.0.0.1),并输入相应的端口号

注意

如果您将 ChatServerChatClient 可执行文件复制到未安装 Visual Studio 的计算机上,这些可执行文件可能无法运行,因为 MFC 默认未安装在 Windows 操作系统上。

如果 ChatServerChatClient 的可安装版本可在顶部的下载部分下载,则可以将其安装在任何运行 Windows 9x / Windows 2000 / Windows XP 的计算机上,因为可安装版本已包含 MFC。

目前我可能不会上传可安装版本,因为它们的大小很大(每个 1.2MB),而且说实话,目前我负担不起。也许随着时间的推移,如果需求量大的话。

还缺少什么

  • 聊天时没有彩色文本
  • 聊天时没有可变字体
  • 单个聊天室
  • 源代码中未使用线程

也许在以后的版本中可以加入以上功能,目前正在测试中。

开发人员须知

服务器和客户端最重要的代码在 CChatServerDocCChatClientDoc 类中,您应该重点关注这两个类。

一个名为 CMsgCObject 类在服务器和客户端中用于双向发送和接收消息,这些消息随后由接收者(服务器或客户端)进行拆解。CMsg 类具有以下成员:

  • Code(指示消息类型)
  • m_strText(消息文本)
  • m_bClose(当客户端断开连接或关闭时发送)

以下是服务器和客户端发送的代码列表(您可以根据需要使用自己的代码)

#define JOINING_CHAT          1
#define LEAVING_CHAT          2
#define SENDING_CHATTERS_LIST 3
#define SENDING_NICKNAME      4
#define NORMAL_MESSAGE        5
#define DUPLICATE_NICKNAME    6

工作原理

在您输入端口号并单击“确定”以启动服务器后,将使用 `OnNewDocument()` 成员函数中的以下代码创建一个用于监听传入连接请求的套接字:

m_pSocket = new CListeningSocket(this);
if (m_pSocket->Create(Dialog.m_nPort+1500))
{
    if (m_pSocket->Listen())
        return TRUE;
}

当收到来自客户端的新连接请求时,系统会通知 `CListeningSocket` 类中的 `OnAccept` 成员函数。然后,`OnAccept` 调用 `CChatServerDoc` 类中的 `ProcessPendingAccept` 函数,如下所示:

void CListeningSocket::OnAccept(int nErrorCode)
{
    CSocket::OnAccept(nErrorCode);
    m_pDoc->ProcessPendingAccept();
}

在 `ProcessPendingAccept` 函数中,为新客户端创建一个新的套接字(`CClientSocket`),并将该套接字添加到指针列表 `CPtrList` 中,这是一种类似数组的结构。我们在整个程序中使用指针列表来检索套接字以发送或接收消息。系统随后在 `CClientSocket` 的 `OnReceive` 成员函数中读取传入的消息,该函数又调用 `ProcessPendingRead`,并将客户端套接字传递给它,如下所示:

void CClientSocket::OnReceive(int nErrorCode)
{
     CSocket::OnReceive(nErrorCode);
     m_pDoc->ProcessPendingRead(this);
}

在 `ProcessPendingRead` 内部,将拆解从客户端发送的 CMsg 对象,并根据消息的 Code 进行相应的操作。

当客户端通过菜单选择 *File->Exit* 断开连接或退出时,会向服务器发送一条消息,其中 `m_bClose` 设置为 `TRUE`。服务器接收消息并关闭客户端套接字,如下所示:

void CChatServerDoc::CloseSocket(CClientSocket* pSocket)
{
    // Close the Client Socket
    pSocket->Close();
    POSITION pos,temp;
    for(pos = m_connectionList.GetHeadPosition(); pos != NULL;)
    {
        temp = pos;
        // Search for the Socket in the Pointer List
        CClientSocket* pSock = 
           (CClientSocket*)m_connectionList.GetNext(pos);
        if (pSock == pSocket)
        {
            // Found it , so remove it from the Pointer list
            // so that no more messages are sent to that Client Socket
            m_connectionList.RemoveAt(temp);
            break;
        }
    }
    UpdateConnections();
    delete pSocket;
    if(m_connectionList.GetCount() == 0)
        m_msgList.RemoveAll();
}

如果您通过菜单选择 *File->Exit* 退出服务器,并且有客户端连接,服务器将通知所有客户端它已关闭,并关闭所有 CClientSocket(s)。这还可以防止客户端崩溃。以下是执行关闭客户端套接字任务的代码:

void CChatServerDoc::DeleteContents()
{
     // You must initilize m_pSocket to NULL in the Constructor of 
     // CChatServerDoc else the server will crash at the start
     if(m_pSocket == (CListeningSocket*)NULL)
        return;

    // Delete the Listening Socket
    delete m_pSocket;
    m_pSocket = NULL;
    
    CString temp;
    if (temp.LoadString(IDS_SERVERSHUTDOWN))
        m_msgList.AddTail(temp);

    while(!m_connectionList.IsEmpty())
    {
     // Get a Pointer to the Client Socket
      CClientSocket* pSocket = 
        (CClientSocket*)m_connectionList.RemoveHead();
      CMsg* pMsg = AssembleMsg(pSocket , NORMAL_MESSAGE);
      //Set Code for Closing
        pMsg->m_bClose = TRUE;
      // Send Server ShutDowm message to the Client
      SendMsg(pSocket, pMsg);

      if (!pSocket->IsAborted())
      {
         // ShutDown the Client
         pSocket->ShutDown();
         BYTE Buffer[50];
         while (pSocket->Receive(Buffer,50) > 0);
         // Delete the Client Socket
         delete pSocket;
      }
   }

   m_msgList.RemoveAll();

   if (!m_viewList.IsEmpty())
      Message("");

   CDocument::DeleteContents();
}

玩得开心,尽情聊天,但别怪我。我开发这个是因为出于好玩的目的修改了 MSDN 的 Chatter & Chatsvr 示例。

© . All rights reserved.