基于IP多播的组讨论会实例分析一

       第一部分:
封装CGroupTalk类
 CGroupTalkl类在构造函数中创建内部线程,工作线程会自动地加入会议组,处理接受到的消息,在析构函数中对资源进行清理,离开会议组,一切都不需要用户干预。CGroupTalk类仅向用户提供一个成员函数-------SendText,用于向指定的成员或会议组发送单播或多播数据。
   CGroupTalk类创建两个套接字----m_sSend和m_sRead,一个用于接受数据,一个用于发 送数据。下面就是 CGroupTalk 的具体定义。
class CGroupTalk
{
public:
       // 构造函数,创建工作线程,加入会议组
       CGroupTalk(HWND hNotifyWnd, DWORD dwMultiAddr, DWORD dwLocalAddr = INADDR_ANY, int nTTL = 64);
       // 析构函数,清理资源,离开会议组
       ~CGroupTalk();
 
       // 向其它成员发送消息。 dwRemoteAddr 为目标成员的地址,如果为 0 则向所有成员发送
       BOOL SendText(char *szText, int nLen, DWORD dwRemoteAddr = 0);
 
protected:
              // 帮助函数
       // 加入一个多播组
       BOOL JoinGroup();
       // 离开一个多播组
       BOOL LeaveGroup();
       // 向指定地址发送 UDP 封包
       BOOL Send(char *szText, int nLen, DWORD dwRemoteAddr);
 
protected:
              // 具体实现
       // 处理到来的封包
       void DispatchMsg(GT_HDR *pHeader, int nLen);
       // 工作线程
       friend DWORD WINAPI _GroupTalkEntry(LPVOID lpParam);      CInitSock theSock;
 
       HWND m_hNotifyWnd;           // 主窗口句柄
       DWORD m_dwMultiAddr;       // 此组使用的多播地址
       DWORD m_dwLocalAddr;      // 用户要使用的本地接口
       int m_nTTL;                       // 多播封包的 TTL
       HANDLE m_hThread;              // 工作线程句柄
       HANDLE m_hEvent;         // 事件句柄,用来使用重叠 I/O 接收数据
 
       SOCKET m_sRead;                 // 接收数据的套节字,它必须加入多播组
       SOCKET m_sSend;                  // 发送数据的套节字,不必加入多播组
 
       BOOL m_bQuit;                // 用来通知工作线程退出
      
       char m_szUser[256];          // 用户名
};
下面对类中的几个重要函数进行说明:
BOOL CGroupTalk::JoinGroup()
{
       // 加入会议组
       ip_mreq mcast;
       mcast.imr_interface.S_un.S_addr = INADDR_ANY;
       mcast.imr_multiaddr.S_un.S_addr = m_dwMultiAddr;
       int nRet = ::setsockopt(m_sRead, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast));
      
       if(nRet != SOCKET_ERROR)
       {
              // 向组中所有成员发送 MT_JION 消息,告诉它们自己的用户信息
              char buf[sizeof(GT_HDR)] = { 0 };
              GT_HDR *pHeader = (GT_HDR *)buf;
              pHeader->gt_type = MT_JION;
              strncpy(pHeader->szUser, m_szUser, 15);
              Send(buf, sizeof(GT_HDR), m_dwMultiAddr);
              return TRUE;
       }
       return FALSE;
}
1. 
加入和离开可以使用 setsockop 函数,又两个套接字选项控制组的加入和离开: IP_ADD-MEMBERSHIP IP_DROP_MEMBERSHIP ,套接字选项的级别是 IPPROTO_IP, 输入参数是一个 ip_mreqj 结构;
定义如下:
struct ip_mreq {
       struct in_addr imr_multiaddr;    /* IP multicast address of group */
       struct in_addr imr_interface;      /* local IP address of interface */
};
int setsockopt (
 SOCKET s,                 
  int level,                
  int optname,              
  const char FAR * optval
  int optlen                
);
第一个参数是要改变的套接字,第二个参数是指定此选项(控制功能)定义在哪个级别,如 SOL_SOCKT,( 应用层 ) IPPROTO_TCP( 传输层 ),IPPROTO_IP( 网络层 )
第三个参数就是关于具体选项的名称,也就是要实现的某项的功能,如代码所示 level IPPROTO_IP IP-ADD-MEMERSHIP 是说增加一个套接字到 IP 多播组中。第四个参数缓冲区内所有选项的值都被返回到这里。
2.
 
例如加入一个新成员时,它向组的所有成员发送 MT_JOIN 消息,告诉他们自己的 IP 地址和用户名,其他在线成员接受到这个消息之后,纷纷向新成员发送 MT_MINE 消息,告诉他自己的 IP 地址和用户名,这样一来,每个成员就都知道了组中的其他成员的信息。
为了实现这一功能,必须首先定义一个简单的协议,也就是定义消息格式。
const enum
{
       MT_JION = 1,    // 一个用户加入
       MT_LEAVE,        // 一个用户离开
       MT_MESG,         // 一个用户发送消息
 
       // 内部使用,告诉新加入的用户自己的用户信息
       MT_MINE
};
只要有成员加入或者离开这个组, MT_JION 或者 MT_LEAVE 封包就会被送到组,以便所有的成员都可以动态跟踪在线成员。
   用户发送的封包因该还有消息类型,自己的 IP 地址,用户名等信息,所有协议的定义如下;
Typedef struct gt_hdr
{
      Unsigned char gt_type;    // 消息类型
      DWORD dwAddr;        // 发送消息的用户名和 IP 地址
      Char szUser[15];         // 发送消息的用户的用户名
      Int nDataLength;        // 后面数据的长度
     Char * data(){return (char*)(this+1);}
 
}GT_HDR;
下面是 :LeaveGroup () JoinGroup 类似 Send 函数对套接字的 API 比较好理解
 
BOOL CGroupTalk::LeaveGroup ()
{
       // 离开会议组
       ip_mreq mcast;
       mcast.imr_interface.S_un.S_addr = INADDR_ANY;
       mcast.imr_multiaddr.S_un.S_addr = m_dwMultiAddr;
       int nRet = ::setsockopt(m_sRead, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mcast, sizeof(mcast));
 
       if(nRet != SOCKET_ERROR)
       {
              // 向组中所有成员发送 MT_LEAVE 消息,告诉它们自己离开了
              char buf[sizeof(GT_HDR)] = { 0 };
              GT_HDR *pHeader = (GT_HDR *)buf;
              pHeader->gt_type = MT_LEAVE;
              strncpy(pHeader->szUser, m_szUser, 15);
              Send(buf, sizeof(GT_HDR), m_dwMultiAddr);
              return TRUE;
       }
       return FALSE;
}
 
int CGroupTalk::Send(char *szText, int nLen, DWORD dwRemoteAddr)
{
       // 发送 UDP 封包
       sockaddr_in dest;
       dest.sin_family = AF_INET;
       dest.sin_addr.S_un.S_addr = dwRemoteAddr;
       dest.sin_port = ::ntohs(GROUP_PORT);
       return ::sendto(m_sSend, szText, nLen, 0, (sockaddr*)&dest, sizeof(dest));
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值