linux下的UDP广播通讯,客户端和服务器实现

2023-05-02 15:44:23 浏览数 (1)

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=33nqakp1y9esg

一、理解广播地址:

专门用于同时向网络中所有工作站进行发送的一个地址叫做广播地址。在使用TCP/IP 协议的网络中,主机标识段host ID 为全1 的IP 地址为广播地址。如果你的IP为:192.168.1.39,子网掩码为:255.255.255.0,则广播地址为:192.168.1.255;如果IP为192.168.1.39,子网掩码为:255.255.255.192,则广播地址为:192.168.1.63(255-192)(来源:https://blog.csdn.net/wangquan1992/article/details/89890816)

1、本地受限广播

如果想在整个网络中广播数据,要向255.255.255.255发送数据包,这种数据包不会被路由,它只能到达本物理网络中的所有主机,此种广播叫有限广播;

简单理解,就是交换机内连接的设备,都能接收到该广播消息,无论该设备的IP如何配置;

2、直接广播,定向广播;

如果只想在本网络内广播数据(假设本网广播地址192.168.1.255),只要向192.168.1.255发送数据包即可,这种数据包可以被路由,它会经由路由器到达本网段内的所有主机,此种广播也叫直接广播,直接广播也可以向指定网段进行广播,前提是指定目标网段(x.x.x.255);

二、如何实现广播发送和接收;

UDP发送端,需要配置发送广播消息的选项:

代码语言:javascript复制
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
                      
参数说明
level:
SOL_SOCKET
optname:
SO_BROADCAST 允许发送广播数据包
SO_RCVBUF 接收缓冲区大小
SO_SNDBUF 发送缓冲区大小  

/设置该套接字为广播类型  
int opt=1;  
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));

参考代码:

代码语言:javascript复制
//通过广播地址:4001端口发送给所有监听4001端口的设备
int send_config_data_from_broadcast(shared_ptr<string> json_str){

    struct sockaddr_in b_addr;
    
    int socked=socket(AF_INET,SOCK_DGRAM,0);
    if(socked<0)
    {
        perror("sock failed");
        return 1;
    }
    int yes=1;
    if(setsockopt(socked,SOL_SOCKET,SO_BROADCAST,&yes,sizeof(yes))<0)
    {
        perror("setsockopt failedn");
        return 2;
    }
    
    b_addr.sin_family=AF_INET;
    b_addr.sin_addr.s_addr=inet_addr("255.255.255.255");
    b_addr.sin_port=htons(4001);
    
    int b_addr_len=sizeof(b_addr);
    printf("nrsend %s from broadcast.nr", json_str->c_str());
    int send_len = sendto(socked, json_str->c_str(), json_str->length(), 0,(struct sockaddr *)&b_addr, b_addr_len);
    if (send_len < 0) {
        printf("nrsend error.nr");
        exit(EXIT_FAILURE);
    }
    printf("send success %d.nr",send_len);
    return 0; 
}

UDP服务器端,需要接收广播消息,这里和普通UDP的服务器就可以接收到广播消息!

参考代码1:

代码语言:javascript复制
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netdb.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<arpa/inet.h>
int main(){
	int sockListen;
	if((sockListen = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
		printf("socket failn");
		return -1;
	}
	int set = 1;
	setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(int));
	struct sockaddr_in recvAddr;
	memset(&recvAddr, 0, sizeof(struct sockaddr_in));
	recvAddr.sin_family = AF_INET;
	recvAddr.sin_port = htons(4001);
	recvAddr.sin_addr.s_addr = INADDR_ANY; 
	if(bind(sockListen, (struct sockaddr *)&recvAddr, sizeof(struct sockaddr)) == -1){
		printf("bind failn");
		return -1;
	}
	int recvbytes;
	char recvbuf[1024] = "";
	int addrLen = sizeof(struct sockaddr_in);
	if((recvbytes = recvfrom(sockListen, recvbuf, sizeof(recvbuf) - 1, 0,
		(struct sockaddr *)&recvAddr, &addrLen)) != -1){
		recvbuf[recvbytes] = '';
		printf("receive a broadCast messgse:%sn", recvbuf);
	}else{
		printf("recvfrom failn");
	}
	close(sockListen);
	return 0;
}

参考代码2:

代码语言:javascript复制
static int init_local_socket(int port){

    struct sockaddr_in servaddr; 
    int local_sockfd = 0; 
       
  
    // Creating socket file descriptor 
    if ( (local_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
        printf("socket creation failed"); 
        return (-1); 
    } 
    int optval = 0;
    if( setsockopt( local_sockfd , SOL_SOCKET, SO_REUSEPORT,  ( char *)&optval, sizeof( optval ) ) < 0 ){
        printf( " set socket reuseport error /n" ); 
    }
        
    memset(&servaddr, 0, sizeof(servaddr)); 
        
    // Filling server information 
    servaddr.sin_family    = AF_INET; // IPv4 
    servaddr.sin_addr.s_addr =  INADDR_ANY; 
    servaddr.sin_port = htons(port); 
        
    // Bind the socket with the server address 
    if ( bind(local_sockfd, (const struct sockaddr *)&servaddr,  
            sizeof(servaddr)) < 0 ) 
    { 
        printf("bind creation failed"); 
        return -1;
    } 
    LOGD("init_local_socket create port:%d, fd:%drn", port, local_sockfd); 
    return local_sockfd;
}


void start_local_listen(){
    struct sockaddr_in cliaddr; 
    int cliaddrlen;

    char buffer[MAXLINE];  
         
    int ret = 0;
    int len_recv = 0;
    
    //初始化本地socket
    ret = init_local_socket(LOCAL_SINGLE_COMMAND_LISTENER_PORT);
    if (ret == -1){
    	return;
    }
    local_command_sockfd = ret;

    memset(&cliaddr, 0, sizeof(cliaddr)); 
    cliaddrlen = sizeof(cliaddr);  //len is value/result 
    
    while(running_local_port_udpserver_flag){ 
  		shared_ptr<P2PBroadcastCommand> req_message;
        memset(buffer, 0x00, MAXLINE);
		ret = recv_with_timeout(local_command_sockfd, 
			buffer, 
			MAXLINE-1, 
			&len_recv, 
			&cliaddr, 
			&cliaddrlen);
	    if (ret != SOCKET_RECV_SUCCESS){
            sleep_ms(10);
	    	continue;
	    }
        if (len_recv == -1){
	    	printf("len_recv %d: errno:%dn", len_recv, errno); 
	    	break;
	    }
	    printf("Client %d: %sn", len_recv, buffer); 

	    //解析json ,send response
	    ret = parse_json_str(buffer, req_message);
	    if (ret == -1){
	    	continue;
	    }
	    do_cmd_handler(req_message, &cliaddr, cliaddrlen);
    }
}

static int recv_with_timeout(int socketfd, 
	char *buffer,
	int buffer_len, 
	int *len_recv, 
	struct sockaddr_in *cliaddr, 
	int *cliaddrlen){
    fd_set rset; 
    int len = 0;

    FD_ZERO(&rset); 
    FD_SET(socketfd, &rset);
    int maxfdp1 = socketfd   1;
    struct timeval tv = { 0 };
    tv.tv_sec = 0;
    tv.tv_usec = 10000000000;//100ms?

    select(maxfdp1, &rset, NULL, NULL, &tv);
    if (!FD_ISSET(socketfd, &rset)){
          //LOGD( "timeout no data received");
          //sleep_ms(10);
          return SOCKET_RECV_TIMEOUT;
    }

    len = recvfrom(socketfd, (char *)buffer, 
    	buffer_len-1,    
    	0, 
    	(struct sockaddr *) cliaddr,   
    	(socklen_t*)cliaddrlen); 
    buffer[len] = ''; 
    *len_recv = (int)len;

    if (len == -1 ) {  
    	LOGD("recv failed errno:%d",errno); 
    }
    return SOCKET_RECV_SUCCESS;
}

0 人点赞