多播收发程序multicast

最近工作中编写UDP多播程序,要求能收能发。要求是非常简单了,但是从来没有这方面的经验,于是开始到网上翻,先看看基本的概念,再下载能用的代码,然后改成自己需要的,凡事都是三段论,三步走。

 

概念:

多播:加入了同一个多播组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择的复制并传输数据,即只将组内数据传输给那些加入组的主机。这样既能一次将数据传输给多个有需要(加入组)的主机,又能保证不影响其他不需要数据(未加入组)主机的其他通讯。

 

代码:

接下来找代码,看看多播是什么效果,有个切身的感受,才能加深理解嘛。上google code search 输入 “multicast ip_add_membership lang:c|c++ 234.5.6.7” ,搜索的关键字里面加了一个ip地址,是因为看了别的例子,拿这个ip地址做多播地址,所以就把它也当关键字输进去了,测试了几个,发现这个好用,简单http://www.google.cn/codesearch/p?hl=zh-CN#uKFNR_GmXJU/reference/programming/features/multicast/MulticastTest.zip%7CYvUlBXkxhqc/Main.cpp&q=multicast%20ip_add_membership%20lang:c%7Cc++%20234.5.6.7

 

下面是连接提供的代码,新建一个控制台工程,粘贴过来就可以用,可能需要在最前面加一行#include "stdafx.h"

 

#include "stdafx.h" ////////////////////////////////////////////////////////////////////////////////////////////////////// /* Application to send and receive multicasts on a UDP socket Windows console application by Denis Lukianov 06.06.2002 basic functionality 15.04.2004 rewritten for tutorial */ ////////////////////////////////////////////////////////////////////////////////////////////////////// // Headers #include <windows.h> #include <winsock.h> #include <stdio.h> #include <conio.h> ////////////////////////////////////////////////////////////////////////////////////////////////////// // Libraries: automatically link with winsock #pragma comment(lib, "wsock32.lib") ////////////////////////////////////////////////////////////////////////////////////////////////////// // Address definitions to be used in the multicast() function #define MULTICAST_GROUP_IP "234.5.6.7" #define DEFAULT_PORT 15 #define DEFAULT_TTL 7 #define STRING_LENGTH 100 ////////////////////////////////////////////////////////////////////////////////////////////////////// // Bread&butter error reporting void PrintWinsockError() { LPVOID lpMsgBuf; int iError = WSAGetLastError(); if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, iError, 0, (LPTSTR)&lpMsgBuf, 0, 0)) { cprintf("WSAGetLastError %d: %s", iError, lpMsgBuf); LocalFree(lpMsgBuf); } else { cprintf("WSAGetLastError %d", iError); } } ////////////////////////////////////////////////////////////////////////////////////////////////////// // Address-to-ip resolution int iResolve(SOCKADDR_IN &addrResolve, char *cHost, UINT uiPort, UINT uiInterface) { struct in_addr iaHost; // Used to resolve IP and connect LPHOSTENT lpHost; // Used to connect unsigned int iIndex; iaHost.s_addr = inet_addr((LPCSTR)cHost); // Check if this syntax is consistent with IP address if(iaHost.s_addr == INADDR_NONE) // If not IP, must be hostname string { lpHost = gethostbyname(cHost); // Get host structure by host name if(lpHost == NULL)return false; // Whoops! Unresolved hostname! } else // This is an IP so just get structure by address { lpHost = gethostbyaddr((LPCSTR)&iaHost, sizeof(struct in_addr), AF_INET); // Get host structure by address if(lpHost == NULL)return false; // Whoops! Unresolved host IP! } for(iIndex = 0; iIndex < uiInterface; iIndex++) if(!lpHost->h_addr_list[iIndex]) return false; if(!lpHost->h_addr_list[uiInterface]) return false; memset(&addrResolve, NULL, sizeof(addrResolve)); addrResolve.sin_family = AF_INET; // We only use internet addresses addrResolve.sin_addr = *((LPIN_ADDR)lpHost->h_addr_list[uiInterface]); // The remote IP addrResolve.sin_port = htons(uiPort); // The remote port // Okays return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////// // Work out which ip comes first on this machine, or nth ip if iInterface is specified bool bGetNetworkInterface(IN_ADDR &in_addrLocal, unsigned int uiInterface = 0) { char cLocalHostName[MAX_PATH]; gethostname(cLocalHostName, sizeof(cLocalHostName)); SOCKADDR_IN addrLocal; if(!iResolve(addrLocal, cLocalHostName, 0, uiInterface)) return false; in_addrLocal = addrLocal.sin_addr; return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////// // Function to associate a socket with a multicast group bool bJoinMulticastGroup(SOCKET UDP_Socket, char *cMulticastGroup) { // Used to set multicast TTL char cTTL = DEFAULT_TTL; // Set the multicast ttl int iRet = setsockopt(UDP_Socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&cTTL, sizeof(cTTL)); if(iRet == SOCKET_ERROR) { cprintf("Failed to set multicast ttl/r/n"); PrintWinsockError(); return false; } // Add socket to be a member of the multicast group struct ip_mreq ipmr; ipmr.imr_multiaddr.s_addr = inet_addr(cMulticastGroup); bGetNetworkInterface(ipmr.imr_interface); // Does the job of... ipmr.imr_interface.s_addr = inet_addr("ip"); iRet = setsockopt(UDP_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&ipmr, sizeof(ipmr)); if(iRet == SOCKET_ERROR) { cprintf("Failed to add multicast membership/r/n"); PrintWinsockError(); return false; } // Set the local interface from which multicast is to be transmitted IN_ADDR inLocal; bGetNetworkInterface(inLocal); // Does the job of inLocal = inet_addr("ip"); setsockopt(UDP_Socket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&inLocal, sizeof(inLocal)); if(iRet == SOCKET_ERROR) { cprintf("Failed to set multicast interface/r/n"); PrintWinsockError(); return false; } return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////// // Function that receives UDP packets (not just multicast ones) bool bReceiveMulticast(SOCKET UDP_Socket, char *cMulticastGroup) { // Used to identify where the packet came from SOCKADDR_IN addrFrom; // String to hold the packet data char cData[STRING_LENGTH]; // Holds status of incoming packet queue ULONG ulQueue[1]; // Tracks if any packets are received bool bGotPacket = false; while(true) { // Did a packet come through? *ulQueue = NULL; // Reset incoming packet status if(ioctlsocket(UDP_Socket, FIONREAD, (ULONG FAR *)ulQueue)) // Ask the socket how many packets are waiting { cprintf("ioctlsocket failed/r/n"); PrintWinsockError(); return false; } // No packets waiting? if(!*ulQueue) break; // Receive the packet int size = sizeof(addrFrom); int iRet = recvfrom(UDP_Socket, (char *)cData, sizeof(cData) - 1, 0, (struct sockaddr*)&addrFrom, &size); if(iRet == SOCKET_ERROR) { cprintf("Packet arrived, but failed to be received/r/n"); PrintWinsockError(); return false; } // Terminate packet so we can display it as a string cData[iRet] = 0; // Print the contents of the packet cprintf("Received packet from %s: /"%s/"/r/n", inet_ntoa(addrFrom.sin_addr), cData); bGotPacket = true; } // If no packets received, inform the user if(!bGotPacket) cprintf("No packets received/r/n"); return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////// // Function that sends a multicast bool bSendMulticast(SOCKET UDP_Socket, char *cMulticastGroup) { // Used to track return values of functions int iRet; // Where the packet will be heading SOCKADDR_IN addrDest; memset(&addrDest, 0, sizeof(addrDest)); // String to hold the packet data char cData[STRING_LENGTH]; // Destination IP Address addrDest.sin_family = AF_INET; addrDest.sin_port = htons(DEFAULT_PORT); addrDest.sin_addr.s_addr = inet_addr(cMulticastGroup); // Data to send memset(cData, 0, sizeof(cData)); wsprintf((char *)cData, "Hello World!"); // Send to MULTICAST iRet = sendto(UDP_Socket, (char *)cData, (int)strlen(cData), 0, (struct sockaddr*)&addrDest, sizeof(addrDest)); if(iRet == SOCKET_ERROR) { cprintf("Packet failed to be sent/r/n"); PrintWinsockError(); return false; } // Bingo return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////// // Application entry point int main(int argc, char *argv[], char *envp[]) { // Local for return value checking int iRet; // Used for winsock startup WORD wVersionRequested; WSADATA wsaData; // Initialise winsock, latest version wVersionRequested = MAKEWORD(2, 2); iRet = WSAStartup(wVersionRequested, &wsaData); if(iRet != 0) { WSACleanup(); cprintf("Failed to start winsock/r/n"); return false; } // Create a new socket SOCKET UDP_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(UDP_Socket==INVALID_SOCKET) { cprintf("Failed to create socket/r/n"); PrintWinsockError(); return false; } // Bind socket to port SOCKADDR_IN addrLocal; addrLocal.sin_family = AF_INET; addrLocal.sin_port = htons(DEFAULT_PORT); bGetNetworkInterface(addrLocal.sin_addr); // Same as... addrLocal.sin_addr.s_addr = inet_addr("yourip"); iRet = bind(UDP_Socket, (LPSOCKADDR)&addrLocal, sizeof(struct sockaddr)); if(iRet == SOCKET_ERROR) { cprintf("Failed to bind socket/r/n"); PrintWinsockError(); return false; } cprintf("Using UDP port %d, press any key to exit/r/n", DEFAULT_PORT); cprintf("Attempting to join multicast group %s... ", MULTICAST_GROUP_IP); // Join multicasting group if(bJoinMulticastGroup(UDP_Socket, MULTICAST_GROUP_IP)) cprintf("done/r/n/r/n"); else cprintf("FAILED/r/n"); while(true) { // Send a multicast packet if(bSendMulticast(UDP_Socket, MULTICAST_GROUP_IP)) cprintf("Multicast sent to group %s/r/n", MULTICAST_GROUP_IP); // Wait for echo packet to arrive, just in case the network is laggy Sleep(100); // Receive a packet bReceiveMulticast(UDP_Socket, MULTICAST_GROUP_IP); // Dont overwhelm the user Sleep(1000); if(kbhit())break; Sleep(1000); if(kbhit())break; Sleep(1000); if(kbhit())break; } // Close socket, kill winsock closesocket(UDP_Socket); WSACleanup(); // Quit return iRet; }   

编译运行,可以收发数据。看上来这个东西可以用啊。关键的东西就是加入多播组。

 

修改:

在发送函数bSendMulticast和接收函数bReceiveMulticast上修改,能够收发我自己的数据了。好啦,有了可以用的代码,我就有了基础设施了,可以搭建自己的东西。

 

开展自己的工作:

上面的代码的读写是在一个循环里面完成,因为我的任务的要求,要有读线程,当有数据时调用一个回调函数,写就不需要另开线程了,主线程里面检查有数据则发送,无数据则空转。

 

 

有人问:socket可以同时读写吗,也就是说我在两个线程里面用同一个socket句柄,需要互斥吗?

答:(非标准答案,请观众再自己确认一下)

如果一个是读线程,一个是写线程,那就不用互斥了。

如果每个线程里面既有读,又有写,那就好好保护一下吧。(好像没有这么干的,自找麻烦嘛,这不是!)

 

 

 

 

 

你可能感兴趣的:(多播收发程序multicast)