1:说明
使用UDP协议进行信息的传输之前不需要建立连接。换句话说就是客户端向服务器发送信息,客户端只需要给出服务器的ip地址和端口号,然后将信息封装到一个待发送的报文中并且发送出去。至于服务器端是否存在,或者能否收到该报文,客户端根本不用管。
单播用于两个主机之间的端对端通信,广播用于一个主机对整个局域网上所有主机上的数据通信。单播和广播是两个极端,要么对一个主机进行通信,要么对整个局域网上的主机进行通信。实际情况下,经常需要对一组特定的主机进行通信,而不是整个局域网上的所有主机,这就是多播的用途。
通常我们讨论的udp的程序都是一对一的单播程序。本文将讨论一对多的服务:广播(broadcast)、多播(multicast)。对于广播,网络中的所有主机都会接收一份数据副本。对于多播,消息只是发送到一个多播地址,网络只是将数据分发给那些表示想要接收发送到该多播地址的数据的主机。总得来说,只有UDP套接字允许广播或多播。
广播UDP与单播UDP的区别就是IP地址不同,广播使用广播地址255.255.255.255,将消息发送到在同一广播网络上的每个主机。值得强调的是:本地广播信息是不会被路由器转发。当然这是十分容易理解的,因为如果路由器转发了广播信息,那么势必会引起网络瘫痪。这也是为什么IP协议的设计者故意没有定义互联网范围的广播机制。
其实广播顾名思义,就是想局域网内所有的人说话,但是广播还是要指明接收者的端口号的,因为不可能接受者的所有端口都来收听广播。
多播,也称为“组播”,将网络中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。
在广域网上广播的时候,其中的交换机和路由器只向需要获取数据的主机复制并转发数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择地复制并传输数据,将数据仅仅传输给组内的主机。多播的这种功能,可以一次将数据发送到多个主机,又能保证不影响其他不需要(未加入组)的主机的其他通 信。
广播,组播,一对一播的服务代码都是一样的,客户端代码也基本一样,只是地址不一样。
广播是: 255.255.255.255 我发现只是网卡所在网
组播是 : 192.168.1.255
一对一播是: 192.168.1.101, 或其他具体地址。
2:服务端程序代码
#include "stdafx.h"
#include"winsock2.h"
#include<iostream>
#include<conio.h>
using namespace std;
#define MYPORT 8080 // the port users will be connecting to
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);
SOCKET sock;
sock = socket(AF_INET,SOCK_DGRAM,0);
char broadcast = '1';
// This option is needed on the socket in order to be able to receive broadcast messages
// If not set the receiver will not receive broadcast messages in the local network.
if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,&broadcast,sizeof(broadcast)) < 0)
{
cout<<"Error in setting Broadcast option";
closesocket(sock);
return 0;
}
struct sockaddr_in Recv_addr;
struct sockaddr_in Sender_addr;
int len = sizeof(struct sockaddr_in);
char recvbuff[50];
int recvbufflen = 50;
char sendMSG[]= "Broadcast message from READER";
Recv_addr.sin_family = AF_INET;
Recv_addr.sin_port = htons(MYPORT);
Recv_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock,(sockaddr*)&Recv_addr, sizeof (Recv_addr)) < 0)
{
cout<<"Error in BINDING"<<WSAGetLastError();
_getch();
closesocket(sock);
return 0;
}
recvfrom(sock,recvbuff,recvbufflen,0,(sockaddr *)&Sender_addr,&len);
cout<<"\nnet addr=\n"<<(int)Sender_addr.sin_addr.S_un.S_un_b.s_b1<<"."<<(int)Sender_addr.sin_addr.S_un.S_un_b.s_b2<<".";
cout<<(int)Sender_addr.sin_addr.S_un.S_un_b.s_b3<<"."<<(int)Sender_addr.sin_addr.S_un.S_un_b.s_b4;
cout<<"\n\n\tReceived Message is : "<<recvbuff;
cout<<"\n\n\tPress Any to send message";
//_getch();
if(sendto(sock,sendMSG,strlen(sendMSG)+1,0,(sockaddr *)&Sender_addr,sizeof(Sender_addr)) < 0)
{
cout<<"Error in Sending."<<WSAGetLastError();
cout<<"\n\n\t\t Press any key to continue....";
_getch();
closesocket(sock);
return 0;
}
else
cout<<"\n\n\n\tREADER sends the broadcast message Successfully";
cout<<"\n\n\tpress any key to CONT...";
_getch();
closesocket(sock);
WSACleanup();
}
3:客户端代码是:
#include"winsock2.h"
#include<iostream>
#include<conio.h>
using namespace std;
#define MYPORT 8080 // the port users will be connecting to
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);
SOCKET sock;
sock = socket(AF_INET,SOCK_DGRAM,0);
char broadcast = '1';
if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,&broadcast,sizeof(broadcast)) < 0)
{
cout<<"Error in setting Broadcast option";
closesocket(sock);
return 0;
}
//这段代码设置广播方式,客户端是必要的,服务端可选。在linux 里, 应该是 int broadcast='1'
struct sockaddr_in Recv_addr;
struct sockaddr_in Sender_addr;
int len = sizeof(struct sockaddr_in);
char sendMSG[] ="Broadcast message from SLAVE TAG";
char recvbuff[50] = "";
int recvbufflen = 50;
Recv_addr.sin_family = AF_INET;
Recv_addr.sin_port = htons(MYPORT);
// Recv_addr.sin_addr.s_addr = INADDR_BROADCAST; // this isq equiv to 255.255.255.255
Recv_addr.sin_addr.s_addr = inet_addr("192.168.1.255"); //组播
//Recv_addr.sin_addr.s_addr = inet_addr("192.168.1.101"); //单播,一对一
//Recv_addr.sin_addr.s_addr = inet_addr("255.255.255.255"); //广播
unsigned long tm=1000;
if(setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(char*)&tm,sizeof(tm))){ printf("sockopt err");}
//这段代码设置socket 的超时,这里设置是1000 ms
// linux 里设置不一样
cout<<"\nsent...";
sendto(sock,sendMSG,strlen(sendMSG)+1,0,(sockaddr *)&Recv_addr,sizeof(Recv_addr));
//long tm0=GetTickCount();
int flag;
for(int i=0;i<5;i++)
{
flag=recvfrom(sock,recvbuff,recvbufflen,0,(sockaddr *)&Recv_addr,&len);
if(flag<0)
{
break; //超时就退出循环
}
char addr_buf[20];
cout<<"\nnet addr=\n"<<(int)Recv_addr.sin_addr.S_un.S_un_b.s_b1<<"."<<(int)Recv_addr.sin_addr.S_un.S_un_b.s_b2<<".";
cout<<(int)Recv_addr.sin_addr.S_un.S_un_b.s_b3<<"."<<(int)Recv_addr.sin_addr.S_un.S_un_b.s_b4;
cout<<"\n\n\tReceived message from Reader is => "<<recvbuff;
}
//假设有5个服务端可能接收, 一旦超时,就不管了
cout<<"\n\n\tpress any key to CONT...";
_getch();
closesocket(sock);
WSACleanup();
}
以上代码在visual studio 2010 c++ 上编译并测试通过。
4: 以下是执行情况的界面
服务端开启了三个,本机执行上述服务端代码, zynq 7000 的petalinux 上执行一个, 另一个主机的虚拟主机ubuntu16运行一个.。 运行的代码,见我另篇博文。 linux 下的udp 通讯,那是一对一单播。