分类 IT技术 下的文章

用goquery从国家统计局抓取最新省市区3级行政区划代码,生成SQL文件导入数据库

想找一份最新的省市区三级联动的数据,网上的怕不够新,不够准确
正好用go从国家统计局官方网站爬一份,生成sql文件,然后导入数据库

最新省市区3级行政区划代码的地址在
http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201703/t20170310_1471429.html

数据库表结构

DROP TABLE IF EXISTS `3goals_area`;
CREATE TABLE `3goals_area` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL COMMENT '名称',
  `code` int(10) unsigned NOT NULL COMMENT '区域代码',
  `level` int(1) unsigned NOT NULL DEFAULT '0' COMMENT '层级1省2市3县区',
  `parent` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父code',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1625 DEFAULT CHARSET=utf8mb4 COMMENT='省市区县';

代码:spider.go

package main

import (
    "log"
    "github.com/PuerkitoBio/goquery"
    "os"
    "io"
    "strings"
)

func GetSQL(){
    
    //地址
    var url string
    url = "http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201703/t20170310_1471429.html"

    doc, err := goquery.NewDocument(url)
    if err != nil{
      log.Fatal(err)
    }

    //打开area.sql文件,准备写入sql语句
    var f *os.File

    f, _ = os.Create("area.sql")

    //按顺序读取,设置当前的省code,市code,用于sql中的parent字段
    var nowProvince string = "0"

    var nowCity string = "0"

    //取到所有数据,循环,判断是省、市还是区县,做不同处理,生成sql语句
    doc.Find(".MsoNormal").Each(func(i int, s *goquery.Selection) {
        //根据页面特点,有加粗<b>标签的是省级数据
        if(s.Find("b").Text() != "") {

            nowProvince = strings.TrimSpace(s.Find("span").First().Text())

            var name string = strings.TrimSpace(s.Find("span").Last().Text())
            var code string = strings.TrimSpace(s.Find("span").First().Text())
            
            io.WriteString(f, "INSERT INTO 3goals_area(`name`,`code`,`level`,`parent`) values('" + name + "','" + code + "',1,0);\r\n")
      
        } else {
            //根据页面特点,第一个<span>标签内容是一个空格的是市级数据
            if(s.Find("span").First().Text() == " ") {

                nowCity = strings.TrimSpace(s.Find("span").Eq(1).Text())

                var name string = strings.TrimSpace(s.Find("span").Last().Text())
                var code string = strings.TrimSpace(s.Find("span").Eq(1).Text())

                io.WriteString(f, "INSERT INTO 3goals_area(`name`,`code`,`level`,`parent`) values('" + name + "','" + code + "',2," + nowProvince + ");\r\n")

            }

            //根据页面特点,第一个<span>标签内容是两个空格的是区县级数据
            if(s.Find("span").First().Text() == "  ") {

                var name string = strings.TrimSpace(s.Find("span").Last().Text())
                var code string = strings.TrimSpace(s.Find("span").Eq(1).Text())

                io.WriteString(f, "INSERT INTO 3goals_area(`name`,`code`,`level`,`parent`) values('" + name + "','" + code + "',3," + nowCity + ");\r\n")

            }
            
            
        }

    })
}

func main(){
    GetSQL()
}

安装好go环境,在spider.go文件当前目录下,运行

go run spider.go

可以看到在当前目录下,生成了area.sql文件

不想跑脚本的,可以直接拿去导入,sql内容如下:

内容太大,无法直接显示,想直接下载sql文件的,点击这里到csdn下载吧

从零开始打造一个报名小程序

微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

优势

1、对用户使用上来说,确实方便,要用的时候打开,不用的时候关掉,即用即走。这点比需要下载,还要占用手机内存空间的APP要好。

2、主要的样式代码都封装在微信小程序里面,所以打开速度比普通的H5要快,接近原生APP。

3、可以调用比H5更多的手机系统功能来进行开发,例如GPS定位、录音、拍视频、重力感应等,能开发更丰富的使用场景。

4、在安卓手机上可以添加到手机桌面,看上去跟原生APP差不多,但仅限安卓手机,iphone就不行了。

5、运行速度跟APP差不多,也能做出很多H5不做到的功能,开发成本跟H5差不多,相对来说开发成本比APP要低。

劣势

1、微信小程序只有2M的大小,这样导致无法开发大型一些的小程序。所以目前你会看到很多小程序真的很小很简单。

2、小程序的技术框架还不稳定,开发方法时常有修改,导致短时间内经常要升级维护,或许这能解析为什么小程序只能2M大小,怕部署太大型的项目会出大问题。

3、不能跳转外链网址,所以间接影响了小程序的开放性。也可能是想限制其他支付方式或功能接入(或许是我想多了)。

4、不能直接分享到朋友圈,哎呀,少了一个重要的推广方式。

5、需要像APP一样审核上架,这点比HTML5即做即发布要麻烦些。

微信小程序入口在哪里?相信大家都已经使用过摩拜单车的小程序了。。。
小程序入口

小程序既不是原生开发,也不是纯html5网页,是以混合hybird的方式展示

小程序API一览

https://mp.weixin.qq.com/debug/wxadoc/dev/api/

更多教程看官方文档:https://mp.weixin.qq.com/debug/wxadoc/dev/

介绍完毕,正式开工

注册小程序

现在个人也可以注册小程序了
打开微信公众平台的登录页:https://mp.weixin.qq.com/
立即注册
TIM图片20170824170511.png
用个人邮箱就可以注册
注册完成后,登录进入后台
进入设置找到小程序的AppId,为下一步做好准备
QQ图片20170824220635.png

下载微信开发者工具

针对公众号和小程序,微信推出了“开发者工具”,集成了开发调试、代码编辑及程序发布等功能。
https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/devtools.html

添加项目

打开开发者工具,选择“本地小程序项目”,添加项目
QQ图片20170824221155.png

输入AppId和项目名,选择一个本地目录作为项目目录
勾选上quick_start会在目录下新建一些示例代码
QQ图片20170824221330.png

点击添加项目以后,可以看到示例的hello world
QQ图片20170824221706.png

系统分析

我们看到示例取到了用户的头像和昵称,
我们现在要实现一个简单的报名功能,
进入小程序,就展示已报名的用户头像和昵称,
未报名的用户,点击报名,记录报名用户的头像和昵称,存入数据库

那么如何与数据库进行交互呢?
接口!
用小程序的api-【wx.request】请求事先准备好的接口
例如:
接口1:获取已报名用户列表接口(GET)
https://api.keyunq.com/getUsers.php
接口2:用户报名接口(POST)(返回0:已经报过名了,其他整数:报名成功)
https://api.keyunq.com/addUser.php

在小程序加载时,调接口1
用户点击报名时,调接口2

报名操作

在示例页面上,新建一个报名按钮
在开发者工具上,打开pages/index/index.wxml首页的页面结构文件
将显示hello world的地方,改成一个报名按钮

<text class="user-motto">{{motto}}</text>

改为

<button type="primary" bindtap="baoming"> {{motto}} </button>

bindtap就是绑定事件处理函数,motto是在data里定义的数据,
关于数据绑定这块,小程序是单向数据绑定,和react类似
通过setData改变数据,同时会刷新页面,展示新的数据

data: {
    motto: '我要报名',
    userInfo: {}
},

然后在index.js文件Page定义里,编写这个函数

baoming: function () {
    console.log('baoming')
}

保存一下,可以在调试界面看到,点击报名,打印出来了baoming字符串
进一步完善,用wx.request调用接口

baoming: function () {
    var that = this
    let avatar = app.globalData.userInfo.avatarUrl
    let name = app.globalData.userInfo.nickName
    wx.request({
      url: 'https://api.keyunq.com/addUser.php',
      data: {
        avatar: avatar ,
        name: name
      },
      method: 'POST',
      header: {
          'content-type': 'application/x-www-form-urlencoded'
      },
      success: function(res) {
        if(res.data == 0) {
          that.setData({
            motto:'您已经报过名了,基佬!'
          })
        } else {
          wx.navigateTo({
            url: '../index/index'
          })
        }
      }
    })
  }

报错"不在以下合法域名列表中",需要在后台配置服务器信息
将接口域名配置为request合法域名,
QQ图片20170824231319.png

再回到开发者工具,项目-》配置信息那刷新一下
QQ图片20170824234840.png

展示已报名人员

上一步实现了报名操作,现在把已报名的人员列表展示出来
在Page的onLoad函数里,调用接口2,读取数据,展示出来

新增data定义baomingList,报名用户列表数据

data: {
    motto: '我要报名',
    userInfo: {},
    baomingList: {}
  },

onLoad函数,后期为了用户体验,把onLoad改为了onShow函数

onLoad: function () {
    console.log('onLoad')
    var that = this
    //调用应用实例的方法获取全局数据
    app.getUserInfo(function(userInfo){
      //更新数据
      that.setData({
        userInfo:userInfo
      })
    })
    wx.request({
      url: 'https://api.keyunq.com/getUsers.php',
      header: {
        'content-type': 'application/x-www-form-urlencoded'
      },
      success: function (res) {
        if(res.data) {
          that.setData({
            baomingList: res.data
          })
        }
      }
    })
  },

调用接口2,然后setData到baomingList
然后在view视图文件index.wxml,输出baomingList

<!--index.wxml-->
<view class="container">
  <view class="">已经报名的基佬:</view>
  <view class="page__bd page__bd_spacing">
    <view class="weui-grids">
      <view class="weui-grid text_center" wx:for="{{baomingList}}" wx:key="key"><image class="userinfo-avatar" src="{{item.avatar}}" background-size="cover"></image><view class="weui-grid__label">{{item.name}}</view></view>
    </view>
  </view>
  <view class="usermotto">
    <button type="primary" bindtap="baoming"> {{motto}} </button>
  </view>
</view>

一些样式,app.wxss

/**app.wxss**/
@import "dist/style/weui.wxss";
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
} 

这里我引入了weui的样式,下载weui的样式文件,放在了dist目录
首页的样式文件index.wxss

/**index.wxss**/
.userinfo {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.userinfo-avatar {
  width: 128rpx;
  height: 128rpx;
  margin: 20rpx;
  border-radius: 50%;
}

.userinfo-nickname {
  color: #aaa;
}

.usermotto {
  margin-top: 20px;
}

.grid {
  display: flex;
}
.grid-item {
  display: inline;
}
.page__bd {
  width: 100%;
}

调试没什么问题后,可以提交到真机预览
点开发者工具-》项目-》基础信息-》预览
用微信扫描二维码,即可进行预览,效果图如下
TIM图片20170825151132.png

要邀请其他人一起体验的话,点击预览下面的上传按钮
上传后会生成一个体验版本,在微信官方后台-》开发管理,设置一下,就会生成一个体验版的二维码
然后在用户身份-》成员管理处,添加成员,邀请进行体验

用docker在阿里云esc上搭建Apache+PHP+MariaDB环境

久闻docker大名,一直没用过
这次在阿里云的一台centOS7上要搭一下环境
正好学习一下,记录一下此过程

首先是安装Docker

yum -y install docker

安装完成后,启动docker服务

systemctl start docker.service

测试运行hello-world

docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

由于本地没有hello-world这个镜像,所以会下载一个hello-world的镜像,并在容器内运行。

还可以运行docker info命令,出现相关信息说明安装成功。

使用阿里云docker镜像加速器

登录阿里云 - 开发者平台:https://dev.aliyun.com/ 进入管理中心->Docker Hub
镜像站点->您的专属加速器地址 就可以看到加速器地址

当你下载安装的Docker Version不低于1.10时,建议直接通过daemon config进行配置。 使用配置文件
/etc/docker/daemon.json(没有时新建该文件)

{
    "registry-mirrors": ["<your accelerate address>"]
}

重启Docker Daemon就可以了。

sudo systemctl daemon-reload
sudo systemctl restart docker

本步骤未配置成功,pull镜像时,显示还是从docker.io下载

通过Dockerfile安装Apache+PHP

到 Docker Hub 找到PHP的镜像
https://hub.docker.com/_/php/

选择需要的Dockerfile
笔者选择的是7.0-apache版的Dockerfile
内容为:

FROM php:7.0-apache
COPY src/ /var/www/html/

FROM php:7.0-apache
表示从php:7.0-apache的镜像构建,而php:7.0-apache的Dockerfile详细内容
https://github.com/docker-library/php/blob/2630167f7e69394bdd91f240443a0a521fd7872d/7.0/apache/Dockerfile

回到自己的服务器
新建my-php-app目录作为项目根目录
进入my-php-app目录并新建src目录存放php代码
将7.0-apache版的Dockerfile
将写好的Dockerfile文件放到此apache-php文件夹内
构建

docker build -t my-php-app .

(注意最后有个点用来表示当前目录,初次构建速度会比较慢,需要多等一会。)
出现如下信息,说明构建成功

Successfully built e669e1432638

然后就可以

运行容器

docker run -p 80:80 --name my-running-app -v $PWD/my-php-app/src:/var/www/html -d my-php-app

-p 80:80 绑定宿主的80端口
--name 容器名称
-v 宿主的目录$PWD/my-php-app/src绑定到容器的/var/www/html目录
-d 后台运行容器
my-php-app镜像名称

在src目录下,新建index.php

<?php
echo "hello world";
?>

访问http://服务器IP/可以看到输出了令人激动的hello world

这样,一个基于docker的php+apache的环境,就算初步建成了

安装mariaDB

新建/home/mariadb/data目录,用来存放数据
将/home/mariadb/data挂载到容器的存放数据的路径
便于保存,否则容器重启之后,数据库数据就没了
对了,要先在宿主主机上把SElinux关闭,否则可能出问题

docker search mariadb
docker pull mariadb
docker run -v /home/mariadb/data:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --name my-mariadb -d mariadb:latest

-e MYSQL_ROOT_PASSWORD 设置root密码

PHP连接mariadb

编辑index.php,连接数据库,报错
因为php没有装相应的mysql扩展
于是安装pdo

编辑Dockerfile

FROM php:7.0-apache
COPY src/ /var/www/html/
RUN docker-php-ext-install pdo_mysql\
    && docker-php-ext-install mysqli

停止my-running-app容器并删除该容器

docker stop my-running-app
docker rm my-running-app 

然后重新build镜像,再从新的镜像运行新容器

docker build -t my-php-app . 
docker run -p 80:80 --name my-running-app -v /home/php/src:/var/www/html -d my-php-app

通过phpinfo发现pdo扩展已经安装上。
编辑index.php

$dsn = 'mysql:dbname=mysql;host=宿主主机IP';
$user = 'root';
$password = 'root';

try {
    $dbh = new PDO($dsn, $user, $password);
    if($dbh) {
        echo "success";
    }
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

打印出success,说明连接数据库成功。

江西小程序-打造一款简单猜拳小程序(go+websocket+redis+mysql+小程序前端)(五)

本节思路:
基于本系列第二节内容,实现一个websocket服务端,redis为存储介质,以自己约定的cmd命令为简单通讯协议,与小程序进行通讯,实现简单的2人猜拳功能

cmd命令按照如下流程:
login(client->server),小程序发给服务端,登录命令,服务端接收命令后,判断房间人数是否已满,未满则保存客户端websocket连接到列表,客户端用户信息存入redis,更新在线用户列表,然后发送init(server->client)命令,通知所有在线用户,更新在线用户列表。

当客户端点击准备按钮,发送ready(client->server)命令到服务端,服务端更新该用户的ready标识位(存于redis),计算房间人数是否已满,以及房间所有用户是否都已设置ready标识位,如是,则发送start(server->client)命令给所有用户,开始游戏。

客户端选择好自己的出拳信息,发送guess(client->server)命令到服务端,服务端记录该用户出拳数据,判断房间内所有人都以提交guess命令,则计算最后结果,发送result(server->client)命令给所有用户。

客户端接收到result命令后,重新进入ready流程。

如退出小程序,客户端发送logout(client->server)命令到服务端,服务端从列表中删除该用户,重新发送init(server->client)命令到所有其他在线用户,更新在线用户列表。

命令流程如下:
login(client->server)
init(server->client)
ready(client->server)
start(server->client)
guess(client->server)
result(server->client)
logout(client->server)

效果示意图:
login
QQ图片20170425143225.png

ready
QQ图片20170425143331.png

start
QQ图片20170425143419.png

result
QQ图片20170425143500.jpg

go服务端

package main
import (
    "golang.org/x/net/websocket"
    "fmt"
    "log"
    "net/http"
    "github.com/go-redis/redis"
    "encoding/json"
    "strconv"
)
const max_room_num = 2

var (  
    JSON          = websocket.JSON              // codec for JSON  
    Message       = websocket.Message           // codec for string, []byte  
    ActiveClients = make(map[string]ClientConn) // map containing clients  //在线websocket列表
    User          = make(map[string]string)
)  

type ClientConn struct {
    websocket *websocket.Conn  
}

type UserMsg struct {
    Room string
    Cmd string
    User string
    AvatarUrl string
    Content string
    Uuid string
    HandNum string
    GuessNum string
}

type UserInfo struct {
    User string
    AvatarUrl string
    Uuid string
}

type ReplyMsg struct {
    Room string
    Cmd string
    Data string
}

type GuessResult struct {
    Result string
    CurrentNum int
    HandRecord map[string]string
    GuessRecord map[string]string
}

func echoHandler(ws *websocket.Conn) {
    var err error  
    var userMsg UserMsg
    
    for {  

        var data []byte
        if err = websocket.Message.Receive(ws, &data); err != nil {  
            fmt.Println("can't receive")  
            break  
        }

        err = json.Unmarshal(data, &userMsg)  
        fmt.Println(userMsg)

        go wsHandler(ws,userMsg)

    }  

}

func wsHandler(ws *websocket.Conn,userMsg UserMsg) {
    sockCli := ClientConn{ws}
    var err error


    redisClient := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    //登录
    if userMsg.Cmd == "login" {
        fmt.Println("login") 
        //判断房间人数是否已满
        checkNumTmp := redisClient.SCard(userMsg.Room)
        checkNum := checkNumTmp.Val()
        if(checkNum < max_room_num) {
            fmt.Println("checkNum success") 
            //socket用户列表新增当前用户websocket连接
            ActiveClients[userMsg.Uuid] = sockCli
            //用户uuid保存到redis房间set集合内
            redisClient.SAdd("ROOM:"+userMsg.Room,userMsg.Uuid)

            var me UserInfo

            me.User = userMsg.User
            me.AvatarUrl = userMsg.AvatarUrl
            me.Uuid = userMsg.Uuid

            //生成用户信息json串
            b, err := json.Marshal(me)
            if err != nil {
                fmt.Println("Encoding User Faild")
            } else {
                //保存用户信息到redis
                redisClient.Set("USER:"+me.Uuid,b,0)

                //初始化用户
                initOnlineMsg(redisClient,userMsg)

            }
        } else {
            var rm ReplyMsg
            rm.Room = userMsg.Room
            rm.Cmd = "loginFailed"
            rm.Data = "登录失败,人数已满"

            sendMsg,err2 := json.Marshal(rm)
            sendMsgStr := string(sendMsg)
            fmt.Println(sendMsgStr) 
            if err2 != nil {
                
            } else {
                if err = websocket.Message.Send(ws, sendMsgStr); err != nil {  
                    log.Println("Could not send UsersList to ", userMsg.User, err.Error())  
                }
            }
        }

    //准备
    } else if userMsg.Cmd == "ready" {

        redisClient.Set("READY:"+userMsg.Uuid,"ready",0)

        //从redis取房间内的所有用户uuid
        roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
        //用户uuid保存到一个go切片online 
        online := roomSlice.Val()

        i := 0

        //循环取在线用户个人信息
        if len(online) != 0 {
            for _, na := range online {  
                if na != "" {  
                    userJson := redisClient.Get("READY:"+na)
                    userJson2 := userJson.Val()
                    if userJson2 == "ready" {
                        i++
                    }
                }  
            }
        }
        if i == len(online) && i == max_room_num {
            var rm ReplyMsg
            rm.Room = userMsg.Room
            rm.Cmd = "start"
            rm.Data = ""

            broadcast(redisClient,userMsg,rm)
        }

    //退出
    } else if userMsg.Cmd == "logout" {
        fmt.Println("logout") 

        //socket用户列表删除该用户websocket连接
        delete(ActiveClients,userMsg.Uuid)
        //从redis房间set集合内删除该用户uuid
        redisClient.SRem("ROOM:"+userMsg.Room,userMsg.Uuid)

        //初始化用户
        initOnlineMsg(redisClient,userMsg)


    //出拳
    } else if userMsg.Cmd == "guess" {
        var result string
        fmt.Println("guess")
        fmt.Println(userMsg.HandNum)
        fmt.Println(userMsg.GuessNum)

        myHandNum,_ := strconv.Atoi(userMsg.HandNum)
        myGuessNum,_ := strconv.Atoi(userMsg.GuessNum)

        redisClient.Set("HANDNUM:"+userMsg.Uuid,myHandNum,0)
        redisClient.Set("GUESSNUM:"+userMsg.Uuid,myGuessNum,0)


        //从redis取房间内的所有用户uuid
        roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
        //用户uuid保存到一个go切片online 
        online := roomSlice.Val()

        
        i := 0

        //循环取在线用户
        if len(online) != 0 {
            for _, na := range online {  
                if na != "" {  
                    handnumCmd := redisClient.Get("HANDNUM:"+na)
                    handnum := handnumCmd.Val()
                    if handnum != "" {
                        i++
                    }
                }  
            }
        }

        //房间内所有人都已提交,则计算最后结果
        if i == len(online) && i == max_room_num {
            var handRecordList map[string]string
            handRecordList = make(map[string]string)
            var guessRecordList map[string]string
            guessRecordList = make(map[string]string)

            //计算正确结果currentNum
            currentNum := 0
            //循环取在线用户
            if len(online) != 0 {
                for _, na := range online {  
                    if na != "" {  
                        //取某用户的出拳数据,已用户名为key,存入结果map
                        handnumCmd := redisClient.Get("HANDNUM:"+na)
                        handnum := handnumCmd.Val()
                        
                        guessnumCmd := redisClient.Get("GUESSNUM:"+na)
                        guessnum := guessnumCmd.Val()   

                        userJson := redisClient.Get("USER:"+na)
                        userJson2 := userJson.Val()

                        var user UserInfo
                        json.Unmarshal([]byte(userJson2), &user)

                        handRecordList[user.User] = handnum
                        guessRecordList[user.User] = guessnum

                        //计算结果
                        thandnum,_ := strconv.Atoi(handnum)
                        currentNum = currentNum + thandnum
                    }  
                }
            }

            //给各个用户发送结果消息
            if len(online) != 0 {
                for _, na := range online { 
                    if na != "" {
                        guessnumCmd := redisClient.Get("GUESSNUM:"+na)
                        guessnum := guessnumCmd.Val()
                        tguessnum ,_ := strconv.Atoi(guessnum)
                        if tguessnum == currentNum {
                            result = "1"
                        } else {
                            result = "0"
                        }
                        var guessResult GuessResult
                        guessResult.Result = result
                        guessResult.CurrentNum = currentNum
                        guessResult.HandRecord = handRecordList
                        guessResult.GuessRecord = guessRecordList

                        resultTmp,_ := json.Marshal(guessResult)
                        resultData := string(resultTmp)

                        //删除用户准备状态
                        redisClient.Del("READY:"+na)
                        //删除用户猜拳数据
                        redisClient.Del("HANDNUM:"+na)
                        redisClient.Del("GUESSNUM:"+na)

                        var rm ReplyMsg
                        rm.Room = userMsg.Room
                        rm.Cmd = "result"
                        rm.Data = resultData

                        sendMsg,_ := json.Marshal(rm)
                        sendMsgStr := string(sendMsg)

                        if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {  
                            log.Println("Could not send UsersList to ", "", err.Error())  
                        }


                    }
                }
            }
        }

    //发消息
    } else {
        /*
        //从redis取房间内的所有用户uuid
        roomSlice := redisClient.SMembers(userMsg.Room)
        //用户uuid保存到一个go切片online 
        online := roomSlice.Val()

        //循环给房间内用户发送消息
        if len(online) != 0 {
            for _, na := range online {  
                if na != "" {  
                    //ActiveClients[na].websocket就是用户对应的websocket链接
                    if err = websocket.Message.Send(ActiveClients[na].websocket, userMsg.User+"说:"+userMsg.Content); err != nil {  
                        log.Println("Could not send message to ", userMsg.User, err.Error())  
                    }  
                }  
            }
        }*/


    }
}

//房间成员初始化,有人加入或者退出都要重新初始化,相当于聊天室的在线用户列表的维护
func initOnlineMsg(redisClient *redis.Client,userMsg UserMsg) {
    
    var err error

    //从redis取房间内的所有用户uuid
    roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
    //用户uuid保存到一个go切片online 
    online := roomSlice.Val()

    var onlineList []string

    //循环取在线用户个人信息
    if len(online) != 0 {
        for _, na := range online {  
            if na != "" {  
                userJson := redisClient.Get("USER:"+na)
                userJson2 := userJson.Val()
                onlineList = append(onlineList,userJson2)
            }  
        }
    }
    fmt.Println("get online success") 
    //生成在线用户信息json串
    //c, err := json.Marshal(onlineList)

    onlineListStr,err2 := json.Marshal(onlineList)

    var rm ReplyMsg
    rm.Room = userMsg.Room
    rm.Cmd = "init"
    rm.Data = string(onlineListStr)

    sendMsg,err2 := json.Marshal(rm)
    sendMsgStr := string(sendMsg)
    fmt.Println("init") 
    if err2 != nil {
        
    } else {
        //给所有用户发初始化消息
        if len(online) != 0 {
            for _, na := range online {  
                if na != "" {                   
                    if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {  
                        log.Println("Could not send UsersList to ", "", err.Error())  
                    }
                }
            }
        }
        //若房间人数满,发送就绪消息
        if len(online) >= max_room_num {
            fmt.Println("full")
            var rm ReplyMsg
            rm.Room = userMsg.Room
            rm.Cmd = "full"
            rm.Data = ""

            sendMsg,_ := json.Marshal(rm)
            sendMsgStr := string(sendMsg)

            for _, na := range online {  
                if na != "" {                   
                    if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {  
                        log.Println("Could not send UsersList to ", "", err.Error())  
                    }
                }
            }
        }
    }
    
}

//广播消息
func broadcast(redisClient *redis.Client,userMsg UserMsg,rm ReplyMsg) {
    var err error

    //从redis取房间内的所有用户uuid
    roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
    //用户uuid保存到一个go切片online 
    online := roomSlice.Val()

    sendMsg,err2 := json.Marshal(rm)
    sendMsgStr := string(sendMsg)
    fmt.Println("broadcast") 
    if err2 != nil {
        
    } else {
        //给所有用户发消息
        if len(online) != 0 {
            for _, na := range online {  
                if na != "" {                   
                    if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {  
                        log.Println("Could not send UsersList to ", "", err.Error())  
                    }
                }
            }
        }
    }
}

func main() {
    http.Handle("/echo", websocket.Handler(echoHandler))
    http.Handle("/", http.FileServer(http.Dir(".")))

    err := http.ListenAndServe(":8929", nil)

    if err != nil {
        panic("ListenAndServe: " + err.Error())
    }
}

小程序代码:
app.js

//app.js
App({
  onLaunch: function () {
    console.log("App生命周期函数——onLaunch函数");
  },
  checkSession:function(mysessionid) {
    return new Promise(function(resolve, reject) {
      wx.request({
        url: 'https://xxx.xxxxx.com/check.php',
        header: {
          sessionid:mysessionid
        },
        success: function(res) {
          console.log("检查sessionid是否有效")
          resolve(res.data)
        },
        fail: function(e) {
          reject(e)
        }
      })
    })
  },
  login:function() {
    return new Promise(function(resolve, reject) {
      wx.login({
        success: function (res0) {
          if (res0.code) {
            wx.request({
              url: 'https://xxx.xxxxx.com/login.php',
              data: {
                code: res0.code
              },
              header: {
                  'content-type': 'application/json'
              },
              success: function(res) {
                console.log("取得新的sessionid")
                console.log(res.data)
                var mysessionid = res.data.k
                wx.setStorageSync("mysessionid",mysessionid)
                var myuuid = res.data.v
                wx.setStorageSync("myuuid",myuuid)
                resolve(mysessionid)
              },
              fail: function(e) {
                reject(e)
              }
            })
          }
        }
      })
    })
  },
  getWxUserInfo:function() {
    return new Promise(function(resolve, reject) {
      wx.getUserInfo({
        withCredentials: false,
        success: function(res) {
          console.log("取得新的userInfo")
          var userInfo = res.userInfo
          wx.setStorageSync("userInfo",userInfo)
          console.log("setUserInfo")
          resolve(userInfo)
        }
      })
    })
  },
  getUserInfo:function() {
    var that = this
    return new Promise(function(resolve, reject) {
      var mysessionid = wx.getStorageSync('mysessionid')
      if(mysessionid) {
        console.log("sessionid存在")
        that.checkSession(mysessionid).then(function(sessionContent){
          if(sessionContent == 0) {
            console.log("sessionid无效-取userInfo存到本地")
            that.login().then(function(){
              that.getWxUserInfo().then(function(userInfo){
                resolve(userInfo)
              })
            })
          } else {
            console.log("sessionid有效-直接取本地userInfo")
            var userInfo = wx.getStorageSync("userInfo")
            resolve(userInfo)
          }
        })
        
      } else {
        console.log("sessionid不存在,重新走登录流程")
        that.login().then(function(){
          that.getWxUserInfo().then(function(userInfo){
            resolve(userInfo)
          })
        })
      }
    })
  },
  globalData:{
    userInfo:null,
    onlineList:[],
    onlineStatus:false,
    myHandNum:0,
    myGuessNum:0
  }
})

page/index.js

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    userInfo: {},
    onlineList:{},
    status:0,
    statusStr:"等待中",
    guessBoxStatus:"hideBox",
    handList:['0','1','2','3','4','5'],
    handStyleList:['primary','default','default','default','default','default'],
    guessList:['0','1','2','3','4','5','6','7','8','9','10'],
    guessStyleList:['primary','default','default','default','default','default','default','default','default','default','default'],
    buttonList:['0','1','2'],
    buttonStrList:['准备','开始','提交'],
    buttonStyleList:['btnShow','btnHide','btnHide'],
    buttonFuncList:['ready','start','guess']
  },
  onLoad: function () {
    console.log("Page onLoad函数");
    wx.playBackgroundAudio({
      dataUrl: 'https://xxx.xxxxx.com/8585.mp3',
      title: '古琴音效',
      coverImgUrl: 'https://xxx.xxxxx.com/logo.png',
      success: function() {
        console.log("播放音效")
      }
    })
  },
  onHide: function() {
    console.log('发送注销消息')
    var myuuid = wx.getStorageSync('myuuid')
    var msg = new Object();
    msg.Room = '1';
    msg.Cmd = 'logout';
    msg.Uuid = myuuid;
    var str = JSON.stringify(msg)
    wx.sendSocketMessage({
      data:str
    })
    wx.closeSocket()
    app.globalData.onlineStatus = false
  },
  onShow: function() {
    var that = this
    app.getUserInfo().then(function(userInfo){
      that.setData({
        userInfo:userInfo
      })
      that.wsHandler(userInfo)
      that.initBox()
    })
  },
  wsHandler: function(userInfo) {
    var that = this

    //websocket
    wx.connectSocket({
      url: 'wss://xx.xxxxx.com/echo'
    })
    wx.onSocketOpen(function(res) {
      console.log('WebSocket连接已打开!')
      var myuuid = wx.getStorageSync('myuuid')
      var msg = new Object();
      msg.Room = '1';
      msg.Cmd = 'login';
      msg.User = userInfo.nickName;
      msg.AvatarUrl = userInfo.avatarUrl;
      msg.Uuid = myuuid;
      var str = JSON.stringify(msg)
      wx.sendSocketMessage({
        data:str
      })
    })
    wx.onSocketMessage(function(res) {
      var msg = JSON.parse(res.data)
      if(msg.Cmd == 'init') {
        var userList = JSON.parse(msg.Data)
        app.globalData.onlineList = []
        for(var i=0;i<userList.length;i++){
          var user = JSON.parse(userList[i])
          app.globalData.onlineList.push(user)
        }

        that.setData({
          onlineList:app.globalData.onlineList,
          status:0,
          statusStr:'等待中'
        })
      }
      if(msg.Cmd == 'full') {
        that.setData({
          status:1,
          statusStr:'准备开始'
        })
      }
      if(msg.Cmd == 'result') {
        
        var result = JSON.parse(msg.Data)

        var content = "总数为"+result.CurrentNum+"\n"
        for (var value in result.HandRecord) {
          content = content+value+"出拳:"+result.HandRecord[value]+"\n";
        }
        for (var value in result.GuessRecord) {
          content = content+value+"猜拳:"+result.GuessRecord[value]+"\n";
        }

        if(result.Result == 1) {
          content = "恭喜你,猜中啦\n" + content
          wx.showModal({
            content: content,
            showCancel: false,
            success: function (res) {
                if (res.confirm) {
                    that.initBox()
                }
            }
          });
        }
        if(result.Result == 0) {
          content = "很遗憾,猜错啦\n" + content
          wx.showModal({
            content: content,
            showCancel: false,
            success: function (res) {
                if (res.confirm) {
                    that.initBox()
                }
            }
          });
        }
        
      }
      if(msg.Cmd == 'start') {
        that.setData({
          status:2,
          statusStr:'游戏中',
          guessBoxStatus:'showBox',
          buttonStyleList:['btnHide','btnHide','btnShow'],
        })
      }
      
    })
  },
  setHandNum: function(event) {
    var that = this
    console.log(event.target.dataset.handnum)
    app.globalData.myHandNum = event.target.dataset.handnum
    var myList = that.data.handStyleList
    for(var i=0;i<myList.length;i++) {
      if(i == event.target.dataset.handnum) {
        myList[i] = 'primary'
      } else {
        myList[i] = 'default'
      }
    }
    that.setData({
      handStyleList:myList
    })
  },
  setGuessNum: function(event) {
    var that = this
    console.log(event.target.dataset.guessnum)
    app.globalData.myGuessNum = event.target.dataset.guessnum
    var myList = that.data.guessStyleList
    for(var i=0;i<myList.length;i++) {
      if(i == event.target.dataset.guessnum) {
        myList[i] = 'primary'
      } else {
        myList[i] = 'default'
      }
    }
    that.setData({
      guessStyleList:myList
    })
  },
  guess: function() {
    var that = this
    var userInfo = that.data.userInfo
    var myuuid = wx.getStorageSync('myuuid')
    var msg = new Object();
    msg.Room = '1';
    msg.Cmd = 'guess';
    msg.User = userInfo.nickName;
    msg.AvatarUrl = userInfo.avatarUrl;
    msg.Uuid = myuuid;
    msg.HandNum = app.globalData.myHandNum
    msg.GuessNum = app.globalData.myGuessNum
    var str = JSON.stringify(msg)
    wx.sendSocketMessage({
      data:str
    })
  },
  ready: function() {
    var that = this
    var userInfo = that.data.userInfo
    var myuuid = wx.getStorageSync('myuuid')
    var msg = new Object();
    msg.Room = '1';
    msg.Cmd = 'ready';
    msg.User = userInfo.nickName;
    msg.AvatarUrl = userInfo.avatarUrl;
    msg.Uuid = myuuid;
    var str = JSON.stringify(msg)
    wx.sendSocketMessage({
      data:str
    })
    that.setData({
      status:1,
      statusStr:'等待对手,准备开始',
      buttonStyleList:['btnHide','btnHide','btnHide'],
    })
    
  },
  start: function() {
    var that = this
    var userInfo = that.data.userInfo
    var myuuid = wx.getStorageSync('myuuid')
    var msg = new Object();
    msg.Room = '1';
    msg.Cmd = 'start';
    msg.User = userInfo.nickName;
    msg.AvatarUrl = userInfo.avatarUrl;
    msg.Uuid = myuuid;
    var str = JSON.stringify(msg)
    wx.sendSocketMessage({
      data:str
    })
  },
  initBox: function() {
    var that = this
    that.setData({
      status:0,
      statusStr:"等待中",
      guessBoxStatus:"hideBox",
      handList:['0','1','2','3','4','5'],
      handStyleList:['primary','default','default','default','default','default'],
      guessList:['0','1','2','3','4','5','6','7','8','9','10'],
      guessStyleList:['primary','default','default','default','default','default','default','default','default','default','default'],
      buttonList:['0','1','2'],
      buttonStrList:['准备','开始','提交'],
      buttonStyleList:['btnShow','btnHide','btnHide'],
      buttonFuncList:['ready','start','guess']
    })
  },
  getAudioStatus: function() {
    wx.getBackgroundAudioPlayerState({
      success: function(res) {
          var status = res.status
          var dataUrl = res.dataUrl
          var currentPosition = res.currentPosition
          var duration = res.duration
          var downloadPercent = res.downloadPercent
          console.log("音乐状态"+status)
          console.log("音乐长度"+duration)
      }
    })
  }
})

ps:播放音乐的功能,在开发工具可以看到,真机上没有听到声音,暂时还没找到解决办法
代码的健壮性,页面效果还需要再优化

check.php

<?php

$post_data = $_POST;
$header = get_all_headers();

$sessionid = $header['sessionid'];

$host = '127.0.0.1';
$port = '6379';
$timeout = 0;

$redis = new Redis();
$redis->connect($host, $port, $timeout);
$session_content = $redis->get("miniappsession:".$sessionid);
if($session_content)
{
    echo $session_content;
} else {
    echo 0;
}

/**
 * 获取自定义的header数据
 */
function get_all_headers(){

    // 忽略获取的header数据
    $ignore = array('host','accept','content-length','content-type');

    $headers = array();

    foreach($_SERVER as $key=>$value){
        if(substr($key, 0, 5)==='HTTP_'){
            $key = substr($key, 5);
            $key = str_replace('_', ' ', $key);
            $key = str_replace(' ', '-', $key);
            $key = strtolower($key);

            if(!in_array($key, $ignore)){
                $headers[$key] = $value;
            }
        }
    }

    return $headers;

}

login.php

<?php
$code = $_GET['code'];
define("APPID",'xxxxxxxxxxxxxxxxxxxxx');
define("SECRET",'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=".APPID."&secret=".SECRET."&js_code=".$code."&grant_type=authorization_code";
$rs = curlGet($url);

$arr = json_decode($rs);

$str = randomFromDev(32);

$host = '127.0.0.1';
$port = '6379';
$timeout = 0;

$redis = new Redis();
$redis->connect($host, $port, $timeout);
$expires_time = 15*24*60*60;
$session_content = md5($arr->openid.$expires_time);
$redis->setex("miniappsession:".$str,$expires_time,$session_content);

$sessionObj['k'] = $str;
$sessionObj['v'] = $session_content;
echo json_encode($sessionObj);


function randomFromDev($len)
{
    $fp = @fopen('/dev/urandom','rb');
    $result = '';
    if ($fp !== FALSE) {
        $result .= @fread($fp, $len);
        @fclose($fp);
    }
    else
    {
        trigger_error('Can not open /dev/urandom.');
    }
    $result = md5($result);
    // convert from binary to string
    //$result = base64_encode($result);
    // remove none url chars
    //$result = strtr($result, '+/', '-_');
    // Remove = from the end
    //$result = str_replace('=', ' ', $result);
    return $result;
}

function curlGet($url, $method = 'get', $data = '')
{
    $ch = curl_init();
    $header = 'Accept-Charset: utf-8';
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $temp = curl_exec($ch);
    return $temp;
}

源码:github
https://github.com/keyunq/ytguess

江西小程序-打造一款简单猜拳小程序(go+websocket+redis+mysql+小程序前端)(四)

本节思路
进入小程序的前端开发,验证前面构建的websocket服务能否和小程序联通。确认联通后再同步进行前后端的开发。

小程序的申请,开发者工具下载等准备工作就不具体阐述了。

打开开发者工具-选择小程序开发,
首先面临的问题就是用户登录状态的维护


小程序登录态维护实现流程

1.通过wx.login获取登录态。
2.通过登录返回的code发送给服务器,服务器用code换取session_key和openid后。
3.获取session_key后一般使用缓存框架保存登录态,服务端随机生成一串唯一字符串3rdSessionId为key,session_key为value组成键值对并存到缓存当中,缓存时间视情况自行决定。
4.将3rdSessionId返回给客户端
5.客户端将3rdSessionId缓存到localStorage中,后续接口从缓存中读取3rdSessionId,传递给服务器;服务器根据3rdSessionId来判断用户身份。
6.如果服务器根据3rdSessionId在缓存中查找是否存在session_key,如果存在正常执行;如果不存在小程序未登录,重新从第一步流程走。

以上流程是官方推荐使用的流程,并不是维护登录态的唯一途径。

另外,你也可以在小程序中使用wx.checkSession()检查登录态是否过期。如果过期重新调用wx.login接口。
session_key在微信服务器有效期是30天,建议服务端缓存session_key不超过30天。

登录时序图
login.png

而根据以上流程,将陷入多层嵌套回调,于是用promise优化流程

官方在 2017.03.28 更新日志 中 支持绝大部分的 ES6 API
其中包括了Promise

于是可以在微信小程序中使用Promise来处理登录流程

代码如下:
app.js

//app.js
App({
  onLaunch: function () {
    console.log("App生命周期函数——onLaunch函数");
  },
  checkSession:function(mysessionid) {
    return new Promise(function(resolve, reject) {
      wx.request({
        url: 'https://xxx.xxxxx.com/check.php',
        header: {
          sessionid:mysessionid
        },
        success: function(res) {
          console.log("检查sessionid是否有效")
          resolve(res.data)
        },
        fail: function(e) {
          reject(e)
        }
      })
    })
  },
  login:function() {
    return new Promise(function(resolve, reject) {
      wx.login({
        success: function (res0) {
          if (res0.code) {
            wx.request({
              url: 'https://xxx.xxxxx.com/login.php',
              data: {
                code: res0.code
              },
              success: function(res) {
                console.log("取得新的sessionid")
                var mysessionid = res.data
                wx.setStorageSync("mysessionid",mysessionid)
                resolve(mysessionid)
              },
              fail: function(e) {
                reject(e)
              }
            })
          }
        }
      })
    })
  },
  getWxUserInfo:function() {
    return new Promise(function(resolve, reject) {
      wx.getUserInfo({
        withCredentials: false,
        success: function(res) {
          console.log("取得新的userInfo")
          var userInfo = res.userInfo
          wx.setStorageSync("userInfo",userInfo)
          console.log("setUserInfo")
          resolve(userInfo)
        }
      })
    })
  },
  getUserInfo:function() {
    var that = this
    return new Promise(function(resolve, reject) {
      var mysessionid = wx.getStorageSync('mysessionid')
      if(mysessionid) {
        console.log("sessionid存在")
        that.checkSession(mysessionid).then(function(sessionFlag){
          if(sessionFlag == 1) {
            console.log("sessionid有效-直接取本地userInfo")
            var userInfo = wx.getStorageSync("userInfo")
            resolve(userInfo)
          } else {
            console.log("sessionid无效-取userInfo存到本地")
            that.login().then(function(){
              that.getWxUserInfo().then(function(userInfo){
                resolve(userInfo)
              })
            })
          }
        })
        
      } else {
        console.log("sessionid不存在,重新走登录流程")
        that.login().then(function(){
          that.getWxUserInfo().then(function(userInfo){
            resolve(userInfo)
          })
        })
      }
    })
  },
  globalData:{
    userInfo:null
  }
})

index.js

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    userInfo: {}
  },
  onLoad: function () {
    console.log("Page onLoad函数");
    var that = this
    app.getUserInfo().then(function(userInfo){
      that.setData({
        userInfo:userInfo
      })
    })
  }
})

根据console的输出内容可以看到:
第一次登录时:

QQ图片20170414115524.png

刷新页面:

QQ图片20170414115618.png

当服务器上的缓存的sessionid失效时:
QQ图片20170414115739.png

check.php(在服务器查找sessionid,可以在redis里得到session_key,本例只判断了sessionid是否存在)

<?php

$post_data = $_POST;
$header = get_all_headers();

$sessionid = $header['sessionid'];

$host = '127.0.0.1';
$port = '6379';
$timeout = 0;

$redis = new Redis();
$redis->connect($host, $port, $timeout);
$session_content = $redis->get("miniappsession:".$sessionid);
if($session_content)
{
    echo 1;
} else {
    echo 0;
}

/**
 * 获取自定义的header数据
 */
function get_all_headers(){

    // 忽略获取的header数据
    $ignore = array('host','accept','content-length','content-type');

    $headers = array();

    foreach($_SERVER as $key=>$value){
        if(substr($key, 0, 5)==='HTTP_'){
            $key = substr($key, 5);
            $key = str_replace('_', ' ', $key);
            $key = str_replace(' ', '-', $key);
            $key = strtolower($key);

            if(!in_array($key, $ignore)){
                $headers[$key] = $value;
            }
        }
    }

    return $headers;

}

login.php(通过code,获取session_key和openid,并生成唯一sessionid,以sessionid为key,session_key和openid为value,存入redis,并返回sessionid给小程序)

<?php
$code = $_GET['code'];
define("APPID",'yourappid');
define("SECRET",'yoursecret');
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=".APPID."&secret=".SECRET."&js_code=".$code."&grant_type=authorization_code";
$rs = curlGet($url);

$arr = json_decode($rs);

$str = randomFromDev(32);

$host = '127.0.0.1';
$port = '6379';
$timeout = 0;

$redis = new Redis();
$redis->connect($host, $port, $timeout);
$expires_time = 10*24*60*60;
$redis->setex("miniappsession:".$str,$expires_time,$arr->openid."|".$arr->session_key);

echo $str;


function randomFromDev($len)
{
    $fp = @fopen('/dev/urandom','rb');
    $result = '';
    if ($fp !== FALSE) {
        $result .= @fread($fp, $len);
        @fclose($fp);
    }
    else
    {
        trigger_error('Can not open /dev/urandom.');
    }
    $result = md5($result);
    return $result;
}

function curlGet($url, $method = 'get', $data = '')
{
    $ch = curl_init();
    $header = 'Accept-Charset: utf-8';
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $temp = curl_exec($ch);
    return $temp;
}

测试websocket联通

登录后取得的用户昵称、用户头像,根据本系列第二节内容
我们在小程序内构造一个room=1,username=用户昵称的websocket请求,看能否连接websocket服务器

index.js

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    userInfo: {}
  },
  onLoad: function () {
    console.log("Page onLoad函数");
    var that = this
    app.getUserInfo().then(function(userInfo){
      that.setData({
        userInfo:userInfo
      })
      //websocket
      wx.connectSocket({
        url: 'wss://xx.xxxxx.com/echo'
      })
      wx.onSocketOpen(function(res) {
        console.log('WebSocket连接已打开!')
        var msg = new Object();
        msg.Room = '1';
        msg.Cmd = 'login';
        msg.User = userInfo.nickName;
        msg.Uuid = userInfo.nickName;
        var str = JSON.stringify(msg)
        wx.sendSocketMessage({
          data:str
        })
      })

    })
  }
})

查看Network里的请求,可以看到
QQ图片20170414145616.png

证明小程序与websocket服务已经连通了