トピック:
プログラミングの実践 4 :サーバーが配置されているサブネットにメッセージをブロードキャストするブロードキャスト サーバーを設計および開発します。サーバープログラムは、定期的に放送情報ファイルから放送が必要な情報を読み込み、指定された情報の公開時刻に合わせて放送を行います。クライアントを設計および開発し、ブロードキャスト メッセージを受信し、モニターに出力します。
プログラムのソースコードを。プログラムの機能は完全かつ正確で、ソース コードは適切にフォーマットされ、適切にコメントされており、モジュール分割は適切です。
テスト効果のスクリーンショット:
指定した公開時刻に従って情報が放送されるまで待機するのに時間がかかるため、テストするのが不便なので、私の放送情報ファイルは次のように記述されています。
コロン (:) の前の数字は時間を秒単位で表すために使用されます。コロンの後にはブロードキャストする情報が入ります (スペースは取り除かれます)。コロンの前の数字が n の場合、ブロードキャスト メッセージの送信後、サーバーは n 秒間一時的に停止します (スリープ機能を使用)。
また、クライアントはブロードキャスト時にポートをバインドする必要があるため、一般に 1 つのポートは 1 つのプログラムにのみバインドできるため、ブロードキャストは 1 対 1 にしかできません。そのため、クライアントでブロードキャストを設定するときに、ポート多重化を使用すると、複数のクライアントがサーバーから送信されたメッセージを受信できるようになります。
正式なテストを始めましょう
図に示すように、クライアントがメッセージを受信すると、受信時刻が表示されます。テストの便宜上、ブロードキャスト用のブロードキャスト ファイルを継続的に読み取るようにサーバーを設定し、各ブロードキャストの間隔は 5 秒にしました。各放送終了後、その放送の時間が表示されます。
ブロードキャスト ファイルとクライアントが情報を受信した時刻を比較します。
クライアントがメッセージを受信した時刻が、ファイルを受信した時刻と一致していることがわかります。
次に、テストのために複数のクライアントを実行します。
ポート多重が設定されているため、複数のクライアントにブロードキャストできることがわかります。
テストは完了です。
プログラムのソースコード:
サーバーコード:
#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:こんにちは、クライアント、私はサーバーです
2:UDP ブロードキャストへようこそ
3:これはテストです!
4:はい、ああああああ
4:返信する必要はありません
3:ただのテスト
5:心配しないでください
エピローグ
今回の練習は比較的簡単ですが、それでも状態ではなく、オンとオフを繰り返して長時間行いました。それ以外の場合は半日で完了します。
テストは仮想マシンでテストされ、QQ を使用して写真をキャプチャしました。少しぼやけている理由はわかりませんが、おそらく見えると思います。
思いがけず、私がゆっくりやったのに、先生から「テーマの要件に従ってやっていない」と指摘されました。
先生はまた、ポート多重化について考えられるとも言いました。つまり、私がそれを実行し、問題を見つけて問題を解決したことを意味しますが、ブロードキャストの 1 対多の多とは、1 つのホスト上の複数のプログラムを指すのではなく、複数のプログラムを指します。ホストなので、実際にはポート多重化は必要ありません。
そうですね、課題はクリアできなかったのですが、それでも先生は高得点をくれました、先生ありがとう(><)
最後に挙げた時間の問題は、その時点ですでに提出されていた(繰り返し提出できない)ことと、私が怠け者だったので変更しませんでした。誰もがそれを解決することに興味があります~