Linux网络编程实践四(UDP广播)

题目:

编程实践4设计开发广播服务器,向服务器所在子网广播消息。服务器程序定时从广播信息文件读取需要广播的信息,按照信息指定发布时间将信息广播出去。设计开发客户端,接收广播消息,打印输出到显示器。

要求使用C/C++编成,提交程序源代;程序功能完整正确,源代码格式化良好、适当注释、模块划分合理。

测试效果截图:

        因为按指定发布时间将信息广播要等待的时间比较长,不方便测试,所以我的广播信息文件是这样写的:

        冒号(:)前面的数字用来代表时间,单位是秒。冒号后面是要广播的信息(会去掉空格)。若冒号前面的数字是n,那么在发送一条广播信息后,服务端会暂定n秒(使用sleep函数)。

        另外,因为广播的时候,客户端要绑定端口,一般一个端口只能有一个程序绑定,这样广播只能是一对一的,所以我在客户端设置广播的时候,同时设置了端口复用,这样就可以有多个客户端接收到服务端发送的消息:

        下面开始正式测试

        如图,客户端收到消息,会显示收到的时间。为了方便测试,我设置了服务端不断读取广播文件进行广播,每次广播间隔了5s。每次广播结束后会显示本次广播的时间。

        对比一下广播文件和客户端收到信息的时间:

        可以看到,客户端每次收到消息的时间与接收文件的时间一致。

        接下来运行多个客户端进行测试:

        可以看到,因为设置了端口复用,是可以对多个客户端进行广播的。

        测试完毕。

程序源代码:

服务端代码:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>	
#include <string.h>	
#include <netinet/in.h>
#include <unistd.h>	
#include <arpa/inet.h>
#include <pthread.h>	
#include <stdbool.h>
#include <sys/stat.h>
#include <fcntl.h>

#define broadcast_ip "192.168.110.255"//设置广播地址,必须为本机/虚拟机的广播地址才行
#define broadcast_port 8080 //设置广播端口
#define buff_len 1024	//设置缓冲区长度
#define Maxline 1024	//设置最大读取文件行数

char buffer[buff_len];//缓冲区

int get_time();//将buffer前面的数字提取出来,作为暂停时间
void get_char();//从buffer中要发送的信息提取出来,

int main(int argc,void *argv[])
{
	int err;	//用来返回错误信息
	int t;		//用来表示每次发送信息后暂停的时间(单位是秒)
	struct sockaddr_in broadcast_addr;  //创建一个地址结构
	char message[buff_len]={0};//用来发送消息
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);   //创建套接字
	if(sockfd < 0){	/*出错*/
		printf("socket error");
		return -1;
	}

	//开启广播
	int opt =1;
	err = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));//将套接字设置为广播模式
	if(err < 0) {	/*出错*/
		printf("setsockopt error");
		return -1;
	}
	
	memset(&broadcast_addr, 0, sizeof(broadcast_addr)); //初始化为0
	broadcast_addr.sin_family = AF_INET;//初始化地址族IPV4
	broadcast_addr.sin_port = htons(broadcast_port); //设置端口号(网络字节序号)
	broadcast_addr.sin_addr.s_addr = inet_addr(broadcast_ip);//初始化绑定地址, 用INADDR_ANY--表示绑定本机地址
	int k=1;
	//读取文件发送数据
	while(1){
		FILE* fp=fopen("test.txt","r");//以只读的方式打开文件
		if(fp == NULL){
			printf("failed to open file\n");
			break;
		}
		int time_sum=0;//记录本次广播所花的时间
		printf("------第 %d 次广播------\n",k);
		while(fgets(buffer,Maxline,fp)){//以行为单位读取文件并将数据发送到客户端
			t=get_time(buffer);
			time_sum+=t;
			//strcpy(message,get_char(buffer));
			get_char();
			strcpy(message,buffer);
			err = sendto(sockfd, message, sizeof(message), 0, (struct sockaddr*)&broadcast_addr, sizeof(broadcast_addr));
			if(err <0){
				printf("sendto error");	//发送错误,关闭连接后结束程序
				//close(sockfd);
				continue;
			}
			memset(buffer,0,buff_len);//清空缓存区
			memset(message,0,buff_len);
			sleep(t);	//暂停ts
		}
		fclose(fp);	//关闭文件指针
		k++;
		printf("本次广播共花时间%d秒\n",time_sum);
		sleep(5);//暂停5s
	}
	close(sockfd);	//关闭套接字
	return 0;
}

int get_time(char s[])//将一行字符串前面的数字提取出来,作为暂停时间
{
	int t=0;
	for(int i=0;i<strlen(s);i++)
		if(s[i]>='0'&&s[i]<='9') t=t*10+s[i]-'0';
		else if(s[i]==':') break;
	return t;
}

void get_char()//将buffer中要发送的信息提取出来
{
	char s[buff_len];
	strcpy(s,buffer);
	memset(buffer,0,buff_len);//清空
	int i=0,j=0;
	while(s[i]!=':')i++;
	i++;
	while(s[i]==' ')i++;
	for(;i<strlen(s);i++)buffer[j++]=s[i];
	//printf("%s\n",buffer);
}

客户端代码:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <semaphore.h>
#include <stdbool.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <time.h>
#define MAXSIZE 100

#define broadcast_port 8080 //设置广播端口
#define buff_len 1024	//设置缓冲区长度

int main(void)
{
	int err; //错误信息
	char buffer[buff_len]={0},message[buff_len]={0};//创建数据缓冲区
	struct sockaddr_in client_addr;  //客户端地址结构
	struct sockaddr_in server_addr;	//服务端地址结构
	socklen_t len = sizeof(server_addr);  //服务端地址结构长度
	struct tm *p;
	time_t t;	//用于显示当前时间
	time(&t);
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);//创建套接字 UDP--SOCK_DGRAM
	if(sockfd < 0){/*出错*/
		printf("socket error\n");
		return -1;
	}
	
	//设置端口复用SO_REUSEADDR
	int opt = 1;
	err  = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	if(err < 0) {	/*出错*/
		printf("setsockopt error");
		return -1;
	}
	
	memset(&client_addr, 0, sizeof(client_addr)); //初始化为0
	client_addr.sin_family = AF_INET;//初始化地址族IPV4
	client_addr.sin_port = htons(broadcast_port); //设置端口号(网络字节序号)
	client_addr.sin_addr.s_addr = INADDR_ANY;//初始化绑定地址, 用INADDR_ANY--表示绑定本机地址
	
	err = bind(sockfd, (struct sockaddr*)&client_addr, sizeof(client_addr));//地址绑定
	if(err < 0){/*出错*/
		printf("bind error\n");
		return -1;
	}
	
	//接收数据
	while(1){
		memset(buffer,0,sizeof(buffer));//清零
		err = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &len);
		if(err < 0){
				printf("recvfrom error\n");
				close(sockfd);
				return -1;
		}
		time(&t);//获取当前时间
		p=gmtime(&t);//
		//构造时间进行输出
		sprintf(message,"%d-%d-%d %02d:%02d:%02d", 1900+p->tm_year,1+p->tm_mon, p->tm_mday,8+p->tm_hour,p->tm_min, p->tm_sec);
		printf("当前时间:%s,接收到消息:\n",message);
		printf("%s\n", buffer);
	}
	
	//关闭套接字
	close(sockfd);
	return 0;
}

广播信息文件test.txt的内容:

1:hello,client,i am server
2:welcome to udp broadcast
3:this is a test!
4:yes,hhhhhhhhhh
4:you don't need to reply
3:just a test
5:    don't worry

结语

        这一次的实践比较简单,但是我还是做了好久,做做停停,不在状态。不然可以半天搞定的。

        测试是在虚拟机上测试的,然后用QQ截的图,不知道为啥有点模糊,大概能看就行。

        没想到,慢慢吞吞做的还是被老师拿来点评了,老师说我没有按题目要求去做啊,题目要求是按指定的时间去发送消息,你却按暂停的时间来发送。

        老师还说我能想到端口复用这个点说明我确实去做了,发现问题并解决问题,但是广播的一对多中的多并不是指一台主机上的多个程序,而是指多个主机,所以实际上是不需要端口复用的。

        嗯,虽然没有按题目完成,但是老师还是给了我挺高分的,谢谢老师(><)

        最后说的那个时间的问题,我没有处理,因为当时已经提交了(不能重复提交),我又比较懒,所以就没改。大家感兴趣的去解决一下吧~

猜你喜欢

转载自blog.csdn.net/xiexieyuchen/article/details/129815733