项目三智能物流WIFI传输网方案设计与实践.docx
《项目三智能物流WIFI传输网方案设计与实践.docx》由会员分享,可在线阅读,更多相关《项目三智能物流WIFI传输网方案设计与实践.docx(22页珍藏版)》请在冰豆网上搜索。
项目三智能物流WIFI传输网方案设计与实践
项目三智能物流WIFI传输网方案设计与实践
一、教学目标
1、掌握WIFI相关基础知识。
2、自行组建WIFI数据传输的流程和方法。
3、完成WIFI数据传输的流程和方法。
二、教学内容
3.1WIFI基础知识
1、WIFI概述
Wifi是一种可以将个人电脑、手持设备(如PDA、手机)等终端以无线方式互相连接的技术。
它是一个无线网路通信技术的品牌,由Wifi联盟所持有,目的是改善基于IEEE802.11标准的无线网路产品之间的互通性。
它是一种短程的无线传输技术,能够在数百英尺范围内支持互联网接入的无线电信号。
随着技术的发展,以及IEEE802.11a及IEEE802.11g等标准的出现,现在IEEE802.11这个标准已被统称作Wifi。
2、Wifi的组成
一个Wifi联接点网络成员和结构站点(Station),是网络最基本的组成部分。
基本服务单元(BasicServiceSet,BSS):
网络最基本的服务单元。
最简单的服务单元可以只由两个站点组成。
站点可以动态地联接(associate)到基本服务单元中。
分配系统(DistributionSystem,DS):
分配系统用于连接不同的基本服务单元。
分配系统使用的媒介(Medium)逻辑上和基本服务单元使用的媒介是截然分开的,尽管它们物理上可能会是同一个媒介,例如同一个无线频段。
接入点(AccessPoint,AP):
接入点既有普通站点的身份,又有接入到分配系统的功能。
拓展服务单元(ExtendedServiceSet,ESS):
由分配系统和基本服务单元组合而成。
这种组合是逻辑上的,并非物理上的。
不同的基本服务单元物有可能在地理位置上相去甚远。
分配系统也可以使用各种各样的技术。
关口(Portal):
也是一个逻辑成分。
用于将无线局域网和有线局域网或其他网络联系起来。
3、Wifi的特点
IEEE802.11只负责在站点使用的无线媒介上的寻址(Addressing),分配系统和其他局域网的寻址不属于无线局域网的范围。
IEEE802.11没有具体的定义分配系统,只是定义了分配系统应该提供的服务(Service)。
整个无线局域网定义了9种服务:
●5种服务属于分配系统的任务,分别为:
联接(Association),结束联接(Dissociation),分配(Distribution),集成(Integration),再联接(Reassociation)。
●4种服务属于站点的任务,分别为:
鉴权(Authentication),结束鉴权(Deauthentication),隐私(Privacy),MAC数据传输(MSDUdelivery)。
4、Wifi的应用
由于Wifi的频段在世界范围内是无需任何电信运营执照的,因此WLAN无线设备提供
了一个世界范围内可以使用的,费用极其低廉且数据带宽极高的无线空中接口。
用户可以在Wifi覆盖区域内快速的浏览网页,随时随地的接听拨打电话。
而其他一些基于WLAN的宽带数据应用,如流媒体、网络游戏等功能更是值得用户期待。
有了Wifi功能,我们可以打长途电话(包括国际长途),浏览网页、收发电子邮件、音乐下载、数码照片传递等,而无需担心速度慢和花费高的问题。
Wifi在掌上设备上应用越来越广泛,而智能手机就是其中一份子。
与早前应用于手机上的蓝牙技术不同,Wifi具有更大的覆盖范围和更高的传输速率,因此Wifi手机成为了目前移动通信业界的时尚潮流。
3.2WIFI传感网实践
3.2.1WIFI接入
1、接入设备
●天线
●ARM网关(EBA)
2、WIFI接入原理
(1)UDP
UDP是一个面向数据报和无连接的简单传输层协议.它不像TCP那样通过握手过程建立服务器与客户端的连接才可以工作。
在网络通信质量较好的情况下,UDP体现出高效率,这适合于传送少量报文的应用。
linux系统是通过套接字结构来进行网络编程的,应用程序通过对套接字的几个函数调用,会返回一个用于通信的套接字描述符,而Linux应用程序在进行任何形式的I/O操作时,程序实际上是在读写一个文件描述符。
因此Linux下的套接字编程,可以看成是对普通文件描述符的操作,这些操作与被使用的硬件平台无关,这是linux设备无关性的优点。
(2)Socket
常用的Socket类型有两种:
流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。
流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;应用流式Socket可以保证数据传输中的完整性、正确性和单一性,可类比于现实生活中的打电话。
数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。
数据报式Socket可以象流式Socket一样进行数据的双向传输,但无法保证传输数据的完整性、正确性和单一性。
可以类比于现实生活中的寄信。
Socket并不是以面向对象的方式提供的。
Socket事实上并不是留给编程开发的,而是留给网络操作系统实现协议栈的一个支持模型。
例如berkeleySocket和windowsSocket。
至于编程开发时使用的Socket模型和相应的API,则是操作系统服务API。
事实上Socket也不是tcp/udp服务的全部而是部分。
如果用电话来对应网络会话的话,端口应该正好比分机号。
rawSocket其实就是跳过TCP,UDP这一层。
用Socket我们不光是只发数据了,还要TCP和UDP的头。
大家也可以自己一个字节一个字节来构建自己的IP数据报文,并非一定需要通过Socket接口。
(通过流行的winpcap,你可以抛开Socket,发送自己构建的任意的IP报文)。
Socket是一个大家都认为好用的构建和解析IP协议的接口而已(当然Socket并非仅仅能够构建和解析ip协议),其实有很多嵌入式设备还有其他的网络通讯接口(比如LWIP)。
只不过在大部分操作系统上Socket更加通用而已。
3、WIFI接入步骤
(1)ARM网关断电。
将天线插入wifi模块的SMA_WIFI部分,如图3-1所示:
图3-1wifi插入天线位置
(2)ARM网关上电,启动Linux系统。
(3)本小节中是一个WIFI连接路由的示例。
由于网络环境的不同,所以在您做本实验时,请根据实际情况进行设置。
(4)在终端内依次执行下面的命令连接路由器。
#ifconfig–a(检测开发板所有网卡状况)
#ifconfigeth0down(关闭dm9000网卡)
#ifconfigwlan0up(启动SDIOWIFI)
#iwlistwlan0scan(使用SDIOWIFI扫描无线网络设备)
#iwconfigwlan0essid“E-Box”(设置essid)
如果路由器不支持自动获取IP功能,则输入命令:
#ifconfigwlan0192.168.0.232(设置SDIOWIFI的IP)
若路由器支持自动获取IP功能,那么无需设置SDIOWIFI的IP,输入下面的命令:
#udhcpc–iwlan0
设置路由器访问密码,若路由器无密码则无需此命令。
#iwconfigwlan0key“123456789”
#routeadddefaultgw192.168.0.201(设置网关,路由器支持自动获取IP则无需此命令)
#ping192.168.0.201(ping网关)
本小节测试的路由器可以访问互联网,所以可以使用QTOPIA浏览器上网冲浪。
3.2.2WIFI数据交互
1、WIFI数据交互设备
●天线
●ARM网关(EBA)两个
●SD卡
2、WIFI数据交互实践原理
(1)Socket
TCP/IP(TransmissionControlProtocol/InternetProtocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
TCP/IP协议族包括运输层、网络层、链路层。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
TCP/IP仅仅是一套协议、规范,并没有实体存在,是完全抽象的;Socket模型则是清晰而具体的。
而大部分情况下,门面模型是可有可无的,只是为了满足封装一个简单的外部门面而存在的,TCP/IP和Socket并没有相提并论的前提,根本是为了解决同一个问题的两个步骤,是完全分离的。
抓包程序基本上是使用rawSocket(原始套接字)实现的,这是Socket的高级功能,并不是因为对TCP/IP协议很了解,而是对Socket很了解(当然不了解TCP/IP也无法理解高级的Socket)。
离开特定操作系统(或者虚拟机)讨论TCP/IP的编程是没有意义的。
Socket在其中的位置如图3-2所示:
图3-2Socket在网络协议中的位置
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。
在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。
客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
如图3-3所示:
图3-3服务器与客户端之间交互过程
1)1Socket建立
为了建立Socket,程序可以调用Socket函数,该函数返回一个类似于文件描述符的句柄。
Socket函数原型为:
intSocket(intdomain,inttype,intprotocol);
domain指明所使用的协议族,通常为PF_INET,表示互联网协议族(TCP/IP协议族);type参数指定Socket的类型:
SOCK_STREAM或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW),允许程序使用低层协议;protocol通常赋值"0"。
Socket()调用返回一个整型Socket描述符,你可以在后面的调用使用它。
两个网络程序之间的一个网络连接包括五种信息:
通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。
Socket数据结构中包含这五种信息。
Bind函数将Socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。
Bind函数原型为:
intbind(intsockfd,structsockaddr*my_addr,intaddrlen);
Sockfd是调用Socket函数返回的Socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(structsockaddr)。
structsockaddr结构类型是用来保存Socket信息的:
structsockaddr
{
unsignedshortsa_family;/*地址族,AF_xxx*/
charsa_data[14];/*14字节的协议地址*/
};
sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data则包含该Socket的IP地址和端口号。
另外还有一种结构类型:
structsockaddr_in
{
shortintsin_family;/*地址族*/
unsignedshortintsin_port;/*端口号*/
structin_addrsin_addr;/*IP地址*/
unsignedcharsin_zero[8];/*填充0以保持与structsockaddr同样大小*/
};
这个结构更方便使用。
sin_zero用来将sockaddr_in结构填充到与structsockaddr同样的长度,可以用bzero()或memset()函数将其置为零。
指向sockaddr_in的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或者相反。
使用bind函数时,可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号:
my_addr.sin_port=0;/*系统随机选择一个未被使用的端口号*/
my_addr.sin_addr.s_addr=INADDR_ANY;/*填入本机IP地址*/
通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。
同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。
注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序;而sin_addr则不需要转换。
面向连接的客户程序使用Connect函数来配置Socket并与远端服务器建立一个TCP连接,其函数原型为:
intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen);
Sockfd是Socket函数返回的Socket描述符;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度。
Connect函数在出现错误时返回-1,并且设置errno为相应的错误码。
进行客户端程序设计无需调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,Socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候到打断口。
Connect函数启动和远端主机的直接连接。
只有面向连接的客户程序使用Socket时才需要将此Socket与远端主机相连。
无连接协议从不建立直接连接。
面向连接的服务器也从不启动一个连接,它只是被动的在协议端口监听客户的请求。
Listen函数使Socket处于被动的监听模式,并为该Socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。
intlisten(intsockfd,intbacklog);
Sockfd是Socket系统调用返回的Socket描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们。
Backlog对队列中等待服务的请求的数目进行了限制,大多数系统缺省值为20。
如果一个服务请求到来时,输入队列已满,该Socket将拒绝连接请求,客户将收到一个出错信息。
当出现错误时listen函数返回-1,并置相应的errno错误码。
accept()函数让服务器接收客户的连接请求。
在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。
intaccept(intsockfd,void*addr,int*addrlen);
sockfd是被监听的Socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(structsockaddr_in)的整型指针变量。
出现错误时accept函数返回-1并置相应的errno值。
首先,当accept函数监视的Socket收到连接请求时,Socket执行体将建立一个新的Socket,执行体将这个新Socket和请求连接进程的地址联系起来,收到服务请求的初始Socket仍可以继续在以前的Socket上监听,同时可以在新的Socket描述符上进行数据传输操作。
数据传输
Send()和recv()这两个函数用于面向连接的Socket上进行数据传输。
Send()函数原型为:
intsend(intsockfd,constvoid*msg,intlen,intflags);
Sockfd是你想用来传输数据的Socket描述符;msg是一个指向要发送数据的指针;Len是以字节为单位的数据的长度;flags一般情况下置为0。
recv()函数原型为:
intrecv(intsockfd,void*buf,intlen,unsignedintflags);
Sockfd是接受数据的Socket描述符;buf是存放接收数据的缓冲区;len是缓冲的长度。
Flags也被置为0。
Recv()返回实际上接收的字节数,当出现错误时,返回-1并置相应的errno值。
如果你对数据报Socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该Socket仍然是数据报Socket,并且利用传输层的UDP服务。
但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。
结束传输:
当所有的数据操作结束以后,你可以调用close()函数来释放该Socket,从而停止在该Socket上的任何数据操作:
close(sockfd);
(1)服务器端代码分析
服务器端一直在监听是否有客户端连接,如有连接,处理客户端的请求,给出回应,然后继续监听。
因为IP地址,端口号等信息需要自己输入,所以main函数需要参数。
intmain(intargc,char**argv)
将第一个输入的字符串参数转化为整形数后,作为端口号赋值给myport,默认参数为7838
if(argv[2])myport=atoi(argv[2]);
elsemyport=7838;
printf("Portisok!
\n");
创建Socket,如果返回值为-1,则创建成功,否则创建失败
if((sockfd=Socket(PF_INET,SOCK_STREAM,0))==-1)
{printf("CreatSocketsucceed!
\n");
perror("Socket");
exit
(1);}
else
printf("CreatSocketfailed!
\n");
将my_addr空间清空,且包括“、0”
bzero(&my_addr,sizeof(my_addr));
设置地址族
my_addr.sin_family=PF_INET;
设置端口号,以大端方式赋值(htons():
把16位值从主机字节序转换成网络字节序)
my_addr.sin_port=htons(myport);
设置IP地址,将IP地址字符参数3,转换为32位网络序列的IP地址,如果没有参数3,则自动填入本机IP地址
if(argv[3])
my_addr.sin_addr.s_addr=inet_addr(argv[1]);
else
my_addr.sin_addr.s_addr=INADDR_ANY;
printf("SetIPsucceed!
\n");
调用bind函数将其与本机地址以及一个本地端口号绑定
if(bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr))==-1)
{
printf("Failtobind!
\n");
perror("bind");
exit
(1);
}
printf("Succeedtobind!
\n");
下面是收发程序的主体,为了实现循环收发,使用了while死循环,需要断开时按ctrl+C
charrecv[30];
char*send=“hello,thisisserver!
”;
while
(1)
{
memset(recv,0,sizeof(recv));
recvfrom(sockfd,recv,sizeof(recv),0,(structsockaddr*)&client_addr,&addr_len);
printf(“%s\n”,recv);
sendto(sockfd,send,strlen(send),0,(structsockaddr*)&client_addr,&addr_len);
}
关闭Socket,返回
close(sockfd);
return0;
(2)客户端代码分析
因为IP地址,端口号等信息需要自己输入,所以main函数需要参数
intmain(intargc,char**argv)
如果输入参数不够,则退出
if(argc!
=3)
{
printf("Peremetererro!
Forexample:
\n\t\t%sIPport\n\t:
\t%s127.0.0.180\n",argv[0],argv[0]);
exit(0);
}
elseprintf("\nProgramstartsnormally!
\n");
创建一个Socket用于tcp通信
if((sockfd=Socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("FailtocreatSocket!
\n");
perror("Socket");
exit(errno);
}
elseprintf("CreatSocketsucceed!
\n");
初始化服务器端(对方)的地址和端口信息
bzero(&dest,sizeof(dest));
dest.sin_family=AF_INET;
dest.sin_port=htons(atoi(argv[2]));
if(inet_aton(argv[1],(structin_addr*)&dest.sin_addr.s_addr)==0)
{
printf("Failtoinitialipaddressandport!
\n");
perror(argv[1]);
exit(errno);
}
elseprintf("Initialipaddressandportsucceed!
\n");
连接服务器
printf("\nConnecting......\n");
connum=connect(sockfd,(structsockaddr*)&dest,sizeof(dest));
printf("Connectfailbecasueof%d%d\n",connum,errno);
if(connum!
=0)
{
printf("Connectfail!
\n");
perror("Connect");
exit(errno);
}
elseprintf("Connectsucceed!
\n");
printf("\nEverything