5.如何应用? RAW API中文译为原始API可以说比较接近底层了,玩过socket编程的人都知道,socket编程用起来,是比较简单了,不像RAW API的模式,用起来比较麻烦,需要应用者对TCP,UDP这些协议,有一个 稍微深入的了解。这里我推荐一款抓包软件Wireshark。这个软件可以帮助你分析这些协议是怎么工作的。不过在用这个软件的,分析协议的时候最好不要连接到外网,会干扰的哦。
①TCP服务器和客户端 A.TCP通信简介 TCP是面向连接的,在进行通信前需要建立连接。下面以TCP服务器模式为例简单介绍下这个过程。 建立连接:(截图看不清见附件)建立连接.jpg发送数据: 发送数据.jpg 关闭连接: 服务器关闭连接.jpg
B.TCP服务器模式 RAW API大部分都是基于回调函数的API,我们需要按照一定的规范去实现这个回调函数。作为TCP服务器,必须要一个本地IP和端口。 处于服务器模式,是不需要设置远程主机IP和端口,因为远程主机在连接到服务器的过程当中,服务器会把它的IP地址等信息记录下来。就可以双向传输了。 tcp_bind(tcp_server_pcb,IP_ADDR_ANY,TCP_SERVER_PORT);//绑定本地所有IP地址和端口号 tcp_listen(tcp_server_pcb); //开始监听端口
tcp_accept(tcp_server_pcb,tcp_server_accept); //指定监听状态的连接联通之后将要调用的回调函数 tcp_recv(newpcb, tcp_server_recv); //指定连接接收到新的数据之后将要调用的回调函数tcp_err(newpcb, tcp_server_error); //指定连接出错将要调用的函数tcp_poll(newpcb, tcp_server_poll, 0); //指定轮询时将要调用的回调函数对于不熟悉这种习惯的,开始可能有点迷糊糊,不过看多了就好了。上面四个函数,都是用来指定回调函数的。 他们指定的回调函数在指定事件(比如建立了连接,接收到了数据,连接空闲等等)发生的时候将会被调用。
C.TCP客户端模式 对于TCP客户端模式,我们需要指定连接的远程服务器的IP地址和端口号,其他的函数就不多说了,详细见代码 ip_addr_t ipaddr;IP4_ADDR(&ipaddr, 192, 168, 1, 101); //设置本地ip地址tcp_connect(tcp_client_pcb,&ipaddr,TCP_CLIENT_PORT,tcp_client_connect); //连接到远程服务器
②UDP的服务器和客户端 相对于TCP来说UDP则就简单了许多,他不需要像TCP一样需要建立连接通道才可以进行通信。没有重发机制。所以它是不可靠的通行。但是速度快,是他的一大亮点。 A:UDP服务器模式 udp_bind(udp_server_pcb,IP_ADDR_ANY,UDP_SERVER_PORT); //帮顶本地IP地址和端口udp_recv(udp_server_pcb,udp_server_rev,NULL); //指定收到数据包时的回调函数不过需要注意在接受数据的udp_server_rev回调函数中需要将客户端的IP地址和端口记下来,方便UDP服务器发送数据给客户端。 udp_server_pcb->remote_ip = *addr; //记录远程主机的IPudp_server_pcb->remote_port = port;//记录远程主机的端口号 B:UDP客户端模式 udp_bind(udp_client_pcb,IP_ADDR_ANY,UDP_CLIENT_PORT); //这里可以绑定任意的端口,不一定是服务器的端口号,作为客户端主要关心的是要连接的端口和IPudp_connect(udp_client_pcb,&ipaddr,UDP_CLIENT_PORT); //设置连接到远程主机 ③web服务器 现在那种browse+server的模式很流行,在PC机中。这种模式,客户端只需要一个浏览器就OK了,不需要下载专门的客户端,现在的网页游戏这么流行就证明了这一点。你想想在家里, 我的手机通过WIFI连接到路由器,你的嵌入式服务器也连接到路由器,只要有手机,不需要携带笨重的电脑,装麻烦软件,就可以控制家里的电器,多爽啊,这就是物联网的应用。 如何减少嵌入式服务器的开发难度,是很多人研究的重点。感觉LWIP的CGI和SSI接口用起来还是比较爽的,结合一些流行的web前端技术,我们可以开发比较漂亮的应用。
A.什么是CGI? CGI是Common Gateway Interface通用网关接口的简称。百度一下解释多多。其实,他的作用是用来让服务器和应用程序打交道的。当我们从浏览器获取参数和对应的值后,然后与应用程序 进行交互的这个函数,就是CGI函数了。对应的URLl路径就写一个对应的CGI函数。这样不同的请求,就有不同的处理,这个函数将会返回,你想发送给浏览器的文件的文件名路径。
B.CGI函数介绍 typedef char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);参数说明: iIndex :表示在ppcURLs数组中的索引号码, ppcURLs 是个什么玩意呢? ppcURLs 的类型是这个东东。 typedef struct{ const char *pcCGIName; //浏览器请求的URL tCGIHandler pfnCGIHandler; //定义的CGI函数指针} tCGI;那么这个东东有什么作用呢?,他是用来注册URL路径和对应的函数,void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers),这个函数会对它进行设置。当浏览器发出一个请求URL时,这个URL路径对应的CGI函数就会执行。 lwip已经帮我们做好了,我只修需要注册URL路径和对应的CGI函数就可以了,爽吧。 iNumParams :表示URL中你请求参数和其参数对应值的个数,比如http://192.168.1.16/login.cgi?name=onetree&password=12345,参数是name和password,值是onetree和12345,个数就是2.pcParam :参数的数组,包含所有的参数 pcValue: 对应参数的值的数组 C.CGI应用举例 比如。浏览器,采用get的方式,发了一个请求。http://192.168.1.16/login.cgi?/name=onetree&password=12345 我们的应用程序怎么接收我们的用户名name和密码password呢?
1.注册URL路径和对应的函数 static const tCGI ppcURLs[] ={ { "/login.cgi", Login_CGIHandler }, };
2.设置请求的URL的路径和CGI函数 #define NUM_CONFIG_CGI_URIS (sizeof(ppcURLs ) / sizeof(tCGI)) http_set_cgi_handlers(ppcURLs , NUM_CONFIG_CGI_URIS);
3.编写CGI函数 static char * Login_CGIHandler ( int iIndex, int iNumParams, char *pcParam[], char *pcValue[] ){ int index; index = FindCGIParameter ( "name", pcParam, iNumParams ); if(index!=-1){ name对应的参数值= pcValue[index]; } index = FindCGIParameter ( " password", pcParam, iNumParams ); if(index!=-1){ password对应的参数值 =pcValue[index]; } return "/index.html"; //这里返回你要发给浏览器的文件名路径
} 注意到有个 FindCGIParameter 的函数,这个函数是用来查找你想要的参数。 static int FindCGIParameter(const char *pcToFind, char *pcParam[], int iNumParams){ int iLoop; for(iLoop = 0; iLoop < iNumParams; iLoop++) { if(strcmp(pcToFind, pcParam[iLoop]) == 0) { return(iLoop); } } return(-1);}到此为止,我们就完成了,浏览器和应用程序的交互了。
D.什么是SSI? SSI是Server Side Include服务器嵌入端的简称。是一种类似于ASP的基于服务器的网页制作技术。 工作原理:LWIP对于.shtml,.ssi,.shtm后缀的文件,会检测文件中<!--#name-->格式的TAG标志。 然后再这个标记后面添加你想要的字符串。并不是替换,不过这个方法在脚本中不行, <!--#name--> 是html文件的注释,但是在 <script>...</script>中就不是注释了,所以在添加js代码的时候必须把整个 JS脚本添加进来。
E.SSI函数简介 typedef int (*tSSIHandler)(int iIndex, char *pcInsert, int iInsertLen)这个函数除了这三个参数外,还有其他三个可选的参数,这里没写出来,具体怎么用交给大家研究啦。 参数说明: iIndex:在ppcTags数组中的索引号,ppcTags是一个字符串数组,用来标记tag名字的,比如<!--#name-->,“name”就是一个tag名 pcInsert:在tag后面插入的字符串 iInsertLen:在tag后面插入字符串的长度
F.SSI应用举例 1.新建一个index.shtml的文件,在文件中输入一下代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>welcom</title><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /><meta http-equiv= "Cache-Control" content= "no-cache" /> </head><body><form id="login" name="login" method="get" action="login.cgi"><table width="200" border="1" align="center"><tr><td colspan="2" align="center">请按F5进行刷新</td></tr> <tr><td width="50">内容:</td><td width="134"><label><!--#alientek--></label></td></tr></table></form></body></html>注意到上面有一个 <!--#alientek--> 的TAG,,利用LWIP的SSI的功能可以将我们想插入的字符串插入到后面。
2.编写SSI处理函数 static const char *ppcTags[] = //TAG数组{ " alientek ", };enum ssi_index_s //索引号{ SSI_INDEX_ALIENTEK_GET = 0, //该表对应ppcTags[]的排序} ; static int SSIHandler ( int iIndex, char *pcInsert, int iInsertLen ){ switch(iIndex) { case SSI_INDEX_ALIENTEK_GET: pcInsert = "alentek"; iInsertLen = strlen(pcInsert ); break; default: strcpy( pcInsert , "" ); } return iInsertLen ; //返回字符串的长度}
3.再编写一个返回URL为/index.shtml的文件的CGI函数就可以了。在页面中就可以看到alientek内容了。
最后重点介绍下ajax做异步提交,更新数据: 一、如何将静态页面转换为16进制的c语言数组? 前面说了这么多都没提这个重要的问题,希望大家别见怪,嘿嘿。 这里我谢谢 zhangpisces网友提供的资料。他的资料给了我很大的参考价值,喝水不忘,挖井人。 步骤: 1.首先将网页源文件编写好,如工程中atk文件夹下的文件。2.将makefsfile工具和atk放在一个文件夹内.3.运行cmd,进入到makefsfile工具的目录。4.使用makefsfile -i atk -o fsdata.h -r -h 命令生成一个 fsdata.h文件 注意:在fs.c文件中需要注释掉//#include "fsdata.c"这个,我们不用。
二,添加/response.ssi文件 #define RESPONSE_BUF_SIZE 512 //http响应缓存大小 unsigned char data_response_ssi[RESPONSE_BUF_SIZE+14] ={ /* /response.ssi */ 0x2F, 0x72, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x2E, 0x73, 0x73, 0x69, 0x00, };struct fsdata_file file_response_ssi[] ={ { NULL, data_response_ssi, data_response_ssi + 14, sizeof(data_response_ssi) - 14 }};makefsfile 生成的都是ROM文件,如果我们想要改变返回给浏览器的响应内容必须定义一个可以变化的文件。 在CGI函数中我们就可以把想发送的数据写入到这个文件当中,return "/response.ssi"就OK了。 三、编写基于ajax的JS代码 我把战舰原来uip的web例程(不过这个例程并没有用到SSI,是综合例程)改成了ajax异步获取数据,参考JS源码如下: <script type="text/javascript">var xmlhttp;function loadXMLDoc(url,cfunc){ if (window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } else { xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange=cfunc; xmlhttp.open("GET",url,true); xmlhttp.send();} function led0(){ loadXMLDoc("/led_red.cgi?red=1&t="+ Math.random(),function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("red").src=xmlhttp.responseText; } });} function led1(){ loadXMLDoc("/led_green.cgi?green=1&t="+ Math.random(),function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("green").src=xmlhttp.responseText; } });}var text; function update(){ loadXMLDoc("/orther.cgi?t="+ Math.random(),function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { text = xmlhttp.responseText; text = text.split(";"); document.getElementById("temperature").innerHTML=text[0]+"℃"; document.getElementById("time").innerHTML=text[1]; } }); }function init(){ setInterval(update,1000); } </script>
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 到现在为止基本上讲完了,第一次发这么长的帖子,各位看官慢慢看。有错误请多多包容哦^_^ 附件中还有战舰的UIP补充例程,添加了UDP的服务器和客户端模式,web没怎么搞。 还有LWIP例程。包括了TCP服务器,TCP客户端,UDP服务器,UDP客户端,WEBSEVER。这些功能,配合我们的ALIENTEK的ENC28J60模块就可以 直接在战舰开发板上运行了。 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- LWIP例程说明: KEY_UP:选择实验(TCP服务器,TCP客户端,UDP服务器,UDP客户端,WEBSEVER)KEY_DOWN:发送数据 建议大家先学习UIP,然后再学LWIP可能效果要好些,毕竟UIP还是要简单得多。
|