单播发送与接收
1.创建套接字。
socketfd_udp_single = socket(AF_INET, SOCK_DGRAM, 0);
2.定义struct sockaddr_in Local_addr变量,存放本地IP以及端口号。
struct sockaddr_in Host_addr; //存放服务端IP信息
struct sockaddr_in Local_addr; //存放本地IP信息
//服务端IP信息
Host_addr.sin_family = AF_INET;
Host_addr.sin_addr.s_addr = inet_addr(hostIp); //服务端IP
Host_addr.sin_port = htons(40001); //服务端端口号
//本地IP信息
Local_addr.sin_family = AF_INET;
Local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地IP
Local_addr.sin_port = htons(50001); //本地端口号
3.把IP地址端口相关信息和套接字相互关联起来。
bind(socketfd_udp_single, (struct sockaddr *)&Local_addr, sizeof(Local_addr));
4.发送数据
int sendlen = sendto(socketfd_udp_single ,temp_buf,sendnum,0,(struct sockaddr*)&Host_addr,addrlen);
5.接收数据
#define MAXDATASIZE 1000
void GetData()
{
char RecvBuf[100];
int i,numbytes;
int RecvLen = 0;
int retUDPsingle=0;
fd_set readsktUDPsingle;
struct timeval time_out;
FD_ZERO(&readsktUDPsingle);
time_out.tv_sec =0;
time_out.tv_usec = 10*1000; //设置超时时间,select遇到读事件或者超时时间到就会解阻
FD_SET(socketfd_udp_single,&readsktUDPsingle);
retUDPsingle = select(socketfd_udp_single+1,&readsktUDPsingle,NULL,NULL,&time_out);
if (retUDPgroup > 0)
{
if(retUDPsingle>0)
{
printf("UDP接收到单播数据\r\n");
/*接收数据*/
RecvLen = recvfrom(socketfd_udp_single, RecvBuf, MAXDATASIZE, 0,NULL,NULL);
}
if ( RecvLen > 0 )
{
printf("recv byte = %d \r\n",RecvLen);
printf("receive: ");
for ( i = 0; i < RecvLen; i++ )
{
printf("%2.2X ",(unsigned char)RecvBuf[i]);
}
printf("\n\n");
}
}
}
单播发送与接收整体代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define HOSTIP "192.168.8.8"
#define HOSTPORT 20000
#define LOCALPORT 50000
int socketfd_udp_single;
struct sockaddr_in Host_addr; //存放服务端IP信息
struct sockaddr_in Local_addr; //存放本地主机IP信息
u8 sendbuff[] = "hello";
int main(int argc, char*argv[])
{
Host_addr.sin_family = AF_INET;
Host_addr.sin_addr.s_addr = inet_addr(HOSTIP);
Host_addr.sin_port = htons(HOSTPORT);
Local_addr.sin_family = AF_INET;
Local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
Local_addr.sin_port = htons(LOCALPORT);
/*创建套接字*/
if ( (socketfd_udp_single = socket(AF_INET, SOCK_DGRAM, 0)) == -1 )
{
perror("Error connecting to socket\n");
return ;
}
else
{
/*绑定端口号,否则本机端口号一直改变*/
bind(socketfd_udp_single, (struct sockaddr *)&Local_addr, sizeof(Local_addr));
}
while(1)
{
GetData();
SendData(sendbuff);
usleep(100*1000);
}
}
void GetData()
{
char RecvBuf[100];
int i,numbytes;
int RecvLen = 0;
int retUDPsingle=0;
fd_set readsktUDPsingle;
struct timeval time_out;
FD_ZERO(&readsktUDPsingle);
time_out.tv_sec =0;
time_out.tv_usec = 10*1000;
FD_SET(socketfd_udp_single,&readsktUDPsingle);
//select函数作用,本质是阻塞,当遇到可读事件或者超时时间到就会解阻,
//select函数,超时时间到返回值为0,遇到可读事件返回值大于0,利用返回值区分是否接受到数据
retUDPsingle = select(socketfd_udp_single+1,&readsktUDPsingle,NULL,NULL,&time_out);
if (retUDPsingle>0)
{
printf("UDP接收到单播数据\r\n");
RecvLen = recvfrom(socketfd_udp_single, RecvBuf, MAXDATASIZE, 0,NULL,NULL);
if ( RecvLen > 0 )
{
printf("recv byte = %d \r\n",RecvLen);
printf("receive: ");
for ( i = 0; i < RecvLen; i++ )
{
printf("%2.2X ",(unsigned char)RecvBuf[i]);
}
printf("\n\n");
}
}
}
void SendData(u8 *sendbuff)
{
int i,sendnum;
int addrlen;
sendnum = sizeof(sendbuff)
if ( sendnum > 0 )
{
addrlen = sizeof(Host_addr);
int sendlen = sendto(socketfd_udp_single,sendbuff,sendnum,0,Host_addr,addrlen);
}
}
多播发送与接收
关于多播,本人理解,多播发送是服务端往组播地址以及对应的端口号发送数据,而客户端从组播地址以及对应端口号读取数据,此处客户端的地址还是本地地址,不要有固定思维,认为多播和单播一样,只需要将本地的IP地址和端口号改为对应多播地址的IP和端口号就行了,这样的想法是错误的,单播和多播有着本质区别。组播地址就像是一个中转机构一样,将服务器的数据转发给客户端。
具体操作步骤如下:
多播发送分为服务器与客户端,服务器主要用来发送组播数据,客户端主要作用是从主播地址接收组播数据,服务器配置相对较简单,下面先来介绍服务器端的配置步骤:
1.创建套接字。
mcastsocketfd = socket(AF_INET, SOCK_DGRAM, 0);
2.定义struct sockaddr_in Mcast_addr变量,存放组播组播地址以及端口号。
struct sockaddr_in mcast_addr; //存放本地IP信息
mcast_addr.sin_family = AF_INET; /*设置协议族类行为AF*/
mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);/*设置多播IP地址*/
mcast_addr.sin_port = htons(MCAST_PORT); /*设置多播端口*/
3.发送数据
sendlen = sendto(socketfd_udp_single ,temp_buf,sendnum,0,(struct sockaddr*)&Host_addr,addrlen);
服务端整体代码:
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#define MCAST_PORT 2000 /*组播地址对应的端口号*/
#define MCAST_ADDR "224.1.0.1" /*一个局部连接多播地址,路由器不进行转发*/
int main(int argc, char*argv)
{
struct sockaddr_in mcast_addr;
int retsend,retrecv;
char sendbuf[9] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
int fd_mcast = socket(AF_INET, SOCK_DGRAM, 0); /*建立套接字*/
if (fd_mcast == -1)
{
perror("socket()");
exit(1);
}
memset(&mcast_addr, 0, sizeof(mcast_addr));
mcast_addr.sin_family = AF_INET; /*设置协议族类行为AF*/
mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR); /*设置多播IP地址*/
mcast_addr.sin_port = htons(MCAST_PORT); /*设置多播端口*/
/*向多播地址发送数据*/
while(1)
{
int n = sendto(fd_mcast,sendbuf,sizeof(sendbuf),0,(struct sockaddr*)&mcast_addr,sizeof(mcast_addr)) ;
if( n < 0)
{
perror("sendto()");
exit(1);
}
sleep(1); /*1s发送一次数据*/
}
return 0;
}
客户端的配置步骤相对于服务端,略微麻烦些,服务端是往多播组内发送数据,因此客户端必须先加入多播组之后,才可以接收组播数据。
下面为组播客户端的配置步骤:
1.创建套接字。
mcastsocketfd = socket(AF_INET, SOCK_DGRAM, 0);
2.定义struct sockaddr_in Local_addr变量,存放本地IP以及端口号(注意此处的端口号不同于单播,而是组播端口号)
struct sockaddr_in Local_addr; //存放本地IP信息
//本地IP信息
Local_addr.sin_family = AF_INET;
Local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地IP
Local_addr.sin_port = htons(Mcast_port); //组播端口号
3.把IP地址端口相关信息和套接字相互关联起来,但是在绑定之前最好需要采用socket端口复用的选项,因为当迅速关闭再重新创建socket之后bind会出错,根据网上资料,socket关闭后释放端口号需要一段延时,此问题的解决办法:采用socket端口复用的选项。
//采用socket端口复用
int yes = 1;
if(setsockopt(mcastsocketfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))<0)
{
printf("reusing ADDR failed\r\n");
}
//绑定socket
int err = bind(mcastsocketfd,(struct sockaddr*)&Mcast_addr, sizeof(Mcast_addr)) ;
if(err < 0)
{
perror("bind()");
exit(1);
}
4.设置循环许可
int loop = 1;
int err = setsockopt(mcastsocketfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
if(err<0)
{
printf("setsockeopt():IP_MULTICAST_LOOP\r\n");
}
5.将本机加入多播组
struct ip_mreq mreq;
memset (&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(_Mcast_addr); //多播地址
mreq.imr_interface.s_addr = htonl(INADDR_ANY); //本机地址
int err = setsockopt(mcastsocketfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
if(err<0)
{
perror("setsockopt():IP_ADD_MEMBERSHIP");
}
注意:有的时候加入多播组会失败,会显示no such device,这是因为本地路由表内没有加入组播地址
解决办法:输入命令 route add -net 224.1.0.1 netmask 255.255.255.255 eth1"),或者直接在加入组播组之前,先加一条代码:
system("route add -net 224.1.0.1 netmask 255.255.255.255 eth1");
其中224.1.0.1为多播组地址,需要更改为你对应的多播组地址,eth1为网口2,也需要更改为你对应的网口。
6.接收数据
void GetData()
{
char RecvBuf[MAXDATASIZE];
int i,numbytes;
int RecvLen = 0;
int ret,retUDPgroup,retUDPsingle=0;
int maxfd;
int addr_len;
fd_set readskt,readsktUDPgroup,readsktUDPsingle;
struct timeval time_out;
FD_ZERO(&readsktUDPgroup);
time_out.tv_sec =0;
time_out.tv_usec = 10*1000;
FD_SET(mcastsocketfd,&readsktUDPgroup);
//此处selset需要特别说明一下,如同上述单播接收一样,也是用来解阻,此处就不多加赘述。
retUDPgroup = select(mcastsocketfd+1,&readsktUDPgroup,NULL,NULL,&time_out);
if (retUDPgroup>0)
{
printf("UDP接收到多播数据\r\n");
addr_len = sizeof(Mcast_addr);
RecvLen = recvfrom(mcastsocketfd, RecvBuf, MAXDATASIZE, 0,(struct
sockaddr*)&Mcast_addr,(socklen_t*)&addr_len);
if ( RecvLen > 0 )
{
for ( i = 0; i < RecvLen; i++ )
{
printf("%2.2X ",(unsigned char)RecvBuf[i]);
}
printf("\n\n");
}
}
}
客户端总体代码为:
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#define _Mcast_port 2000
#define _Mcast_addr "224.1.0.1"
int mcastsocketfd;
int main(int argc, char*argv)
{
struct sockaddr_in Local_addr; //存放本地IP信息
//创建套接字
mcastsocketfd = socket(AF_INET, SOCK_DGRAM, 0);
//本地IP信息
Local_addr.sin_family = AF_INET;
Local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地IP
Local_addr.sin_port = htons(_Mcast_port); //组播端口号
//采用socket端口复用
int yes = 1;
if(setsockopt(mcastsocketfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))<0)
{
printf("reusing ADDR failed\r\n");
}
//绑定socket
int err = bind(mcastsocketfd,(struct sockaddr*)&Mcast_addr, sizeof(Mcast_addr)) ;
if(err < 0)
{
perror("bind()");
exit(1);
}
//设定循环许可
int loop = 1;
err = setsockopt(mcastsocketfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
if(err<0)
{
printf("setsockeopt():IP_MULTICAST_LOOP\r\n");
}
//将本机地址加入多播组
struct ip_mreq mreq;
memset (&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(_Mcast_addr); //多播地址
mreq.imr_interface.s_addr = htonl(INADDR_ANY); //本机地址
err = setsockopt(mcastsocketfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
if(err<0)
{
perror("setsockopt():IP_ADD_MEMBERSHIP");
}
//读取数据
while(1)
{
getData();
usleep(10*1000);
}
}
void GetData()
{
char RecvBuf[MAXDATASIZE];
int i,numbytes;
int RecvLen = 0;
int ret,retUDPgroup,retUDPsingle=0;
int maxfd;
int addr_len;
fd_set readskt,readsktUDPgroup,readsktUDPsingle;
struct timeval time_out;
FD_ZERO(&readsktUDPgroup);
time_out.tv_sec =0;
time_out.tv_usec = 10*1000;
FD_SET(mcastsocketfd,&readsktUDPgroup);
//此处selset需要特别说明一下,如同上述单播接收一样,也是用来解阻,此处就不多加赘述。
retUDPgroup = select(mcastsocketfd+1,&readsktUDPgroup,NULL,NULL,&time_out);
if (retUDPgroup>0)
{
printf("UDP接收到多播数据\r\n");
addr_len = sizeof(Mcast_addr);
RecvLen = recvfrom(mcastsocketfd, RecvBuf, MAXDATASIZE, 0,(struct
sockaddr*)&Mcast_addr,(socklen_t*)&addr_len);
if ( RecvLen > 0 )
{
for ( i = 0; i < RecvLen; i++ )
{
printf("%2.2X ",(unsigned char)RecvBuf[i]);
}
printf("\n\n");
}
}
}