在這裡我不想照本宣科的大說什麼理論,概念的東東,我的目的是說明在我們的代碼中如何實現多播功能。
開發環境: VC 6.0(sp5), 使用winsock2(ws2_32.lib)
測試環境: 三台機器位於兩個不同的AD域中,所有機器都是Windows 2000(SP4)的操作系統。 sz09和kenfilweb4位於域kenfilszwin2k, kenfil-sz18是域sz18-domain域的一台DC。
對於局域網來說,我們可以使用的多播地址為224.0.0.0-224.0.0.255(想知道為什麼?看看書吧。:-) )
多播是通過設置套接口(socket, 來自書上的翻譯)選項來實現的,這個套接口必須是一個UDP的套接口
IP_ADD_MEMBERSHIP: 加入一個多播組
IP_DROP_MEMBERSHIP: 離開一個多播組
IP_MULTICAST_IF: 指定外出多播數據報的外出接口
IP_MULTICAST_TTL: TTL數
IP_MULTICAST_LOOP: 是否禁止回饋,我的理解是一台機器是否可以接收到自己發送的多播數據報在設置IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP選項的時候,我們需要用到ip_mreq結構, 要使用此結構,你必須include , 並確保此include位於include 之下
程序功能:程序讀取用戶輸入的行發送到多播組,加入多播組的任何客戶端應該顯示從多播組中讀到的數據。
實現代碼如下:
//Project Setting -> C/C++ -> Code Generation -> 確認選中"Debug Multithreaded"
#include
#include //注意這裡的include文件順序
#include
#include //_beginthread要求
#pragma comment(lib, "ws2_32.lib")
using namespace std;
const char* MULTICAST_IP = "224.0.0.99"; //多播組地址
const int MULTICAST_PORT = 2002; //多播組端口
const int BUFFER_SIZE = 1024;
void do_send(void* arg); //讀取用戶輸入並發送到多播組線程函數
void do_read(void* arg); //讀物多播組數據函數
int main()
{
WSAData wsaData;
if( WSAStartup(MAKEWord(2,2), &wsaData) != 0 )
{
cout<<"Error in WSAStartup"< return 0;
}
SOCKET server;
server = socket(AF_INET, SOCK_DGRAM, 0); //創建一個UDP套接口
cout<<"create socket: "<
int ret ;
const int on = 1; //允許程序的多個實例運行在同一台機器上
ret = setsockopt(server, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
if( ret == SOCKET_ERROR )
{
WSACleanup();
cout<<"Error in setsockopt(SO_REUSEADDR): "< return 0;
}
const int routenum = 10;
ret = setsockopt(server,IPPROTO_IP,IP_MULTICAST_TTL,
(char*)&routenum,sizeof(routenum));
if( ret == SOCKET_ERROR )
{
WSACleanup();
cout<<"Error in setsockopt(IP_MULTICAST_TTL): "< return 0;
}
const int loopback = 0; //禁止回饋
ret = setsockopt(server,IPPROTO_IP,IP_MULTICAST_LOOP,
(char*)&loopback,sizeof(loopback));
if( ret == SOCKET_ERROR )
{
WSACleanup();
cout<<"Error in setsockopt(IP_MULTICAST_LOOP): "< return 0;
}
sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(MULTICAST_PORT);
local.sin_addr.S_un.S_addr = INADDR_ANY;
ret = bind(server, (sockaddr*)(&local), sizeof(local));
if( ret == SOCKET_ERROR )
{
WSACleanup();
cout<<"Error in bind: "< return 0;
}
ip_mreq mreq;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_interface.S_un.S_addr = INADDR_ANY;
mreq.imr_multiaddr.S_un.S_addr = inet_addr(MULTICAST_IP);
//加入一個多播組
ret = setsockopt(server,IPPROTO_IP,IP_ADD_MEMBERSHIP,
(char*)&mreq,sizeof(mreq));
if( ret == SOCKET_ERROR )
{
WSACleanup();
cout<<"Error in setsockopt(IP_ADD_MEMBERSHIP): "< return 0;
}
//創建了兩個線程,一個讀用戶輸入並發送,一個讀多播組數據
HANDLE hHandle[2];
hHandle[0] = (HANDLE)_beginthread(do_send,0,(void*)server);
hHandle[1] = (HANDLE)_beginthread(do_read,0,(void*)server);
//如果用戶輸入結束,程序就終止了
WaitForSingleObject(hHandle[0], INFINITE);
WSACleanup();
return 0;
}
void do_send(void* arg)
{
SOCKET server = (SOCKET)arg;
char sendline[BUFFER_SIZE+1];
sockaddr_in remote;
memset(&remote, 0, sizeof(remote));
remote.sin_addr.s_addr = inet_addr ( MULTICAST_IP );
remote.sin_family = AF_INET ;
remote.sin_port = htons(MULTICAST_PORT);
for(;;) //讀取用戶輸入知道用戶輸入"end"
{
cin.getline(sendline, BUFFER_SIZE);
if(strncmp(sendline,"end",3)==0)
break;
//發送用戶輸入的數據到多播組
sendto(server, sendline, strlen(sendline), 0, (sockaddr*)(&remote), sizeof(remote));
}
cout<<"do_send end..."<}
void do_read(void* arg)
{
SOCKET server = (SOCKET)arg;
char buf[BUFFER_SIZE+1];
int ret;
sockaddr_in clIEnt;
int clIEntLen;
for(;;) //一直讀取知道主線程終止
{
clientLen = sizeof(clIEnt);
memset(&client, 0, clIEntLen);
ret = recvfrom(server, buf, BUFFER_SIZE, 0, (sockaddr*)(&clientLen), &clIEntLen);
if ( ret == 0) //do_read在用戶直接回車發送了一個空字符串
{
continue;
}
else if( ret == SOCKET_ERROR )
{
if( WSAGetLastError() == WSAEINTR ) //主線程終止recvfrom返回的錯
break;
cout<<"Error in recvfrom: "< break ;
}
buf[ret] = '';
cout<<"received: "< }
cout<<"do_read end..."<}
當然上面的代碼如果應用到實際的程序中還需要作更多的工作,不過實現多播功能真的很簡單,不是嗎 :-)
我只是剛剛才開始學習網絡編程方面的東東,上面的代碼如果你認為有什麼問題,請直接發email給我。