3202| 0
|
共享单车源代码 包括服务器和控制板 |
volatile 共享单车智能锁设计 2018年03月18日 16:04:37 yancy_扬希 阅读数:3054 版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/ca1m0921/article/details/79601275 -------------------------------------------------------------------------------- 2018-05-15 毕设答辩结束20天后: 毕业设计已经完成,且答辩结束。回过头来认真梳理下开发的流程和使用到的技术,写一写遇到的重点问题,然后贴上来所有的源码以及对项目使用到的开发环境和开发工具进行必要的说明。一是为了做个了结,二是为了更好的帮助到你们。 -------------------------------------------------------------------------------- 2018-04-15 毕业设计进入关键时刻: 再此整理下,毕设涉及到的相关技术,在毕设基本成型的时候,认真整理一下各部分的知识。 一马当先的Socket,万物互联的Socket。使用Socket通信建立起IP地址之间的通信,将SIM连接到网络上。 安全传输的TCP,牢靠的TCP。对于TCP or UDP,其实凭我个人感觉,以后网速越来越稳定之后,更偏爱UDP。 多线程,多线程和多进程。在此项目中,最后做功能实现时,有点急躁,多线程并没有成功,多进程可以用之后,就不再想尝试多线程了。那时候感觉整个项目的东西有点多,乱,轻易不敢下手了。而且截止时间也不多了。 微信公众号开发,最火的小程序开发。对于客户端选择了微信,一是因为老师说项目的重点是客户端,二是当时用微信构想了下需要的功能,觉得勉强能够用,所以就没有尝试开发app。 最好的操作系统,Linux基本操作。项目没有涉及到太多Linux的东西。如果说有,那就是云服务器是CnetOS的,Ngrok+MySQL+PHP搭建在服务器上,因为忘记了数据库密码致使把整个环境重搭了一下,后来因为出现未知的错误,以及安装多进程、多线程插件时,需要修改一些东西,被迫重搭系统,总的来说,整个环境至少重做了4次。在这4次重做过程中,我对Linux的命令操作有点基本的认识。 -------------------------------------------------------------------------------- 2018-03-16 毕设初起步: 我的毕设开始于2018/3/5,毕设已经做了有两周了,一直没有整理自己的进度,现在来补充下,以便以后能回过头来回顾下。 先上任务书: file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps2717.tmp.jpg 再上开题报告,算了,开题报告还是先不上了,等答辩结束再上,免得出事端。 file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps2718.tmp.jpg 整体的设计思路跟共享单车的实现原理相似(此处不敢用一致,毕竟不清楚其具体原理)。 技术选型:STM32F103+SIM900(GPRS模块)+腾讯云的云服务器(学生优惠)+微信公众号(测试号开发) 开发语言:单片机使用了C语言,服务器使用的Centos7,服务器开发语言是PHP,通信方式暂定为Socket。 整个系统的架构图: file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps2719.tmp.jpg 第一周完成的工作主要有:配置好服务器,搭建一个可以满足自己使用的微信公众号,完成基本的功能。 ①、后来发现微信公众号没有自定义菜单功能,遂采用了微信公众号平台的测试账号,刚开始对自己的云服务不太了解,使用了 百度的EBA应用引擎,一天0.4元,很实惠,千万注意是 基础版并不是专业版,专业版9.9,当初自己买错了,还好没有操作,直接就申请退款了。 关于微信测试账号的设置,请参考,这个博客,在此谢过,真的帮到了我很多。在此谢过博主大佬。 zc的救赎:②、服务器的配置,因为要使用 SIM与服务器通信,之前学习过Socket通信,觉得Socket通信可以实现此功能,就直接确定了使用Socket通信,我之前是学习Java的,对PHP没有好感,也没有兴趣,md,这个一点的不严谨的语言,真的是不想学,但因为我的云服务器当初是用来搭建博客的,使用的是PHP的开发环境和WDCP的面板,所以就想用PHP来搞一下Socket,反正Socket也不太难搞。这里主要涉及到 端口的开放和监听,让我搞了两天,真的有点头大,百度查询了很多材料,都是众说纷纭吧。其间,给自己的服务器安装了iptables、telnet等等,当时安装的时候,真的不知道这是干什么用的,后来敲了两天的Linux命令,才开始慢慢的有了点头绪,并质疑过是腾讯后台管理关闭了我要使用的1337端口,连续两次提交工单到腾讯云,真的是非常的抱歉。 关于端口的开放和监听,我自己的理解是:服务器默认开放了所有的端口,而我使用了端口扫描(百度就能找到有个网站提供这个服务,指定IP和端口检测端口是否可用),去扫描我的服务器的1337端口,检测的结果是端口是关闭状态,是因为服务器端没有进程在监听1337端口,后来写了个Socket服务监听1337端口在服务器上运行,就能扫描到1377端口已开启,此时也可以看到通过putty远程连接服务器查看1377端口已在监听状态。(特别注意要修改iptables防火墙,要同意开放指定的端口,此处针对CentOS7:关闭默认的防火墙firewall,使用iptbles,firewall是7版本才具有的防火墙,网上相关的操作没有iptables的多,技术人员偏爱旧东西,可靠)。 所以开放端口只需要修改防火墙文件并重启服务器(此处好像是只需要重启apache就行),然后写个socket监听某端口。 现在的进度为: 一方面:已经可以完成微信的扫码,发送单车id到服务器端,服务器获取id查询数据库找到对应的ip地址, 另一方面:完成启动socket监听,获取客户端的ip和端口, 下一步的进度:连接起两个功能区,实现基本的功能。 需要特别攻关的方面是 socket监听如何实现多线程监听,因为对PHP不太熟悉,不敢搞太多的东西,因为调试什么的都很复杂,自己有点怵。 暂时先写这些吧,昨天看了php的基本语法,学习了些全局变量,数据类型等东西,今天还是再看一些吧,然后把自己松散的页面结构用类和方法封装起来,然后写一写后台的管理页面,再完成基本的功能,那就很开心了。 关于多线程,和GPS位置,调用地图组件,等完成这些之后,再扩展吧, 持续更新,私以为这就是NB-loT的缩影,可以应付一般的智能设备,主要是采用了SIM的GPRS通信,完成了独立硬件硬件的联网功能,也就是物联网的概念吧。 2018/03/20 23:41 刚刚玩完吃鸡游戏,临时更两句吧, 今天开始调试 服务器和 硬件的通信(STM32+SIM900),实现的目的是 服务器开启Socket服务,监听1337端口,客户端(STM32+SIM800)创建一个Socket客户端连接服务器(IP+Port)。服务端等待客户端连接,当客户端连接上之后,检测客户端的IP,将解锁指令写入Socket连接(类似于管道或者缓冲区的东西)中,客户端从连接中读取数据,检测指令进行判断,通过STM32的IO发送指令给电控锁(其实是继电器),继电器控制电控锁开启,从而完成开锁功能。 目前,要解决的问题是:客户端持续向服务器发送数据,以保证ip地址不会改变,这也是socket的主要问题,要完成长连接,通过心跳包。 测试结果:服务器端Socket要持续监听端口,保持客户端能持续的联网,ip地址不变,重点测试是不是客户端启动中,ip就不会变,与服务器无关。经过测试,GPRS一旦与网络建立连接,IP地址就不会改变。 学习下,继电器和电机锁的原理,接触下。 首要任务,先调通整个系统,然后再完整以上功能。加油。 ------------------------------------------------------------------------------- 2018/03/21 09:23 打卡上班了,先刷一些嘻哈、吾爱、知乎,再搞。 继电器的东西,搞清楚了些。老师没给我电机锁,我使用的STM32集成板看了好久原理图,没找到5V的输出,只有3.3V的输出,而继电器使用的是5V的输入,当使用3.3V的输出接入继电器时,继电器的信号灯会亮,继电器内部并不会产生磁性而开启或断开(也就说只是有个信号灯,效果会折半,但是还是可以用的)。 整个系统 还是没有调通,因为整体的进度还可以,就去帮同学看了些她的毕设,基于Arduino的红外避障小车实现,因为电子竞赛的时候也做个小车的题目,没有使用过arduino,所以有些兴趣。可惜她买的是全套的资料,包括了车架、arduino板子、红外模块、电机控制以及一部分代码,都包含了。只需要安装好,烧录下随单附赠的程序就行了,烧完程序,小车的红外模块正常,小车的车轮不转,后来给马达单独接上电池,发现马达是好的,后来换了6节南孚电池,小车才开始跑起来。果然南孚就是吊。小车目前已经可以完成红外避障了,就是红外模块的感应不太敏感,总是碰到东西之后才会转弯,反应有点慢,初步方向是:调节PWM调节转速使小车的速度降下来,这样转弯就会流畅一些吧。不过,不管她了。 发现对于整个系统还是有些不清楚的地方,所以还是梳理一下吧。 整体:要实现的功能从 微信端扫码开始,经过服务器,服务器发送指令到GPRS模块,GPRS模块通过串口发送到STM32F103处理器,STM32F103通过IO口置位控制继电器,继电器达到条件可触发车锁开关。 第一部分:目前微信端可以通过扫码发送单车id到服务器,服务器可以提取到id编号,此处通过Utils的方法获取单车id,然后通过数据库查询检索该id的条目,获取其对应的IP地址。 第二部分:然后调用Server.php的find($ip)方法,使用数据库查询的ip做参数,find方法中启动Socket服务器监听,通过socket_accept方法接受客户端的连接,等客户端连接上之后,获取客户端的ip地址,用参数ip和客户端的ip做比较。 if _:如果一致,就向socket通道里写入标识号(目前是“1”),然后sleep一小会,以使得客户端能够读取数据,然后SIM800模块通过串口3发送数据到STM32处理器,处理器判断数据满足条件,然后将某个IO口置位(目前是将一个控制led灯的口置位为1), 此处插入一下,继电器和STM32的连接:STM32的5V 连接 继电器的 VCC,STM32的GND 连接 继电器的GND,使用一个IO口连接继电器的IN1,IN1用来控制继电器的触发条件。目前的连接方式是 ON(常开端)接STM32的 5V,COM(公开端)接STM32的GND,这样连接的话,不需要接其他的的东西,继电器就有反应,会听到“滴”的一声。明天会问老师要电机锁,然后将ON和COM接入电机锁,测试下是不是能成功。 else_:如果ip匹配不一致,说明扫得单车id和目前连接到Socket服务器的客户端不是同一辆车,此时就断开Socket连接,等到下一个客户端连接,再检测ip是否一致,一致就执行if的操作。 还是心理有畏惧,不敢接触多线程,如果启动Socket服务器监听端口,并接受每个客户端的连接,同时接受多个客户端的数据传输,就会产生多个进程,客户端定时发送心跳包到服务器端,服务器端实时检测客户端的ip地址,再匹配数据库的ip地址,发送指令的话,如果有100个客户单(即单车)就会产生100个进程,几乎是实时通信,服务器可能会宕机,参考共享单车的实现,如果一个服务器同时连接几百或者上千个客户端连接的话,会不会宕机,我的实现思路是不是正确,值得考虑。 晚安了。 --------------------------------------------------------------------------- 2018/3/22 20:09 下班了。把昨天码过的字,认真看了下,今天还是没有成功,想哭,旁边选了单片机毕设题目的小哥哥小姐姐们,某宝成品100搞定,而我竟然折腾了Centos7云服务器,接触了花生壳,学习了用自有服务器开发微信公众号,PHP的基本语法,Socket通信。尽管现在还是有点乱,但是我相信多学点东西总是有好处的。 目前的情况是,云服务器有两套独立的微系统,支持微信公众号的功能 以及 支持Socket服务。 微信系统:用户通过公众号扫码,发送id到服务器,服务器获取id号,查询数据库找到id对应的IP地址(车锁的SIM动态IP地址),发送给Socket服务器。 Socket服务器:接受到经微信系统查询数据库找到的IP地址,启动Socket监听,获取连接的客户端IP地址,与数据库IP地址匹配,成功则将解锁指令写到Socket通道中。 单车的智能车锁:车锁的SIM模块会接收到数据,发送到STM32的处理器,STM32控制继电器,从而完成开锁。 我在尝试着实现的时候,发现了非常恐怖的事情,云服务器的两个系统(我自己写的系统)是相对独立的,特别是微信端的系统,用来支撑微信公众号的功能,我尝试着在php中include一个文件,从而调用启动Socket的方法,直接完成一系列的功能执行,但是一旦添加了socket_bind的方法时,公众号总会异常。下面贴一下代码,很难讲清楚。 Utils.php :主要是logger起作用,用来查询数据库。 00001. <?php 00002. 00003. include 'Server.php'; 00004. 00005. 00006. 00007. class Utils{ 00008. 00009. /** 00010. 00011. * 打印日志到log.xml 00012. 00013. * @param $log_content:日志内容 00014. 00015. * @param string $type:日志来源,默认‘用户’ 00016. 00017. */ 00018. 00019. public static function logger($log_content, $type = '用户') 00020. 00021. { 00022. 00023. $xml_log_content = simplexml_load_string($log_content); 00024. 00025. $id = $xml_log_content->Content; 00026. 00027. echo $id."\n\r"; 00028. 00029. $con = mysql_connect("localhost","root","Mysql2966031005"); 00030. 00031. 00032. 00033. mysql_select_db("my_db", $con); 00034. 00035. 00036. 00037. $rs = mysql_query("SELECT * from bike where id = '$id'"); 00038. 00039. 00040. 00041. $row = mysql_fetch_array($rs); 00042. 00043. 00044. 00045. $mysql_id = $row['id']; 00046. 00047. $ip = $row['ip']; 00048. 00049. 00050. 00051. Server::find($ip); 00052. 00053. 00054. 00055. mysql_close($con); 00056. 00057. 00058. 00059. $max_size = 3000; 00060. 00061. $log_filename = "./log.xml"; 00062. 00063. if (file_exists($log_filename) and (abs(filesize($log_filename)) > 00064. 00065. $max_size)) { 00066. 00067. unlink($log_filename); 00068. 00069. } 00070. 00071. 00072. 00073. file_put_contents($log_filename, $log_content."\n\r".$id.$ip."\n\r", FILE_APPEND); 00074. 00075. } 00076. 00077. /** 00078. 00079. * 获取access_token 00080. 00081. * @return mixed 00082. 00083. */ 00084. 00085. public static function get_access_token() 00086. 00087. { 00088. 00089. $appid = "wxfd8fd157a47bc539"; //需替换成你的appID 00090. 00091. $appsecret = "9efe8ba722e7506f1c1aed11740e64b3"; //需替换成你的appsecret 00092. 00093. $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&". 00094. 00095. "appid=$appid&secret=$appsecret"; 00096. 00097. $output = Utils::https_request($url); 00098. 00099. $jsoninfo = json_decode($output, true); 00100. 00101. return $jsoninfo["access_token"]; 00102. 00103. } 00104. 00105. /** 00106. 00107. * 发送请求 00108. 00109. * @param $url:地址 00110. 00111. * @param null $data:post的数据 00112. 00113. * @return mixed:请求返回的结果 00114. 00115. */ 00116. 00117. public static function https_request($url, $data = null) 00118. 00119. { 00120. 00121. $curl = curl_init(); 00122. 00123. curl_setopt($curl, CURLOPT_URL, $url); 00124. 00125. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 00126. 00127. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); 00128. 00129. if (!empty($data)){ 00130. 00131. curl_setopt($curl, CURLOPT_POST, 1); 00132. 00133. curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 00134. 00135. } 00136. 00137. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 00138. 00139. $output = curl_exec($curl); 00140. 00141. curl_close($curl); 00142. 00143. return $output; 00144. 00145. } 00146. 00147. } 00148. Server.php: 主要是 find方法, 00001. <?php 00002. 00003. class Server{ 00004. 00005. /* 主要有 find($ip)方法,接收通过Utils传递过来的IP地址, 00006. 00007. * 启动 socket监听,等待客户端连接,获取连接的客户端IP地址 00008. 00009. * 与 Utils页面传递过来的地址比较,若 一致, 00010. 00011. * 而向 IP地址发送 开锁 指令,通过TCPc传输发送GPRS数据到 SIM卡 00012. 00013. * */ 00014. 00015. public static function find($ip){ 00016. 00017. $ip_local = "123.23.13.250"; 00018. 00019. set_time_limit(0); 00020. 00021. //设置IP和端口号、 00022. 00023. echo "start....\r\n"; 00024. 00025. printf("start...\r\n"); 00026. 00027. $address = "10.141.75.98"; //要连接的服务器地址 00028. 00029. $port = 1337; //调试的时候,可以多换端口来测试程序 00030. 00031. 00032. 00033. echo "set_IP and set_port...\r\n"; 00034. 00035. /** 00036. 00037. * 创建一个SOCKET 00038. 00039. * AF_INET=是ipv4 如果用ipv6,则参数为 AF_INET6 00040. 00041. * SOCK_STREAM为socket的tcp类型,如果是UDP则使用SOCK_DGRAM 00042. 00043. */ 00044. 00045. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00046. 00047. //绑定到socket端口 00048. 00049. $result = socket_bind($sock, $address, $port) or die("socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00050. 00051. echo "start_listening....../n/r"; 00052. 00053. //开始监听 00054. 00055. $result = socket_listen($sock, 4) or die("socket_listen() 失败的原因是:" . socket_strerror(socket_last_error()) . "/r/n"); 00056. 00057. echo "OK\nBinding the socket on $address:$port ... ....\r\n"; 00058. 00059. // 等待 客户端 连接 00060. 00061. 00062. 00063. $time = 0; 00064. 00065. do { 00066. 00067. $msgsock = socket_accept($sock); 00068. 00069. // 获取客户端 数据 (IP地址 和 端口号) 00070. 00071. socket_getpeername($msgsock, $addr, $por); 00072. 00073. 00074. 00075. echo "<br/>" . $addr ."==". $ip . "<br/>"; // 223.104.13.203 00076. 00077. // 拿 客户端的 IP 地址 和 Utils 传递过来的 IP地址 比较,一致而 发送指令到 该 IP地址 00078. 00079. break; 00080. 00081. // 如果 数据的ip与获取的客户端ip一致,就发送结果指令到 客户端 00082. 00083. // 否则,就返回accept 继续执行等待客户端 00084. 00085. if ($addr == $ip) { 00086. 00087. $length = socket_write($msgsock, "2"); // 将开锁指令发送发到 客户端 00088. 00089. echo "success!"; 00090. 00091. echo $length; 00092. 00093. sleep(3); 00094. 00095. }else{ 00096. 00097. socket_close($msgsock); 00098. 00099. continue; 00100. 00101. } 00102. 00103. sleep(3); 00104. 00105. echo "after_sleep"; 00106. 00107. 00108. 00109. socket_close($msgsock); 00110. 00111. $time++; 00112. 00113. echo "$time == ".$time; 00114. 00115. }while($time<2); 00116. 00117. socket_close($sock); 00118. 00119. } 00120. 00121. } 00122. 00123. 00124. 00125. 00126. 00127. ?> 00128. 本来的想法是,在Utils.php中调用一下Server.php中的find($ip)方法,将数据库的ip做参数传递过去,然后find($ip)方法中启动Socket监听,等待客户端的连接,获取客户端ip与参数ip做比较,一致则写标识符到Socket通道中,否则退出循环,再等待客户端连接。 每次在Utils.php的logger方法中写 $server->find($ip)或者Server::find($ip)方法,公众号总是会出现异常,尽管我写了include或require,甚至_once都试过了,后来测试,将实现功能的代码直接加入Utils.php的方法中,发现在socket_bind(...)方法时,公众号会出现异常,遂放弃了。 现在的思路比较难受,属于绕了个弯。因为两个系统独立,不能传递参数,当时想过换个app去实现,但时间比较少了,不想再调试其他的东西,怕时间不够用。所以想到了数据库,我可以将Utils.php的参数存储到数据库,然后在Server.php页面将数据取出来,这样岂不是可以实现功能,简直爽歪歪。 从而开始引入了MySQL。遂写成了这样,哈哈哈哈。 Utils.php 00001. <?php 00002. 00003. 00004. 00005. class Utils{ 00006. 00007. /** 00008. 00009. * 打印日志到log.xml 00010. 00011. * @param $log_content:日志内容 00012. 00013. * @param string $type:日志来源,默认‘用户’ 00014. 00015. */ 00016. 00017. public static function logger($log_content, $type = '用户') 00018. 00019. { 00020. 00021. $xml_log_content = simplexml_load_string($log_content); // 获取xml字符串 00022. 00023. $Content = $xml_log_content->Content; // 获取其中的 Content内容 00024. 00025. $id_weixin = $Content; 00026. 00027. $con = mysql_connect("localhost","root","Mysql2966031005");// 连接数据库 00028. 00029. mysql_select_db("my_db", $con); // 选择数据库 00030. 00031. $rs = mysql_query("SELECT * from bike where id = '$id_weixin'");// 执行查询操作 00032. 00033. $row = mysql_fetch_array($rs); // 获取查询的 结果,(以数组形式存储) 00034. 00035. $id = $row['id']; //获取 数据库对应条目的 ID 00036. 00037. $ip = $row['ip']; // 获取 数据库对应条目 的 IP 00038. 00039. 00040. 00041. mysql_query("update bike set status = '1' where id = '$id_weixin'");// 将对应的id条目的status设置为1 00042. 00043. // 调用Server的方法启动,Socket监听 00044. 00045. 00046. 00047. mysql_close($con); //关闭数据库连接 00048. 00049. 00050. 00051. $max_size = 3000; 00052. 00053. $log_filename = "./log.xml"; 00054. 00055. if (file_exists($log_filename) and (abs(filesize($log_filename)) > $max_size)) { 00056. 00057. unlink($log_filename); 00058. 00059. } 00060. 00061. file_put_contents($log_filename, $log_content."\n\r".$ip."hh\n\r", FILE_APPEND); 00062. 00063. 00064. 00065. set_time_limit(0); 00066. 00067. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00068. 00069. $result = socket_bind($sock, "10.141.75.98", 1337) or die("socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00070. 00071. $result = socket_listen($sock, 4); 00072. 00073. } 00074. 00075. /** 00076. 00077. * 获取access_token 00078. 00079. * @return mixed 00080. 00081. */ 00082. 00083. public static function get_access_token() 00084. 00085. { 00086. 00087. $appid = "wxfd8fd157a47bc539"; //需替换成你的appID 00088. 00089. $appsecret = "9efe8ba722e7506f1c1aed11740e64b3"; //需替换成你的appsecret 00090. 00091. 00092. 00093. $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&". 00094. 00095. "appid=$appid&secret=$appsecret"; 00096. 00097. $output = Utils::https_request($url); 00098. 00099. $jsoninfo = json_decode($output, true); 00100. 00101. return $jsoninfo["access_token"]; 00102. 00103. } 00104. 00105. /** 00106. 00107. * 发送请求 00108. 00109. * @param $url:地址 00110. 00111. * @param null $data:post的数据 00112. 00113. * @return mixed:请求返回的结果 00114. 00115. */ 00116. 00117. public static function https_request($url, $data = null) 00118. 00119. { 00120. 00121. $curl = curl_init(); 00122. 00123. curl_setopt($curl, CURLOPT_URL, $url); 00124. 00125. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 00126. 00127. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); 00128. 00129. if (!empty($data)){ 00130. 00131. curl_setopt($curl, CURLOPT_POST, 1); 00132. 00133. curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 00134. 00135. } 00136. 00137. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 00138. 00139. $output = curl_exec($curl); 00140. 00141. curl_close($curl); 00142. 00143. return $output; 00144. 00145. } 00146. 00147. } 00148. Server.php: 00001. <?php 00002. 00003. 00004. 00005. Server::find(); 00006. 00007. class Server{ 00008. 00009. public static function find(){ 00010. 00011. set_time_limit(0); 00012. 00013. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00014. 00015. $result = socket_bind($sock, "10.141.75.98", 1337) or die("socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00016. 00017. $result = socket_listen($sock, 4); 00018. 00019. 00020. 00021. $time = 0; 00022. 00023. do { 00024. 00025. $msgsock = socket_accept($sock); 00026. 00027. socket_getpeername($msgsock, $addr, $por); 00028. 00029. 00030. 00031. echo "<br/>" . $addr . "<br/>"; // 223.104.13.203 00032. 00033. // 拿 客户端的 IP 地址 和 Utils 传递过来的 IP地址 比较,一致而 发送指令到 该 IP地址 00034. 00035. $ip_status_1 = Server::find_status_1(); 00036. 00037. // 如果 数据的ip与获取的客户端ip一致,就发送结果指令到 客户端 00038. 00039. // 否则,就返回accept 继续执行等待客户端 00040. 00041. if ($addr == $ip_status_1) { 00042. 00043. $length = socket_write($msgsock, "1"); // 将开锁指令发送发到 客户端 00044. 00045. echo "success!"; 00046. 00047. echo $length; 00048. 00049. sleep(3); 00050. 00051. }else{ 00052. 00053. socket_close($msgsock); 00054. 00055. $time++; 00056. 00057. continue; 00058. 00059. } 00060. 00061. sleep(3); 00062. 00063. echo "after_sleep"; 00064. 00065. socket_close($msgsock); 00066. 00067. $time++; 00068. 00069. }while($time<20); 00070. 00071. socket_close($sock); 00072. 00073. } 00074. 00075. 00076. 00077. public static function find_status_1(){ // 获取status为1的条目,并获取其 ip 00078. 00079. $con = mysql_connect("localhost", "root", "Mysql2966031005");// 连接数据库 00080. 00081. mysql_select_db("my_db", $con); // 选择数据库 00082. 00083. while (true) { 00084. 00085. $rs = mysql_query("SELECT * from bike where status = '1'");// 执行查询操作 00086. 00087. if (!($rs == null)) { // 如果status = 1 的条目存在,执行 if 00088. 00089. $row = mysql_fetch_array($rs); // 获取查询的 结果,(以数组形式存储) 00090. 00091. //$id = $row['id']; //获取 数据库对应条目的 ID 00092. 00093. $ip = $row['ip']; // 获取 数据库对应条目 的 IP 00094. 00095. echo "IF_Module"; 00096. 00097. return $ip; // 返回 IP地址 到 find 方法 00098. 00099. } else { 00100. 00101. echo "else_Module"; 00102. 00103. return "else"; 00104. 00105. } 00106. 00107. } 00108. 00109. } 00110. 00111. } 00112. 00113. ?> 00114. 这样,我觉得我可以 先启动Socket服务器,实时监听客户端的连接,然后查询数据库,是否有哪条记录的status被置位为1,说明该IP地址匹配的二维码被扫码了,发现之后,就获取ip,与连接的客户端匹配,匹配成功,完成开锁。 没有测试还是没谱,希望能有个好的结果吧。对于服务器端总是会出现不定时间的Listen状态,有点搞不明白。感觉自己写的sleep并没有影响。 就这样吧,晚安吧。 ----------------------------------------------------------------------------------------- 2018/3/23 11:50 经过调试,目前可以使用,但是还存在一些小的问题,还需要完善,不管怎么说,还是蛮开心的,尽管在意料之中,可依然兴奋。 给毕设指导老师看过了,测试还是正常的,开心。估计大体就这样了,从开始折腾,到模块测试,再到集成测试,觉得也花了些时间,毕竟防火墙的端口开放,就搞了两天。后来的系统集成,也是存在各种问题,现在折腾到基本功能实现,已经有点筋疲力尽了,不准备朝着这方面就业,那就做成这样,就了结吧。 争取这周完成毕设论文终稿,然后整理出毕设的指导文档,就当是给老师留给念想吧,也给学弟学妹提供个肩膀。我觉得我的毕设应该叫 互联网+,因为昨天看到周鸿祎前辈讲 +互联网 和 互联网+,共享单车的产品思路 是为了颠覆传统的设计,所以,我是互联网+, 结束之后,我要拾起我的Java,I'm ready。 给老师看了下我的 论文,讲了下我的毕设,老师说 让我把多线程做一下,要不有点太简单了。 然后就查了 PHP对多线程的支持,看到说 pcntl 的php组件,然后就想搞这个吧,我觉得也挺简单的。我的服务器是用的WDCP平台完成的 lanmp 集成安装,然后就是找wdcp如何安装 pcntl 确实有些说法,也这折腾了,发现 服务器上有 多个php.ini,不知道是哪个起作用,后来就有点烦躁,毕竟对Linux还有不太熟悉,而且平台是 通过WDCP来管理的,总觉得它们有个托管什么的,不知道整个系统是怎么配置的,到底是在wdcp还是在linux设置的权限大。 折腾了好久,通过重新搭建环境,在MySQL的安装上做一些操作,最终还是安装好插件了。 不管了,开始搞我的多线程, 搞定之后,就可以答辩了,答辩结束 就可以自由了。飞起来了。 等我答辩结束,把这些东西整理一下,再认真梳理下,物联网的原理。 好了,才搞完pcntl 发现不是我想要的东西,pcntl是用来分解进程的,可以分解出一个子进程,然后按顺序执行。而我想要的是多线程的,需要new一个线程 用来同步执行。又是一下午,发现搞不定,所以把我的wdcp系统废掉,重做了一下。 所以呢,就又开始扩展多线程的组件,pthreads,这么一搞,又是一天,md,然后还写了测试程序,最后回想起,多线程是 cpu 轮询执行,并不是并行操作,感觉这次 要凉。 没错,已经凉凉了。 所以呢 今天 又了解了一下 进程和线程的 区别,后来使用进程测试了下,好像可以了,现在不搞了,缓一缓。 --------------------------------------------------------------------------- 2018-04-18 论文写了很多 好久没有更新了。目前的情况,可以搞定了多进程方式搞定。然后就开始写论文了,写了7、8稿了。老师都不太满意,一直再改,等搞稳了,再来更新吧。最后把所有的实现流程都整理一下。 2018-04-25 现在终于定稿了,剩下的就是细节了。发现了一个很重要的问题,在毕设的初步阶段(有了比较详细的实现流程以及能讲清楚想做什么),你一定要跟老师沟通过,看看他想要你做的东西,和你想做的东西是不是一致的。以为仅仅凭借一个任务书,一个开题报告,并不能确定性的将问题定位好,还是需要进一步的沟通,免得最后出问题。 每一次小的论文修改(不涉及到整体的方向和框架的时候,都是1.x,当老师说你写的不是我要求的东西时,就会出现2.0)。小版本改动和大版本改动。此处想说,老师比较忙,一般不会很认真的看你的需求,(此处单指我们老师)所以就出现了最终的论文我只写了硬件部分,想哭。 file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps2739.tmp.jpg 关于论文,我觉得现在贴上来有点太早了。还是再等几天再说。如果有什么需要帮助的,可以留言,记得留下联系方式哈。 最后贴一张查重报告吧,一次过,开心、毕竟是自己写的。 file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps273A.tmp.jpg 后期也帮同学做过人工降重,发现也没有多难。一个39%直接降到16%。一个43%直接降到19%。还有一个55%直接到10%。 而且我对硬件和软件的开发都比较了解,所以降重也不会语义不同,而是换一些更专业的词汇,毕竟做了类似课题的都是大学生,多数人写的文章真是是菜,用到专业词汇的比较少。 最后的实现细节跟之前的描述有些区别。 首先,车锁部分即硬件部分,使用了STM32F103作为处理器,搭载一个GPRS模块(自备SIM卡),通过IO口连接电控锁。车锁上贴一个二维码,二维码的信息是STM32F103的序列号。 服务器上有微信端,微信端开发扫码功能,通过二维码生成网站生成一个带特定信息的二维码(此处的特定信息是STM32F103的序列号,这样就能唯一的确定车锁)。当用户使用公众号扫描车锁上的二维码时,将STM32F103的序列号发送到服务器。 服务器获取序列号,查询数据库中的记录,若存在该序列号记录,就将记录中的一个字段Status置位1,标识该车锁应该开启。 Socket服务始终开启着,车锁开启之后,就会主动连接Socket Server,每一个车锁与SocketServer建立连接,都开启一个新进程对连接进行操作。在进程中,车锁端将车锁的STM32F103序列号发送到SocketServer,SocketServer获取到序列号,查询数据库是否存在该记录,该记录的Status值是否为1,若为1,则向该车锁发送解锁指令。否则,sleep一秒,再次查询。 车锁的GPRS模块接收到服务器发送的解锁指令,通过串口发送到STM32F103处理器,处理器通过IO置位控制连接的电控锁开启。 就这些啦!感觉做的并不是很好,但是有点力不从心,真正做的时候,还是没有说的这么简单。 最后贴一下,共享单车项目源码 希望小伙伴有什么 类似的想法和项目的 也可以跟我交流,对我物联网这块 我还是很感兴趣的。 共享单车智能锁设计2018年03月18日 16:04:37 yancy_扬希 阅读数:3054 版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/ca1m0921/article/details/79601275 -------------------------------------------------------------------------------- 2018-05-15 毕设答辩结束20天后: 毕业设计已经完成,且答辩结束。回过头来认真梳理下开发的流程和使用到的技术,写一写遇到的重点问题,然后贴上来所有的源码以及对项目使用到的开发环境和开发工具进行必要的说明。一是为了做个了结,二是为了更好的帮助到你们。 -------------------------------------------------------------------------------- 2018-04-15 毕业设计进入关键时刻: 再此整理下,毕设涉及到的相关技术,在毕设基本成型的时候,认真整理一下各部分的知识。 一马当先的Socket,万物互联的Socket。使用Socket通信建立起IP地址之间的通信,将SIM连接到网络上。 安全传输的TCP,牢靠的TCP。对于TCP or UDP,其实凭我个人感觉,以后网速越来越稳定之后,更偏爱UDP。 多线程,多线程和多进程。在此项目中,最后做功能实现时,有点急躁,多线程并没有成功,多进程可以用之后,就不再想尝试多线程了。那时候感觉整个项目的东西有点多,乱,轻易不敢下手了。而且截止时间也不多了。 微信公众号开发,最火的小程序开发。对于客户端选择了微信,一是因为老师说项目的重点是客户端,二是当时用微信构想了下需要的功能,觉得勉强能够用,所以就没有尝试开发app。 最好的操作系统,Linux基本操作。项目没有涉及到太多Linux的东西。如果说有,那就是云服务器是CnetOS的,Ngrok+MySQL+PHP搭建在服务器上,因为忘记了数据库密码致使把整个环境重搭了一下,后来因为出现未知的错误,以及安装多进程、多线程插件时,需要修改一些东西,被迫重搭系统,总的来说,整个环境至少重做了4次。在这4次重做过程中,我对Linux的命令操作有点基本的认识。 -------------------------------------------------------------------------------- 2018-03-16 毕设初起步: 我的毕设开始于2018/3/5,毕设已经做了有两周了,一直没有整理自己的进度,现在来补充下,以便以后能回过头来回顾下。 先上任务书: file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps273B.tmp.jpg 再上开题报告,算了,开题报告还是先不上了,等答辩结束再上,免得出事端。 file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps274C.tmp.png 整体的设计思路跟共享单车的实现原理相似(此处不敢用一致,毕竟不清楚其具体原理)。 技术选型:STM32F103+SIM900(GPRS模块)+腾讯云的云服务器(学生优惠)+微信公众号(测试号开发) 开发语言:单片机使用了C语言,服务器使用的Centos7,服务器开发语言是PHP,通信方式暂定为Socket。 整个系统的架构图: file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps274D.tmp.jpg 第一周完成的工作主要有:配置好服务器,搭建一个可以满足自己使用的微信公众号,完成基本的功能。 ①、后来发现微信公众号没有自定义菜单功能,遂采用了微信公众号平台的测试账号,刚开始对自己的云服务不太了解,使用了 百度的EBA应用引擎,一天0.4元,很实惠,千万注意是 基础版并不是专业版,专业版9.9,当初自己买错了,还好没有操作,直接就申请退款了。 关于微信测试账号的设置,请参考,这个博客,在此谢过,真的帮到了我很多。在此谢过博主大佬。 zc的救赎:②、服务器的配置,因为要使用 SIM与服务器通信,之前学习过Socket通信,觉得Socket通信可以实现此功能,就直接确定了使用Socket通信,我之前是学习Java的,对PHP没有好感,也没有兴趣,md,这个一点的不严谨的语言,真的是不想学,但因为我的云服务器当初是用来搭建博客的,使用的是PHP的开发环境和WDCP的面板,所以就想用PHP来搞一下Socket,反正Socket也不太难搞。这里主要涉及到 端口的开放和监听,让我搞了两天,真的有点头大,百度查询了很多材料,都是众说纷纭吧。其间,给自己的服务器安装了iptables、telnet等等,当时安装的时候,真的不知道这是干什么用的,后来敲了两天的Linux命令,才开始慢慢的有了点头绪,并质疑过是腾讯后台管理关闭了我要使用的1337端口,连续两次提交工单到腾讯云,真的是非常的抱歉。 关于端口的开放和监听,我自己的理解是:服务器默认开放了所有的端口,而我使用了端口扫描(百度就能找到有个网站提供这个服务,指定IP和端口检测端口是否可用),去扫描我的服务器的1337端口,检测的结果是端口是关闭状态,是因为服务器端没有进程在监听1337端口,后来写了个Socket服务监听1337端口在服务器上运行,就能扫描到1377端口已开启,此时也可以看到通过putty远程连接服务器查看1377端口已在监听状态。(特别注意要修改iptables防火墙,要同意开放指定的端口,此处针对CentOS7:关闭默认的防火墙firewall,使用iptbles,firewall是7版本才具有的防火墙,网上相关的操作没有iptables的多,技术人员偏爱旧东西,可靠)。 所以开放端口只需要修改防火墙文件并重启服务器(此处好像是只需要重启apache就行),然后写个socket监听某端口。 现在的进度为: 一方面:已经可以完成微信的扫码,发送单车id到服务器端,服务器获取id查询数据库找到对应的ip地址, 另一方面:完成启动socket监听,获取客户端的ip和端口, 下一步的进度:连接起两个功能区,实现基本的功能。 需要特别攻关的方面是 socket监听如何实现多线程监听,因为对PHP不太熟悉,不敢搞太多的东西,因为调试什么的都很复杂,自己有点怵。 暂时先写这些吧,昨天看了php的基本语法,学习了些全局变量,数据类型等东西,今天还是再看一些吧,然后把自己松散的页面结构用类和方法封装起来,然后写一写后台的管理页面,再完成基本的功能,那就很开心了。 关于多线程,和GPS位置,调用地图组件,等完成这些之后,再扩展吧, 持续更新,私以为这就是NB-loT的缩影,可以应付一般的智能设备,主要是采用了SIM的GPRS通信,完成了独立硬件硬件的联网功能,也就是物联网的概念吧。 2018/03/20 23:41 刚刚玩完吃鸡游戏,临时更两句吧, 今天开始调试 服务器和 硬件的通信(STM32+SIM900),实现的目的是 服务器开启Socket服务,监听1337端口,客户端(STM32+SIM800)创建一个Socket客户端连接服务器(IP+Port)。服务端等待客户端连接,当客户端连接上之后,检测客户端的IP,将解锁指令写入Socket连接(类似于管道或者缓冲区的东西)中,客户端从连接中读取数据,检测指令进行判断,通过STM32的IO发送指令给电控锁(其实是继电器),继电器控制电控锁开启,从而完成开锁功能。 目前,要解决的问题是:客户端持续向服务器发送数据,以保证ip地址不会改变,这也是socket的主要问题,要完成长连接,通过心跳包。 测试结果:服务器端Socket要持续监听端口,保持客户端能持续的联网,ip地址不变,重点测试是不是客户端启动中,ip就不会变,与服务器无关。经过测试,GPRS一旦与网络建立连接,IP地址就不会改变。 学习下,继电器和电机锁的原理,接触下。 首要任务,先调通整个系统,然后再完整以上功能。加油。 ------------------------------------------------------------------------------- 2018/03/21 09:23 打卡上班了,先刷一些嘻哈、吾爱、知乎,再搞。 继电器的东西,搞清楚了些。老师没给我电机锁,我使用的STM32集成板看了好久原理图,没找到5V的输出,只有3.3V的输出,而继电器使用的是5V的输入,当使用3.3V的输出接入继电器时,继电器的信号灯会亮,继电器内部并不会产生磁性而开启或断开(也就说只是有个信号灯,效果会折半,但是还是可以用的)。 整个系统 还是没有调通,因为整体的进度还可以,就去帮同学看了些她的毕设,基于arduino的红外避障小车实现,因为电子竞赛的时候也做个小车的题目,没有使用过arduino,所以有些兴趣。可惜她买的是全套的资料,包括了车架、arduino板子、红外模块、电机控制以及一部分代码,都包含了。只需要安装好,烧录下随单附赠的程序就行了,烧完程序,小车的红外模块正常,小车的车轮不转,后来给马达单独接上电池,发现马达是好的,后来换了6节南孚电池,小车才开始跑起来。果然南孚就是吊。小车目前已经可以完成红外避障了,就是红外模块的感应不太敏感,总是碰到东西之后才会转弯,反应有点慢,初步方向是:调节PWM调节转速使小车的速度降下来,这样转弯就会流畅一些吧。不过,不管她了。 发现对于整个系统还是有些不清楚的地方,所以还是梳理一下吧。 整体:要实现的功能从 微信端扫码开始,经过服务器,服务器发送指令到GPRS模块,GPRS模块通过串口发送到STM32F103处理器,STM32F103通过IO口置位控制继电器,继电器达到条件可触发车锁开关。 第一部分:目前微信端可以通过扫码发送单车id到服务器,服务器可以提取到id编号,此处通过Utils的方法获取单车id,然后通过数据库查询检索该id的条目,获取其对应的IP地址。 第二部分:然后调用Server.php的find($ip)方法,使用数据库查询的ip做参数,find方法中启动Socket服务器监听,通过socket_accept方法接受客户端的连接,等客户端连接上之后,获取客户端的ip地址,用参数ip和客户端的ip做比较。 if _:如果一致,就向socket通道里写入标识号(目前是“1”),然后sleep一小会,以使得客户端能够读取数据,然后SIM800模块通过串口3发送数据到STM32处理器,处理器判断数据满足条件,然后将某个IO口置位(目前是将一个控制led灯的口置位为1), 此处插入一下,继电器和STM32的连接:STM32的5V 连接 继电器的 VCC,STM32的GND 连接 继电器的GND,使用一个IO口连接继电器的IN1,IN1用来控制继电器的触发条件。目前的连接方式是 ON(常开端)接STM32的 5V,COM(公开端)接STM32的GND,这样连接的话,不需要接其他的的东西,继电器就有反应,会听到“滴”的一声。明天会问老师要电机锁,然后将ON和COM接入电机锁,测试下是不是能成功。 else_:如果ip匹配不一致,说明扫得单车id和目前连接到Socket服务器的客户端不是同一辆车,此时就断开Socket连接,等到下一个客户端连接,再检测ip是否一致,一致就执行if的操作。 还是心理有畏惧,不敢接触多线程,如果启动Socket服务器监听端口,并接受每个客户端的连接,同时接受多个客户端的数据传输,就会产生多个进程,客户端定时发送心跳包到服务器端,服务器端实时检测客户端的ip地址,再匹配数据库的ip地址,发送指令的话,如果有100个客户单(即单车)就会产生100个进程,几乎是实时通信,服务器可能会宕机,参考共享单车的实现,如果一个服务器同时连接几百或者上千个客户端连接的话,会不会宕机,我的实现思路是不是正确,值得考虑。 晚安了。 --------------------------------------------------------------------------- 2018/3/22 20:09 下班了。把昨天码过的字,认真看了下,今天还是没有成功,想哭,旁边选了单片机毕设题目的小哥哥小姐姐们,某宝成品100搞定,而我竟然折腾了Centos7云服务器,接触了花生壳,学习了用自有服务器开发微信公众号,PHP的基本语法,Socket通信。尽管现在还是有点乱,但是我相信多学点东西总是有好处的。 目前的情况是,云服务器有两套独立的微系统,支持微信公众号的功能 以及 支持Socket服务。 微信系统:用户通过公众号扫码,发送id到服务器,服务器获取id号,查询数据库找到id对应的IP地址(车锁的SIM动态IP地址),发送给Socket服务器。 Socket服务器:接受到经微信系统查询数据库找到的IP地址,启动Socket监听,获取连接的客户端IP地址,与数据库IP地址匹配,成功则将解锁指令写到Socket通道中。 单车的智能车锁:车锁的SIM模块会接收到数据,发送到STM32的处理器,STM32控制继电器,从而完成开锁。 我在尝试着实现的时候,发现了非常恐怖的事情,云服务器的两个系统(我自己写的系统)是相对独立的,特别是微信端的系统,用来支撑微信公众号的功能,我尝试着在php中include一个文件,从而调用启动Socket的方法,直接完成一系列的功能执行,但是一旦添加了socket_bind的方法时,公众号总会异常。下面贴一下代码,很难讲清楚。 Utils.php :主要是logger起作用,用来查询数据库。 00001. <?php 00002. 00003. include 'Server.php'; 00004. 00005. 00006. 00007. class Utils{ 00008. 00009. /** 00010. 00011. * 打印日志到log.xml 00012. 00013. * @param $log_content:日志内容 00014. 00015. * @param string $type:日志来源,默认‘用户’ 00016. 00017. */ 00018. 00019. public static function logger($log_content, $type = '用户') 00020. 00021. { 00022. 00023. $xml_log_content = simplexml_load_string($log_content); 00024. 00025. $id = $xml_log_content->Content; 00026. 00027. echo $id."\n\r"; 00028. 00029. $con = mysql_connect("localhost","root","Mysql2966031005"); 00030. 00031. 00032. 00033. mysql_select_db("my_db", $con); 00034. 00035. 00036. 00037. $rs = mysql_query("SELECT * from bike where id = '$id'"); 00038. 00039. 00040. 00041. $row = mysql_fetch_array($rs); 00042. 00043. 00044. 00045. $mysql_id = $row['id']; 00046. 00047. $ip = $row['ip']; 00048. 00049. 00050. 00051. Server::find($ip); 00052. 00053. 00054. 00055. mysql_close($con); 00056. 00057. 00058. 00059. $max_size = 3000; 00060. 00061. $log_filename = "./log.xml"; 00062. 00063. if (file_exists($log_filename) and (abs(filesize($log_filename)) > 00064. 00065. $max_size)) { 00066. 00067. unlink($log_filename); 00068. 00069. } 00070. 00071. 00072. 00073. file_put_contents($log_filename, $log_content."\n\r".$id.$ip."\n\r", FILE_APPEND); 00074. 00075. } 00076. 00077. /** 00078. 00079. * 获取access_token 00080. 00081. * @return mixed 00082. 00083. */ 00084. 00085. public static function get_access_token() 00086. 00087. { 00088. 00089. $appid = "wxfd8fd157a47bc539"; //需替换成你的appID 00090. 00091. $appsecret = "9efe8ba722e7506f1c1aed11740e64b3"; //需替换成你的appsecret 00092. 00093. $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&". 00094. 00095. "appid=$appid&secret=$appsecret"; 00096. 00097. $output = Utils::https_request($url); 00098. 00099. $jsoninfo = json_decode($output, true); 00100. 00101. return $jsoninfo["access_token"]; 00102. 00103. } 00104. 00105. /** 00106. 00107. * 发送请求 00108. 00109. * @param $url:地址 00110. 00111. * @param null $data:post的数据 00112. 00113. * @return mixed:请求返回的结果 00114. 00115. */ 00116. 00117. public static function https_request($url, $data = null) 00118. 00119. { 00120. 00121. $curl = curl_init(); 00122. 00123. curl_setopt($curl, CURLOPT_URL, $url); 00124. 00125. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 00126. 00127. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); 00128. 00129. if (!empty($data)){ 00130. 00131. curl_setopt($curl, CURLOPT_POST, 1); 00132. 00133. curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 00134. 00135. } 00136. 00137. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 00138. 00139. $output = curl_exec($curl); 00140. 00141. curl_close($curl); 00142. 00143. return $output; 00144. 00145. } 00146. 00147. } 00148. Server.php: 主要是 find方法, 00001. <?php 00002. 00003. class Server{ 00004. 00005. /* 主要有 find($ip)方法,接收通过Utils传递过来的IP地址, 00006. 00007. * 启动 socket监听,等待客户端连接,获取连接的客户端IP地址 00008. 00009. * 与 Utils页面传递过来的地址比较,若 一致, 00010. 00011. * 而向 IP地址发送 开锁 指令,通过TCPc传输发送GPRS数据到 SIM卡 00012. 00013. * */ 00014. 00015. public static function find($ip){ 00016. 00017. $ip_local = "123.23.13.250"; 00018. 00019. set_time_limit(0); 00020. 00021. //设置IP和端口号、 00022. 00023. echo "start....\r\n"; 00024. 00025. printf("start...\r\n"); 00026. 00027. $address = "10.141.75.98"; //要连接的服务器地址 00028. 00029. $port = 1337; //调试的时候,可以多换端口来测试程序 00030. 00031. 00032. 00033. echo "set_IP and set_port...\r\n"; 00034. 00035. /** 00036. 00037. * 创建一个SOCKET 00038. 00039. * AF_INET=是ipv4 如果用ipv6,则参数为 AF_INET6 00040. 00041. * SOCK_STREAM为socket的tcp类型,如果是UDP则使用SOCK_DGRAM 00042. 00043. */ 00044. 00045. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00046. 00047. //绑定到socket端口 00048. 00049. $result = socket_bind($sock, $address, $port) or die("socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00050. 00051. echo "start_listening....../n/r"; 00052. 00053. //开始监听 00054. 00055. $result = socket_listen($sock, 4) or die("socket_listen() 失败的原因是:" . socket_strerror(socket_last_error()) . "/r/n"); 00056. 00057. echo "OK\nBinding the socket on $address:$port ... ....\r\n"; 00058. 00059. // 等待 客户端 连接 00060. 00061. 00062. 00063. $time = 0; 00064. 00065. do { 00066. 00067. $msgsock = socket_accept($sock); 00068. 00069. // 获取客户端 数据 (IP地址 和 端口号) 00070. 00071. socket_getpeername($msgsock, $addr, $por); 00072. 00073. 00074. 00075. echo "<br/>" . $addr ."==". $ip . "<br/>"; // 223.104.13.203 00076. 00077. // 拿 客户端的 IP 地址 和 Utils 传递过来的 IP地址 比较,一致而 发送指令到 该 IP地址 00078. 00079. break; 00080. 00081. // 如果 数据的ip与获取的客户端ip一致,就发送结果指令到 客户端 00082. 00083. // 否则,就返回accept 继续执行等待客户端 00084. 00085. if ($addr == $ip) { 00086. 00087. $length = socket_write($msgsock, "2"); // 将开锁指令发送发到 客户端 00088. 00089. echo "success!"; 00090. 00091. echo $length; 00092. 00093. sleep(3); 00094. 00095. }else{ 00096. 00097. socket_close($msgsock); 00098. 00099. continue; 00100. 00101. } 00102. 00103. sleep(3); 00104. 00105. echo "after_sleep"; 00106. 00107. 00108. 00109. socket_close($msgsock); 00110. 00111. $time++; 00112. 00113. echo "$time == ".$time; 00114. 00115. }while($time<2); 00116. 00117. socket_close($sock); 00118. 00119. } 00120. 00121. } 00122. 00123. 00124. 00125. 00126. 00127. ?> 00128. 本来的想法是,在Utils.php中调用一下Server.php中的find($ip)方法,将数据库的ip做参数传递过去,然后find($ip)方法中启动Socket监听,等待客户端的连接,获取客户端ip与参数ip做比较,一致则写标识符到Socket通道中,否则退出循环,再等待客户端连接。 每次在Utils.php的logger方法中写 $server->find($ip)或者Server::find($ip)方法,公众号总是会出现异常,尽管我写了include或require,甚至_once都试过了,后来测试,将实现功能的代码直接加入Utils.php的方法中,发现在socket_bind(...)方法时,公众号会出现异常,遂放弃了。 现在的思路比较难受,属于绕了个弯。因为两个系统独立,不能传递参数,当时想过换个app去实现,但时间比较少了,不想再调试其他的东西,怕时间不够用。所以想到了数据库,我可以将Utils.php的参数存储到数据库,然后在Server.php页面将数据取出来,这样岂不是可以实现功能,简直爽歪歪。 从而开始引入了MySQL。遂写成了这样,哈哈哈哈。 Utils.php 00001. <?php 00002. 00003. 00004. 00005. class Utils{ 00006. 00007. /** 00008. 00009. * 打印日志到log.xml 00010. 00011. * @param $log_content:日志内容 00012. 00013. * @param string $type:日志来源,默认‘用户’ 00014. 00015. */ 00016. 00017. public static function logger($log_content, $type = '用户') 00018. 00019. { 00020. 00021. $xml_log_content = simplexml_load_string($log_content); // 获取xml字符串 00022. 00023. $Content = $xml_log_content->Content; // 获取其中的 Content内容 00024. 00025. $id_weixin = $Content; 00026. 00027. $con = mysql_connect("localhost","root","Mysql2966031005");// 连接数据库 00028. 00029. mysql_select_db("my_db", $con); // 选择数据库 00030. 00031. $rs = mysql_query("SELECT * from bike where id = '$id_weixin'");// 执行查询操作 00032. 00033. $row = mysql_fetch_array($rs); // 获取查询的 结果,(以数组形式存储) 00034. 00035. $id = $row['id']; //获取 数据库对应条目的 ID 00036. 00037. $ip = $row['ip']; // 获取 数据库对应条目 的 IP 00038. 00039. 00040. 00041. mysql_query("update bike set status = '1' where id = '$id_weixin'");// 将对应的id条目的status设置为1 00042. 00043. // 调用Server的方法启动,Socket监听 00044. 00045. 00046. 00047. mysql_close($con); //关闭数据库连接 00048. 00049. 00050. 00051. $max_size = 3000; 00052. 00053. $log_filename = "./log.xml"; 00054. 00055. if (file_exists($log_filename) and (abs(filesize($log_filename)) > $max_size)) { 00056. 00057. unlink($log_filename); 00058. 00059. } 00060. 00061. file_put_contents($log_filename, $log_content."\n\r".$ip."hh\n\r", FILE_APPEND); 00062. 00063. 00064. 00065. set_time_limit(0); 00066. 00067. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00068. 00069. $result = socket_bind($sock, "10.141.75.98", 1337) or die("socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00070. 00071. $result = socket_listen($sock, 4); 00072. 00073. } 00074. 00075. /** 00076. 00077. * 获取access_token 00078. 00079. * @return mixed 00080. 00081. */ 00082. 00083. public static function get_access_token() 00084. 00085. { 00086. 00087. $appid = "wxfd8fd157a47bc539"; //需替换成你的appID 00088. 00089. $appsecret = "9efe8ba722e7506f1c1aed11740e64b3"; //需替换成你的appsecret 00090. 00091. 00092. 00093. $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&". 00094. 00095. "appid=$appid&secret=$appsecret"; 00096. 00097. $output = Utils::https_request($url); 00098. 00099. $jsoninfo = json_decode($output, true); 00100. 00101. return $jsoninfo["access_token"]; 00102. 00103. } 00104. 00105. /** 00106. 00107. * 发送请求 00108. 00109. * @param $url:地址 00110. 00111. * @param null $data:post的数据 00112. 00113. * @return mixed:请求返回的结果 00114. 00115. */ 00116. 00117. public static function https_request($url, $data = null) 00118. 00119. { 00120. 00121. $curl = curl_init(); 00122. 00123. curl_setopt($curl, CURLOPT_URL, $url); 00124. 00125. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 00126. 00127. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); 00128. 00129. if (!empty($data)){ 00130. 00131. curl_setopt($curl, CURLOPT_POST, 1); 00132. 00133. curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 00134. 00135. } 00136. 00137. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 00138. 00139. $output = curl_exec($curl); 00140. 00141. curl_close($curl); 00142. 00143. return $output; 00144. 00145. } 00146. 00147. } 00148. Server.php: 00001. <?php 00002. 00003. 00004. 00005. Server::find(); 00006. 00007. class Server{ 00008. 00009. public static function find(){ 00010. 00011. set_time_limit(0); 00012. 00013. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00014. 00015. $result = socket_bind($sock, "10.141.75.98", 1337) or die("socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); 00016. 00017. $result = socket_listen($sock, 4); 00018. 00019. 00020. 00021. $time = 0; 00022. 00023. do { 00024. 00025. $msgsock = socket_accept($sock); 00026. 00027. socket_getpeername($msgsock, $addr, $por); 00028. 00029. 00030. 00031. echo "<br/>" . $addr . "<br/>"; // 223.104.13.203 00032. 00033. // 拿 客户端的 IP 地址 和 Utils 传递过来的 IP地址 比较,一致而 发送指令到 该 IP地址 00034. 00035. $ip_status_1 = Server::find_status_1(); 00036. 00037. // 如果 数据的ip与获取的客户端ip一致,就发送结果指令到 客户端 00038. 00039. // 否则,就返回accept 继续执行等待客户端 00040. 00041. if ($addr == $ip_status_1) { 00042. 00043. $length = socket_write($msgsock, "1"); // 将开锁指令发送发到 客户端 00044. 00045. echo "success!"; 00046. 00047. echo $length; 00048. 00049. sleep(3); 00050. 00051. }else{ 00052. 00053. socket_close($msgsock); 00054. 00055. $time++; 00056. 00057. continue; 00058. 00059. } 00060. 00061. sleep(3); 00062. 00063. echo "after_sleep"; 00064. 00065. socket_close($msgsock); 00066. 00067. $time++; 00068. 00069. }while($time<20); 00070. 00071. socket_close($sock); 00072. 00073. } 00074. 00075. 00076. 00077. public static function find_status_1(){ // 获取status为1的条目,并获取其 ip 00078. 00079. $con = mysql_connect("localhost", "root", "Mysql2966031005");// 连接数据库 00080. 00081. mysql_select_db("my_db", $con); // 选择数据库 00082. 00083. while (true) { 00084. 00085. $rs = mysql_query("SELECT * from bike where status = '1'");// 执行查询操作 00086. 00087. if (!($rs == null)) { // 如果status = 1 的条目存在,执行 if 00088. 00089. $row = mysql_fetch_array($rs); // 获取查询的 结果,(以数组形式存储) 00090. 00091. //$id = $row['id']; //获取 数据库对应条目的 ID 00092. 00093. $ip = $row['ip']; // 获取 数据库对应条目 的 IP 00094. 00095. echo "IF_Module"; 00096. 00097. return $ip; // 返回 IP地址 到 find 方法 00098. 00099. } else { 00100. 00101. echo "else_Module"; 00102. 00103. return "else"; 00104. 00105. } 00106. 00107. } 00108. 00109. } 00110. 00111. } 00112. 00113. ?> 00114. 这样,我觉得我可以 先启动Socket服务器,实时监听客户端的连接,然后查询数据库,是否有哪条记录的status被置位为1,说明该IP地址匹配的二维码被扫码了,发现之后,就获取ip,与连接的客户端匹配,匹配成功,完成开锁。 没有测试还是没谱,希望能有个好的结果吧。对于服务器端总是会出现不定时间的Listen状态,有点搞不明白。感觉自己写的sleep并没有影响。 就这样吧,晚安吧。 ----------------------------------------------------------------------------------------- 2018/3/23 11:50 经过调试,目前可以使用,但是还存在一些小的问题,还需要完善,不管怎么说,还是蛮开心的,尽管在意料之中,可依然兴奋。 给毕设指导老师看过了,测试还是正常的,开心。估计大体就这样了,从开始折腾,到模块测试,再到集成测试,觉得也花了些时间,毕竟防火墙的端口开放,就搞了两天。后来的系统集成,也是存在各种问题,现在折腾到基本功能实现,已经有点筋疲力尽了,不准备朝着这方面就业,那就做成这样,就了结吧。 争取这周完成毕设论文终稿,然后整理出毕设的指导文档,就当是给老师留给念想吧,也给学弟学妹提供个肩膀。我觉得我的毕设应该叫 互联网+,因为昨天看到周鸿祎前辈讲 +互联网 和 互联网+,共享单车的产品思路 是为了颠覆传统的设计,所以,我是互联网+, 结束之后,我要拾起我的Java,I'm ready。 给老师看了下我的 论文,讲了下我的毕设,老师说 让我把多线程做一下,要不有点太简单了。 然后就查了 PHP对多线程的支持,看到说 pcntl 的php组件,然后就想搞这个吧,我觉得也挺简单的。我的服务器是用的WDCP平台完成的 lanmp 集成安装,然后就是找wdcp如何安装 pcntl 确实有些说法,也这折腾了,发现 服务器上有 多个php.ini,不知道是哪个起作用,后来就有点烦躁,毕竟对Linux还有不太熟悉,而且平台是 通过WDCP来管理的,总觉得它们有个托管什么的,不知道整个系统是怎么配置的,到底是在wdcp还是在linux设置的权限大。 折腾了好久,通过重新搭建环境,在MySQL的安装上做一些操作,最终还是安装好插件了。 不管了,开始搞我的多线程, 搞定之后,就可以答辩了,答辩结束 就可以自由了。飞起来了。 等我答辩结束,把这些东西整理一下,再认真梳理下,物联网的原理。 好了,才搞完pcntl 发现不是我想要的东西,pcntl是用来分解进程的,可以分解出一个子进程,然后按顺序执行。而我想要的是多线程的,需要new一个线程 用来同步执行。又是一下午,发现搞不定,所以把我的wdcp系统废掉,重做了一下。 所以呢,就又开始扩展多线程的组件,pthreads,这么一搞,又是一天,md,然后还写了测试程序,最后回想起,多线程是 cpu 轮询执行,并不是并行操作,感觉这次 要凉。 没错,已经凉凉了。 所以呢 今天 又了解了一下 进程和线程的 区别,后来使用进程测试了下,好像可以了,现在不搞了,缓一缓。 --------------------------------------------------------------------------- 2018-04-18 论文写了很多 好久没有更新了。目前的情况,可以搞定了多进程方式搞定。然后就开始写论文了,写了7、8稿了。老师都不太满意,一直再改,等搞稳了,再来更新吧。最后把所有的实现流程都整理一下。 2018-04-25 现在终于定稿了,剩下的就是细节了。发现了一个很重要的问题,在毕设的初步阶段(有了比较详细的实现流程以及能讲清楚想做什么),你一定要跟老师沟通过,看看他想要你做的东西,和你想做的东西是不是一致的。以为仅仅凭借一个任务书,一个开题报告,并不能确定性的将问题定位好,还是需要进一步的沟通,免得最后出问题。 每一次小的论文修改(不涉及到整体的方向和框架的时候,都是1.x,当老师说你写的不是我要求的东西时,就会出现2.0)。小版本改动和大版本改动。此处想说,老师比较忙,一般不会很认真的看你的需求,(此处单指我们老师)所以就出现了最终的论文我只写了硬件部分,想哭。 file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps276D.tmp.jpg 关于论文,我觉得现在贴上来有点太早了。还是再等几天再说。如果有什么需要帮助的,可以留言,记得留下联系方式哈。 最后贴一张查重报告吧,一次过,开心、毕竟是自己写的。 file:///C:\Users\Administrator\AppData\Local\Temp\ksohtml\wps276E.tmp.jpg 后期也帮同学做过人工降重,发现也没有多难。一个39%直接降到16%。一个43%直接降到19%。还有一个55%直接到10%。 而且我对硬件和软件的开发都比较了解,所以降重也不会语义不同,而是换一些更专业的词汇,毕竟做了类似课题的都是大学生,多数人写的文章真是是菜,用到专业词汇的比较少。 最后的实现细节跟之前的描述有些区别。 首先,车锁部分即硬件部分,使用了STM32F103作为处理器,搭载一个GPRS模块(自备SIM卡),通过IO口连接电控锁。车锁上贴一个二维码,二维码的信息是STM32F103的序列号。 服务器上有微信端,微信端开发扫码功能,通过二维码生成网站生成一个带特定信息的二维码(此处的特定信息是STM32F103的序列号,这样就能唯一的确定车锁)。当用户使用公众号扫描车锁上的二维码时,将STM32F103的序列号发送到服务器。 服务器获取序列号,查询数据库中的记录,若存在该序列号记录,就将记录中的一个字段Status置位1,标识该车锁应该开启。 Socket服务始终开启着,车锁开启之后,就会主动连接Socket Server,每一个车锁与SocketServer建立连接,都开启一个新进程对连接进行操作。在进程中,车锁端将车锁的STM32F103序列号发送到SocketServer,SocketServer获取到序列号,查询数据库是否存在该记录,该记录的Status值是否为1,若为1,则向该车锁发送解锁指令。否则,sleep一秒,再次查询。 车锁的GPRS模块接收到服务器发送的解锁指令,通过串口发送到STM32F103处理器,处理器通过IO置位控制连接的电控锁开启。 就这些啦!感觉做的并不是很好,但是有点力不从心,真正做的时候,还是没有说的这么简单。 最后贴一下,共享单车项目源码 希望小伙伴有什么 类似的想法和项目的 也可以跟我交流,对我物联网这块 我还是很感兴趣的。 |
© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed