socket下的广播与多播实现

简介:

一、广播:广播是指在一个局域网中向所有的网上节点发送信息。这是UDP连接的一种。
    1.初始化 :WSAStartup(MAKEWORD(2,2),&wsad);
    2.创建一个UDP的socket :s=socket(AF_INET,SOCK_DGRAM,0);
    3.如果这个socket希望收到信息,则需要绑定地址和这组广播的端口号,如果只是希望发送广播信息,则不需要这步
  SOCKADDR_IN udpAdress,sender; 
  int senferAddSize=sizeof(sender); 
  udpAdress.sin_family=AF_INET; 
  udpAdress.sin_port=htons(11114); 
  udpAdress.sin_addr.s_addr=inet_addr("10.11.131.32"); 
  bind(s,(SOCKADDR*)&udpAdress,sizeof(udpAdress));  //只接收发往该插口的IP数据
//这样这个节点即可收到局域网内所有发往端口11114的广播信息

    4.设置socket的属性为广播 
  bool optval=true; 
  setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char*)&optval,sizeof(bool));
    5.下面就可以使用recvfrom或sendto来收发广播信息了
    这里是接受,这是一个阻塞操作 
        ret=recvfrom(s,data,1000,0,(SOCKADDR*)&sender,&senferAddSize);
    这里是像该广播组发送信息,注意发送的地址为广播地址INADDR_BROADCAST,端口号为改组广播的端口号11114
  SOCKADDR_IN dstAdd; 
  dstAdd.sin_family=AF_INET; 
  dstAdd.sin_port=htons(11114); 
  dstAdd.sin_addr.s_addr=INADDR_BROADCAST; 
  sendto(s,data(),totalbyte,0,(SOCKADDR*)&dstAdd,sizeof(SOCKADDR));

二、多播
    1.初始化
  WSAStartup(MAKEWORD(2,2),&wsad);
    2.创建一个用于多播通信的socket,注意这个socket的参数为设置成多播 
  s=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|WSA_FLAG_OVERLAPPED);
    3.将socket绑定到一个本地地址、端口,和广播不同,在多播中,无论是发送还是接收端都必须绑定一个本地地址,这个地址就是多播通信时处理信息的端口 
  udpAdress.sin_family=AF_INET; 
  udpAdress.sin_port=htons(22222); 
  udpAdress.sin_addr.s_addr=inet_addr("10.11.131.32"); 
  bind(s,(SOCKADDR*)&udpAdress,sizeof(udpAdress));
    4.定义多播组的地址 
  multiCastGroup.sin_family=AF_INET; 
  multiCastGroup.sin_port=htons(1111);此处端口任意,每个节点的可以设置成不同的 
  multiCastGroup.sin_addr.s_addr=inet_addr("224.0.0.3"); 此处需使用上面规定地址段内的多播地址
    5.加入这个多播组。注意这里的函数返回了一个socket,这个socket不负责通信,只是在脱离多播组时使用
  SOCKET sockM=WSAJoinLeaf(s,(SOCKADDR*)&multiCastGroup,sizeof(multiCastGroup),NULL,NULL,NULL,NULL,JL_BOTH);
    6.下面使用recvfrom接受多播信息,或者使用sendto发送多播信息   
ret=recvfrom(s,data,1000,0,(SOCKADDR*)&sender,&senferAddSize);
sendto(s,data(),totalbyte,0,(SOCKADDR*)&multiCastGroup,sizeof(multiCastGroup));
    7.最后关闭清理 
  closesocket(sockM); 
  closesocket(s); 
  WSACleanup();


    7.其他:
1)在多播组中,默认情况下一个发出多播信息的节点也会收到自己发送的信息,这称为多播回环,可以关闭多播回环:
bool val=false;
setsocket(s,IPPROTO_IP,IP_MULTICAST_LOOP,(char*)val,sizeof(val));
2)在多播时,通常要设置适当的TTL(TTL的值是多少,那么多播信息就可以经过多少路由器,每经过一个路由器,TTl的值自动减1):
int val=3;
setsocket(s,IPPROTO_IP,IP_MULTICAST_TTL,(char*)val,sizeof(int)); 

三、sendto和recvfrom用法

int sendto( SOCKET s, const char FAR* buf, int len, int flags,
const struct sockaddr FAR* to, int tolen);
s:一个标识套接口的描述字。
buf:包含待发送数据的缓冲区。
len:buf缓冲区中数据的长度。
flags:调用方式标志位。
to:指针,指向目的套接口的地址,由作者填充
tolen:to所指地址的长度。

int recvfrom( SOCKET s, char FAR* buf, int len, int flags,
struct sockaddr FAR* from, int FAR* fromlen);
s:标识一个已连接套接口的描述字。
buf:接收数据缓冲区。
len:缓冲区长度。
flags:调用操作方式。
from:指针,指向装有源地址的缓冲区,当接收到数据时被自动填充,保存了源sock地址
fromlen:指针,指向from缓冲区长度值。


注:客户端和服务器使用具名端口的方法:指定一个sock地址(包括端口),然后将该地址bind到sock句柄,则这个sock就是具名sock;需要接收消息的一方,就需要使用bind指定一个具名sock句柄,否则网络层不知道将收到的IP包投递到哪些端口对应的socket句柄。

 

四、例子

例子1、广播
// 服务器端
// Server.cpp : Defines the entry point for the console application.//

#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
const int MAX_BUF_LEN = 255;
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

// 启动socket api
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ){return -1;}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
{WSACleanup( );return -1; }
// 创建socket
SOCKET connect_socket;
connect_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(INVALID_SOCKET == connect_socket)
{err = WSAGetLastError();printf("/"socket/" error! error code is %d/n", err);return -1;}

SOCKADDR_IN sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(3779);
sin.sin_addr.s_addr = INADDR_BROADCAST;

bool bOpt = true;
//设置该套接字为广播类型
setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));
int nAddrLen = sizeof(SOCKADDR);
char buff[MAX_BUF_LEN] = "";
int nLoop = 0;
while(1)
{nLoop++;
sprintf(buff, "%8d", nLoop);
// 发送数据
int nSendSize = sendto(connect_socket, buff, strlen(buff), 0, (SOCKADDR*)&sin, nAddrLen);
if(SOCKET_ERROR == nSendSize)
{err = WSAGetLastError();printf("/"sendto/" error!, error code is %d/n", err);return -1;}
printf("Send: %s/n", buff);
Sleep(500);
}
return 0;
}

// 客户端
// Client.cpp : Defines the entry point for the console application.//
#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
const int MAX_BUF_LEN = 255;
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
// 启动socket api
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ){return -1;}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
{WSACleanup( );return -1; }

// 创建socket
SOCKET connect_socket;
connect_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(INVALID_SOCKET == connect_socket)
{
err = WSAGetLastError();
printf("/"socket/" error! error code is %d/n", err);
return -1;
}

// 用来绑定套接字
SOCKADDR_IN sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(3779);
sin.sin_addr.s_addr = 0;
// 用来从网络上的广播地址接收数据
SOCKADDR_IN sin_from;
sin_from.sin_family = AF_INET;
sin_from.sin_port = htons(3779);
sin_from.sin_addr.s_addr = INADDR_BROADCAST;
//设置该套接字为广播类型,
bool bOpt = true;
setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));

// 绑定套接字
err = bind(connect_socket, (SOCKADDR*)&sin, sizeof(SOCKADDR));
if(SOCKET_ERROR == err)
{
err = WSAGetLastError();
printf("/"bind/" error! error code is %d/n", err);
return -1;
}

int nAddrLen = sizeof(SOCKADDR);
char buff[MAX_BUF_LEN] = "";
int nLoop = 0;
while(1)
{
// 接收数据
int nSendSize = recvfrom(connect_socket, buff, MAX_BUF_LEN, 0, (SOCKADDR*)&sin_from, &nAddrLen);
if(SOCKET_ERROR == nSendSize)
{
  err = WSAGetLastError();
  printf("/"recvfrom/" error! error code is %d/n", err);
  return -1;
}
buff[nSendSize] = '/0';
printf("Recv: %s/n", buff);
}
return 0;
}




本文转自 a_liujin 51CTO博客,原文链接:http://blog.51cto.com/a1liujin/1699540,如需转载请自行联系原作者

相关文章
|
4月前
|
网络协议 搜索推荐
网络中的单播、多播和广播
【8月更文挑战第24天】
132 0
|
7月前
|
网络协议 Unix 网络架构
IP多播需要使用两种协议(IGMP和多播路由选择协议)
IP多播需要使用两种协议(IGMP和多播路由选择协议)
160 0
|
网络安全 网络架构
单播,组播和广播
单播,组播和广播
|
网络协议
Qt网络编程之搭建Udp通信【单播、组播、广播】
Qt网络编程之搭建Udp通信【单播、组播、广播】
848 0
|
网络协议 Java
UDP发送数据、接收数据及UDP通信程序练习
UDP发送数据、接收数据及UDP通信程序练习
294 0
|
消息中间件 网络协议 算法
Netty实战二-实现UDP的单播和广播
Netty实战二-实现UDP的单播和广播
1000 0
|
Go 网络架构 网络协议
UDP 单播、广播和多播
阅读目录(Content) 一、UDP广播  二、UDP多播 1、多播(组播)的概念 2、广域网的多播 三、UDP广播与单播 广播与单播的比较      使用UDP协议进行信息的传输之前不需要建议连接。
3772 0
|
网络协议 Java 网络性能优化
java网络编程(3)UDP协议编程(单播多播广播)
这一篇文章开始着重讲解UDP编程。这块的知识也算是非常重要的,而且现在的编程都离不开网络。花了一些时间整理了一下。
392 0
java网络编程(3)UDP协议编程(单播多播广播)
|
网络协议 API C++
C++ | 实现UDP广播
手把手教你实现UDP广播。
641 0
|
Java 数据处理
【Java 网络编程】UDP 服务器 客户端 通信 ( DatagramSocket | DatagramPacket | UDP 发送数据包 | UDP 接收数据包 | 端口号分配使用机制 )
【Java 网络编程】UDP 服务器 客户端 通信 ( DatagramSocket | DatagramPacket | UDP 发送数据包 | UDP 接收数据包 | 端口号分配使用机制 )
503 0
【Java 网络编程】UDP 服务器 客户端 通信 ( DatagramSocket | DatagramPacket | UDP 发送数据包 | UDP 接收数据包 | 端口号分配使用机制 )