<?php
set_time_limit(0);
error_reporting(E_ERROR);require_once 'conf/db_conf.php';$address = '127.0.0.1';
$port = 1919;$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>3, "usec"=>0 ));
socket_set_option($sock, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>3, "usec"=>0 ));
socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);socket_bind($sock, $address, $port);
socket_listen($sock);$clients = array($sock);$ark_port_map = array();while (true){
  $loop_start_time = time();
  
  $read = $clients;
  
  if (count($read) < 1){
    break;
  }
  
  if (socket_select($read, $write = NULL, $except = NULL, NULL) < 1){
    continue;
  }
  
  /**
   * 建立连接
   */
  if (in_array($sock, $read)){
    $clients[] = $new_sock = socket_accept($sock);
    
    $key_in_read = array_search($sock, $read);
    unset($read[$key_in_read]);
  }
  
  /**
   * 如果是首次连接,客户端发送01,
   * 将建立连接的设备的设备号与其ip和端口作匹配。
   *
   * 将要开启的设备号和盒子号数组被保存到了数据库中,
   * 数组格式为array('arkid' => array('cabid1', 'cabid2'))。
   * 根据数组中匹配的设备号发送开锁数据到设备客户端。
   *
   * 将要更新的设备号数组被保存到了数据库中,
   * 数组格式为array('0001', '0003')。
   * 根据数组中匹配的设备号发送更新数据到设备客户端。
   */
  
  /**
   * 1.查询将要开启的盒子的json数组
   * 2.查询将要更新的设备的json数组
   */
  $msi_cached = get_connection();
  $sql_cached = "select cached_goods, updating_arks from imicms_yh_saling_goods";
  $res_cached = $msi_cached->query($sql_cached);
  $cached_goods_json = '';
  $updating_arks_json = '';
  if ($datarow_cached = $res_cached->fetch_array()){
    $cached_goods_json = $datarow_cached[0];
    $updating_arks_json = $datarow_cached[1];
  }
  $msi_cached->close();
  
  $opening_arksandcabs_arr = json_decode($cached_goods_json, true);
  $updating_arks_arr = json_decode($updating_arks_json, true);
  
  foreach ($read as $read_sock){
    socket_getpeername($read_sock, $read_sock_ip, $read_sock_port);
    
    $recv_count=socket_recv($read_sock, $sock_data, 2048, 0);
    
    /**
     * 如果从客户端接收数据失败,
     * 并且从上一次接受数据到现在时间大于60秒,
     * 则服务器认为该客户端已断开连接,
     * 将该链接从clients数组中去除,
     * 同时将链接从ip端口-设备匹配数组中去除。
     */
    if (($recv_count < 0 || $recv_count == false) && time() - $loop_start_time > 60){
      $key_in_clients = array_search($read_sock, $clients);
      unset($clients[$key_in_clients]);
      unset($ark_port_map[$read_sock_ip . $read_sock_port]);
      continue;
    }elseif ($recv_count < 0 || $recv_count == false){
      continue;
    }    $key_arkid = $ark_port_map[$read_sock_ip . $read_sock_port]['arkid'];
    
    $sock_data = trim($sock_data);
    $sock_data_arr = explode(',', $sock_data);
    
    /**
     * 如果发来的数据为01,将建立连接的设备的设备号与其ip和端口作匹配。
     * 处理发送过来的数据时,若有返回数据,
     * 返回数据完成均跳转到下一次foreach循环
     */
    $recv_num = $sock_data_arr[2];
    if ($recv_num == '01'){
      $new_sock_arkid = $sock_data_arr[1];
      $ark_port_map[$read_sock_ip . $read_sock_port]['arkid'] = $new_sock_arkid;
      $send_data_first_return = "%S,$new_sock_arkid,02,ok";
      $send_count_first_return = socket_send($read_sock, $send_data_first_return, strlen($send_data_first_return), 0);
      continue;
    }elseif ($recv_num == '10' || $recv_num == '20' || $recv_num == '30' || $recv_num == '40' || $recv_num == '50'){      /**
       * 如果发来的数据为10到50,判断对应盒子的状态并强制开启
       * 将设备和盒子的ID放入要开启的盒子的数组中
       */
      $force_opening_cabid = substr($recv_num, 0, 1);
      $msi_stat = get_connection();
      $sql_stat = "select cabstat$force_opening_cabid from imicms_yh_goods where arkid = '$key_arkid'";
      $res_stat = $msi_stat->query($sql_stat);
      $cabstat = 'Y';
      if ($datarow_stat= $res_stat->fetch_array()){
        $cabstat = $datarow_stat[0];
      }
      $msi_stat->close();
      if($cabstat == 'Y'){
        if (array_key_exists($key_arkid, $opening_arksandcabs_arr)){
          if (!in_array($force_opening_cabid, $opening_arksandcabs_arr[$key_arkid])){
            $opening_arksandcabs_arr[$key_arkid][] = $key_arkid;
          }
        }else{
          $opening_arksandcabs_arr[$key_arkid] = array($force_opening_cabid);
        }
      }
    }elseif ($recv_num == 'k'){
    
      /**
       * 如果发来的数据为k,表示盒子开启成功,
       * 从要开启的盒子的数组中删除对应的设备和盒子
       */
      $opened_return_cabid = $sock_data_arr[3];
      $arr2floor_key = array_search($opened_return_cabid, $opening_arksandcabs_arr[$key_arkid]);
      unset($opening_arksandcabs_arr[$key_arkid][$arr2floor_key]);
      
      //如果设备没有要开启的盒子,删除数组中的设备
      if (empty($opening_arksandcabs_arr[$key_arkid])){
        unset($opening_arksandcabs_arr[$key_arkid]);
      }
    }elseif ($recv_num == 'h'){      /**
       * 如果发来的数据为h,表示设备更新成功,
       * 从要更新的设备的数组中删除对应的设备
       */
      if (in_array($key_arkid, $updating_arks_arr)){
        $key_in_updating = array_search($key_arkid, $updating_arks_arr);
        unset($updating_arks_arr[$key_in_updating]);
        $updating_arks_arr = array_values($updating_arks_arr);
      }
    }
    
    /**
     * 根据要开启的盒子数组中匹配的设备号发送开锁数据到设备客户端。
     */
    if (array_key_exists($key_arkid, $opening_arksandcabs_arr)){
      $opening_cabs_arr = $opening_arksandcabs_arr[$key_arkid];
      
      foreach ($opening_cabs_arr as $opening_cab){
        $send_data_open = "%S,$key_arkid,$opening_cab,ok";
        
        $send_count = socket_send($read_sock, $send_data_open, strlen($send_data_open), 0);
        
        if($send_count < 0 || $send_count == false){    
          continue;
        }
      }
      
      //为了保证每次通讯只调用一次socket_send,此处直接跳转至下一个foreach循环
      continue;
    }
    
    /**
     * 根据将要更新设备数组中的匹配的设备号发送更新命令到客户端。
     */
    if (in_array($key_arkid, $updating_arks_arr)){
      $send_data_update = "%S,$key_arkid,r,ok";
      
      $send_count_update = socket_send($read_sock, $send_data_update, strlen($send_data_update), 0);
      
      if($send_count_update < 0 || $send_count_update == false){    
        continue;
      }
    }
  }  $msi_cached_now = get_connection();
  $sql_cached_now = "select cached_goods, updating_arks from imicms_yh_saling_goods";
  $res_cached_now = $msi_cached_now->query($sql_cached_now);
  $cached_goods_now_json = '';
  $updating_arks_now_json = '';
  if ($datarow_cached_now = $res_cached_now->fetch_array()){
    $cached_goods_now_json = $datarow_cached_now[0];
    $updating_arks_now_json = $datarow_cached_now[1];
  }
  $msi_cached_now->close();
  
  /**
   * 1.重新向数据库写入将要开启的盒子的json数组
   * 2.重新向数据库写入将要更新的设备的json数组
   */
  $new_cached_goods_json = json_encode($opening_arksandcabs_arr);
  $new_updating_arks_json = json_encode($updating_arks_arr);
  
  if ($new_cached_goods_json != $cached_goods_now_json || $new_updating_arks_json != $updating_arks_now_json){
    $msi_new_cached = get_connection();
    $sql_new_cached = "update imicms_yh_saling_goods set cached_goods = '$new_cached_goods_json', updating_arks = '$new_updating_arks_json'";
    $msi_new_cached->query($sql_new_cached);
    $msi_new_cached->close();
  }
}socket_close($sock);
?>

解决方案 »

  1.   

    不是有 while (true){ 吗?
      

  2.   

    可是socket_select函数不会阻塞程序吗?
      

  3.   

    跟 socket_select 什么关系?
    虽然 socket_select 会等待连接字的变化
    但你不断有请求,自然也就不断有相应的数据库操作当然,一旦你没有了请求,自然也就没有了数据库操作。不过你也感觉不到
      

  4.   

    当 socket_select 返回假时,就意味着出现了错误
    因此你
      if (socket_select($read, $write = NULL, $except = NULL, NULL) < 1){
        continue;
      }
    是没有道理的
      

  5.   

    请问你说的不断有请求,是指有tcp客户端发来的请求吗?
      

  6.   

    对,是 tcp 客户端发来的请求(收到数据后的应答)