计算机网络课程设计智能家居协议设计
一、项目描述
实现一个基于socket的物联网服务器(上位机),接收来自客户端的数据。实现一个物联网客户端,模拟设备。下位机连接各种设备,采集设备数据,发送至服务器,服务器根据协议对数据作出处理。
需要设置的设备有:
温度传感器:采集房间温度信息。
湿度传感器:采集房间湿度信息。
灯光:开关灯(多路),调节灯亮度(多路)
空调:可以根据服务器发来的指令进行调节模式(制冷、制热、送风)、设置温度、风速;还可以根据服务器的指令上传子自己的当前状态:模式、温度、风速。
门窗磁:可以根据服务器发来的指令打开或者关闭窗户。
插卡取电:插入卡表示有人进入,拔下卡表示房间无人,根据房间状态控制空调灯光
上位机根据下位机传来的数据更新设备状态并显示。
用户可以在上位机修改设备(空调、灯光)的状态,将命令下发给下位机,下位机收到后调整设备。
通信规则:
下位机10秒上传一次心跳,上位机收到后进行答复,答复数据中包含自己的时间;
下位机5秒上传设备信息(温度、湿度、灯光、空调、门窗、插卡取电),上位机收到后进行答复,答复数据中包含自己的时间。
二、协议的设计
功能 | 长度 | 解释 |
---|---|---|
固定字 | 3 | ABC |
插卡取电 | 1 | 0:无电 1:有电 |
调节空调 | 1 | 0:关闭空调1:温度2:风速3:模式 |
调节灯光 | 1 | 0:不调1:调节1号灯2:调节2号灯3:调节3号灯4:调节4号灯5:调节5号灯 |
温度值 | 2 | 显示温度 |
湿度 | 2 | 显示湿度 |
灯光亮度 | 5 | 一位显示一个灯 |
空调模式 | 1 | 0:关闭状态1:制热2:制冷3:送风 |
风速 | 1 | 显示风速123 |
门窗 | 1 | 0:关闭1:打开 |
目标温度 | 2 | 调节过后的温度 |
目标湿度 | 2 | 调节过后的湿度 |
日志 | 6 | 1-2位:时,3-4位分,5-6位秒 |
三、项目实现
1.server.cpp(服务端)
先运行server.cpp.
#include<iostream>
#include<stdio.h>
#include <conio.h>
#include<Winsock2.h>
#include<fstream>
#include <ctime>
#include<cstdio>
#define N 10
using namespace std;
#include<windows.h>
#include <cstring>
#include <string>
#define N 10
#define random(a,b) (rand()%(b-a)+a)
#pragma comment(lib, "WS2_32")
#pragma comment(lib, "ws2_32.lib")
char recv_buf[100];
char send_buf[100];
char love[200]; //心跳数据报
void print(char *s)
{
int key=s[3]-'0';
int kongtiao1=s[15]-'0';
int kongtiao2=s[16]-'0';
int window=s[17]-'0';
int num1=s[6]-'0';
int num2=s[7]-'0';
int tem=num1*10+num2;
num1=s[8]-'0';
num2=s[9]-'0';
int shidu=num1*10+num2;
int i;
time_t tt = time(NULL);//这句返回的只是一个时间cuo
tm* t= localtime(&tt);
printf("当前的时间为%02d:%02d:%02d\n",
t->tm_hour,
t->tm_min,
t->tm_sec);
cout<<"------------------------------------"<<endl;
if(key==0)
cout<<"未插卡..."<<endl;
else
cout<<"读卡中..."<<endl;
cout<<"收到来自温度传感器的数据: "<<tem<<"° "<<endl;
cout<<endl;
cout<<"收到来自湿度传感器的数据: "<<shidu<<"% "<<endl;
if(kongtiao1==0)
cout<<"空调模式:关"<<endl;
else if(kongtiao1==1)
cout<<"空调模式:制热"<<endl;
else if(kongtiao1==2)
cout<<"空调模式:制冷"<<endl;
else if(kongtiao1==3)
cout<<"空调模式:送风"<<endl;
if(kongtiao2==0)
cout<<"空调风速:关"<<endl;
else if(kongtiao2==1)
cout<<"空调风速:弱风"<<endl;
else if(kongtiao2==2)
cout<<"空调风速:中风"<<endl;
else if(kongtiao2==3)
cout<<"空调风速:强风"<<endl;
for(i=10;i<15;i++)
{
if(s[i]=='0')
cout<<"灯-"<<i-9<<":关"<<endl;
else if(s[i]=='1')
cout<<"灯-"<<i-9<<":柔和"<<endl;
else if(s[i]=='2')
cout<<"灯-"<<i-9<<":经典"<<endl;
else if(s[i]=='3')
cout<<"灯-"<<i-9<<":强光"<<endl;
}
if(window==0)
cout<<"门窗模式:关"<<endl;
else
cout<<"门窗模式:开"<<endl;
cout<<"\n------------------------------------"<<endl;
cout<<endl;
return ;
}
int main()
{
WORD wsa = MAKEWORD(2,2);
WSADATA wsaData;
if(WSAStartup(wsa, &wsaData)!=0)
{
cout<<"初始化失败"<<endl; //这进行初始化
return 0;
}
//创建套接字
SOCKET s_server = socket(AF_INET, SOCK_STREAM, 0);
//AF_INET 面向网络的 IPV4
//AF_INET6用于第6版因特网协议(IPv6)寻址
//SOCK_STREAM TCP链接 面向链接 SOCK_DGRAM 无连接 UDP
if(s_server==INVALID_SOCKET) //INVALID_SOCKET是-1 错误标志
{
cout<<"socket创建失败"<<endl;
return 0;
}
//这里配置server配置信息
sockaddr_in server_addr; //创建sockaddr_in结构体
server_addr.sin_family = AF_INET; //IPV4协议
server_addr.sin_port = htons(8888); //本地端口8888
server_addr.sin_addr.S_un.S_addr = INADDR_ANY; //设置地址 监听本地端口
//利用bind函数将socket和配置信息绑定
//bind通常和下面的listen配套使用 处于被动链接中泰
if ( bind( s_server,(SOCKADDR *)&server_addr,sizeof(SOCKADDR)) ==SOCKET_ERROR )
{
//socket和配置绑定
cout<<"ERROR bind!!"<<endl; //绑定错误
WSACleanup();
}
if(listen( s_server,5 )<0)
{
cout<<"监听失败"<<endl;
}
cout<<"监听链接请求~~~"<<endl;
sockaddr_in clinet_addr; //用于获取客户端地址端口信息 用于回传
SOCKET s_accept;
//这里定义了一个用于接收的sockaddr_in名字为s_accept。用于接收客户端的地址信息和端口信息,用于回传。
int len=sizeof(SOCKADDR);
s_accept = accept(s_server,(SOCKADDR *)&clinet_addr,&len);
//在这里阻塞 挂起等待
if( s_accept ==SOCKET_ERROR ) //
{
cout<<"connect error"<<endl;
return 0;
}
cout<<"connection is OK;ready to accept"<<endl;
int count1=0;
while(1)
{
int recv_len= recv(s_accept,recv_buf,100,0);
//这里 即使链接断开 也能继续监听 服务端不关闭
if(recv_len<0)
{
cout<<"服务器继续监听~~~~"<<endl;
s_accept = accept(s_server,(SOCKADDR *)&clinet_addr,&len);
//客户端断开阻塞,挂起等待。在这里阻塞 挂起等待
continue;
if( s_accept ==SOCKET_ERROR ) //
{
cout<<"connect error"<<endl;
return 0;
}
}
char recv_message[100]; //接收到的数据帧
strcpy(recv_message,recv_buf);
int message_len = strlen(recv_message); //接收到的数据帧的长度
count1++;
if(count1%2==0)
{
cout<<"心跳:"<<endl;
recv(s_accept,love,100,0);
cout<<love<<endl<<endl;
}
print(recv_message);
int key;
int control_k;
int control_d;
int wendu;
int shidu;
int deng[N];
int kongtiao1;
int kongtiao2;
int window;
string str0="ABC";
key=1;
control_k=4;
control_d=0;
int num1=recv_message[6]-'0';
int num2=recv_message[7]-'0';
wendu=num1*10+num2;
num1=recv_message[8]-'0';
num2=recv_message[9]-'0';
shidu=num1*10+num2;
int i;
for(i=10;i<15;i++)
deng[i-10]=recv_message[i]-'0';
kongtiao1=recv_message[15]-'0';
kongtiao2=recv_message[16]-'0';
if(wendu>=18)
window=1;
else
window=0;
int dwendu=wendu;
int dshidu=shidu;
if(key==1)
{
if(wendu<18)
{
dwendu=18;
kongtiao1=1;
}
else if(wendu>25)
{
dwendu=25;
kongtiao1=2;
}
else
{
dwendu=wendu;
kongtiao1=3;
}
if(shidu<40)
{
dshidu=40;
kongtiao2=3;
}
else if(shidu>50)
{
dshidu=50;
kongtiao2=2;
}
else
{
dshidu=shidu;
kongtiao2=1;
}
}
string str1=to_string(key);
string str2=to_string(control_k);
string str3=to_string(control_d);
string str4=to_string(wendu);
string str5=to_string(shidu);
string str7=to_string(kongtiao1);
string str8=to_string(kongtiao2);
string str9=to_string(window);
string str10=to_string(dwendu);
string str11=to_string(dshidu);
cout<<"可以进行人工控制!!!"<<endl;
cout<<"空调1温度/2模式/3风速 灯光(1-5)"<<endl;
string s,t;
Sleep(3*1000);
if(kbhit())
{
cin>>s;
if(s[0]=='1')
{
cout<<"设置温度:"<<endl;
cin>>t;
str10=t;
}
else if(s[0]=='2')
{
cout<<"设置模式:"<<endl;
cin>>t;
str7=t;
}
else if(s[0]=='3')
{
cout<<"设置风速:"<<endl;
cin>>t;
str8=t;
}
cout<<"设置亮度:"<<endl;
cin>>t;
int m=s[1]-'0';
deng[m]=t[0]-'0';
}
string str6=to_string(deng[0]);
for(i=1;i<5;i++)
str6+=to_string(deng[i]);
time_t tt = time(NULL);//这句返回的只是一个时间cuo
tm* a= localtime(&tt);
string strt1=to_string(a->tm_hour);
if(a->tm_hour<10)
strt1='0'+strt1;
string strt2=to_string(a->tm_min);
if(a->tm_min<10)
strt2='0'+strt2;
string strt3=to_string(a->tm_sec);
if(a->tm_sec<10)
strt3='0'+strt3;
string str=str0+str1+str2+str3+str4+str5+str6+str7+str8+str9;
str=str+str10+str11;
str=str+strt1+strt2+strt3;
int j=0;
memset(send_buf,0,sizeof(send_buf));
for(int i=0; i<str.length(); i++)
{
send_buf[j]=str[i];
j++;
}
cout<<send_buf<<endl;
send(s_accept,send_buf,100,0);
cout<<"发送成功"<<endl<<endl;
}
closesocket(s_server);
closesocket(s_accept);
//释放DLL资源
WSACleanup();
return 0;
}
2.client.cpp(客服端)
#include<iostream>
#include<stdio.h>
#include<Winsock2.h>
#include<windows.h>
#include <ctime>
#include <cstring>
#include <string>
#define N 10
#define random(a,b) (rand()%(b-a)+a)
#pragma comment(lib, "WS2_32")
using namespace std;
char send_buf[200]; //发送数据报
char accept_buf[200]; //接受数据报
char love[200]; //心跳数据报
void tcpxy(); //生成上传服务器报文
void analysis(char receive[]); //对服务器传回的报文进行解析
void get(int n);
int main()
{
WORD wsa = MAKEWORD(2,2);
WSADATA wsaData;
//WSADATA结构体中主要包含了系统所支持的Winsock版本信息 WSA API
if(WSAStartup(wsa, &wsaData)!=0)
{
cout<<"初始化失败"<<endl; //这进行初始化
return 0;
}
SOCKET s_server = socket(AF_INET, SOCK_STREAM, 0);
if(s_server==INVALID_SOCKET)
{
cout<<"socket创建失败"<<endl;
return 0;
}
sockaddr_in server_addr;
server_addr.sin_family = AF_INET; //IPV4协议
server_addr.sin_port = htons(8888); //服务器端口
server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.56.1"); //服务器IP,改成自己电脑的IP
if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) //链接失败
{
cout << "server connnet fail!" << endl;
WSACleanup();
return 0;
}
else
{
cout << "server connnet succeed!" << endl<<endl;
}
Sleep(1*1000);
cout<<"开始上传温度和湿度传感器信息..."<<endl<<endl;
int count1=0,count2=2;
while(1)
{
count1++;
cout<<"上传服务器报文为:"<<endl;
tcpxy();
Sleep(2*1000);//延时3秒
int res_len = send(s_server,send_buf,100,0);
if(res_len<0)
{
cout<<"error"<<endl;
return 0;
}
if(count1%2==0)
{
get(count2%2);
send(s_server,love,100,0);
count2++;
}
cout<<"报文发送成功,等待接受"<<endl<<endl;
recv(s_server, accept_buf, 200, 0);
cout<<"收到服务器端消息";
analysis(accept_buf);
cout<<"------------------------------------------------"<<endl<<endl<<endl;
}
closesocket(s_server);
//释放DLL资源
WSACleanup();
return 0;
}
void get(int n)
{
memset(love,0,sizeof(love));
string a=to_string(n);
for(int i=0; i<20; i++)
love[i]=a[0];
}
void analysis(char *s) //解析
{
cout<<"传输的报文为:"<<s<<endl;
cout<<"报文解析结果为:"<<endl;
int key=s[3]-'0';
int kongtiao1=s[15]-'0';
int kongtiao2=s[16]-'0';
int window=s[17]-'0';
int num1=s[6]-'0';
int num2=s[7]-'0';
int tem=num1*10+num2;
num1=s[8]-'0';
num2=s[9]-'0';
int shidu=num1*10+num2;
num1=s[18]-'0';
num2=s[19]-'0';
int dwendu=num1*10+num2;
num1=s[20]-'0';
num2=s[21]-'0';
int dshidu=num1*10+num2;
num1=s[22]-'0';
num2=s[23]-'0';
int hour=num1*10+num2;
num1=s[24]-'0';
num2=s[25]-'0';
int min1=num1*10+num2;
num1=s[26]-'0';
num2=s[27]-'0';
int sec=num1*10+num2;
int i;
cout<<"------------------------------------"<<endl;
printf("服务端时间为%02d:%02d:%02d\n",hour,min1,sec);
if(key==0)
cout<<"未插卡..."<<endl;
else
cout<<"读卡中..."<<endl;
cout<<"当前温度传感器的数据: "<<tem<<"° "<<endl;
cout<<"当前湿度传感器的数据: "<<shidu<<"% "<<endl;
cout<<endl;
cout<<"目标温度设置为: "<<dwendu<<"° "<<endl;
cout<<"目标湿度设置为: "<<dshidu<<"% "<<endl;
cout<<"设置";
if(kongtiao1==0)
cout<<"空调模式:关"<<endl;
else if(kongtiao1==1)
cout<<"空调模式:制热"<<endl;
else if(kongtiao1==2)
cout<<"空调模式:制冷"<<endl;
else if(kongtiao1==3)
cout<<"空调模式:送风"<<endl;
cout<<"设置";
if(kongtiao2==0)
cout<<"空调风速:关"<<endl;
else if(kongtiao2==1)
cout<<"空调风速:弱风"<<endl;
else if(kongtiao2==2)
cout<<"空调风速:中风"<<endl;
else if(kongtiao2==3)
cout<<"空调风速:强风"<<endl;
for(i=10;i<15;i++)
{
if(s[i]=='0')
cout<<"灯-"<<i-9<<":关"<<endl;
else if(s[i]=='1')
cout<<"灯-"<<i-9<<":柔和"<<endl;
else if(s[i]=='2')
cout<<"灯-"<<i-9<<":经典"<<endl;
else if(s[i]=='3')
cout<<"灯-"<<i-9<<":强光"<<endl;
}
cout<<"设置";
if(window==0)
cout<<"门窗模式:关"<<endl;
else
cout<<"门窗模式:开"<<endl;
cout<<"\n------------------------------------"<<endl;
cout<<endl;
return ;
}
void tcpxy()
{
memset(send_buf,0,sizeof(send_buf));
int j=0,i;
srand((int)time(0)); // 产生随机种子 把0换成NULL也行
int key;
int control_k;
int control_d;
int wendu;
int shidu;
int deng[N];
int kongtiao1;
int kongtiao2;
int window;
string str0="ABC";
key=1;
if(key==0)
{
control_k=0;
control_d=0;
wendu=random(10,40);
shidu=random(15,80);
for(i=0;i<5;i++)
deng[i]=0;
kongtiao1=0;
kongtiao2=0;
window=0;
}
else
{
control_k=0;
control_d=0;
wendu=random(10,40);
shidu=random(15,80);
for(i=0;i<5;i++)
deng[i]=random(0,3);
kongtiao1=random(0,3);
kongtiao2=random(0,3);
window=random(0,1);
}
string str1=to_string(key);
string str2=to_string(control_k);
string str3=to_string(control_d);
string str4=to_string(wendu);
string str5=to_string(shidu);
string str6=to_string(deng[0]);
for(i=1;i<5;i++)
str6+=to_string(deng[i]);
string str7=to_string(kongtiao1);
string str8=to_string(kongtiao2);
string str9=to_string(window);
string str=str0+str1+str2+str3+str4+str5+str6+str7+str8+str9; //帧格式(协议)
for(int i=0; i<str.length(); i++)
{
send_buf[j]=str[i];
j++;
}
cout<<send_buf<<endl;
}
四、实验结果显示
总结
本次实验在Windows环境下,网络应用程序编程接口称作Windows Socket。使用bind函数绑定端口号和IP地址,connect函数实现套接字连接到目的地址,实现服务端和客户端之间的连接。Listen函数在服务端申请到套接字和调用bind与本地地址绑定后,使用listen函数等待客户机程序的链接。Accept函数接受连接函数,send、recv函数实现发送和接受数据。使用完scoket之后要closesocket关闭套接字。通过socket通信,传输设计的通信帧实现通信同步。